diff --git a/.changeset/blue-meals-chew.md b/.changeset/blue-meals-chew.md new file mode 100644 index 0000000000..479e77a982 --- /dev/null +++ b/.changeset/blue-meals-chew.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-auth-backend': patch +--- + +`oauth2-proxy` auth implementation has been moved to `@backstage/plugin-auth-backend-module-oauth2-proxy-provider` diff --git a/.changeset/brown-books-carry.md b/.changeset/brown-books-carry.md new file mode 100644 index 0000000000..1299354445 --- /dev/null +++ b/.changeset/brown-books-carry.md @@ -0,0 +1,5 @@ +--- +'@backstage/frontend-app-api': patch +--- + +Create a core components extension that allows adopters to override core app components such as `Progress`, `BootErrorPage`, `NotFoundErrorPage` and `ErrorBoundaryFallback`. diff --git a/.changeset/chilly-bikes-kick.md b/.changeset/chilly-bikes-kick.md new file mode 100644 index 0000000000..f5eb5380d3 --- /dev/null +++ b/.changeset/chilly-bikes-kick.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-components': patch +--- + +Added a new `/testUtils` sub-path that initially exports a `mockBreakpoint` helper. diff --git a/.changeset/chilly-lies-collect.md b/.changeset/chilly-lies-collect.md new file mode 100644 index 0000000000..c84e6cebd4 --- /dev/null +++ b/.changeset/chilly-lies-collect.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-auth-backend-module-oauth2-proxy-provider': patch +--- + +Exported the provider as default so it gets discovered when using `featureDiscoveryServiceFactory()` diff --git a/.changeset/clever-wombats-sneeze.md b/.changeset/clever-wombats-sneeze.md new file mode 100644 index 0000000000..ffd94aa9f0 --- /dev/null +++ b/.changeset/clever-wombats-sneeze.md @@ -0,0 +1,5 @@ +--- +'@backstage/frontend-plugin-api': patch +--- + +Create factories for overriding default core components extensions. diff --git a/.changeset/cool-knives-argue.md b/.changeset/cool-knives-argue.md new file mode 100644 index 0000000000..74890a0a9b --- /dev/null +++ b/.changeset/cool-knives-argue.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-auth-backend-module-oauth2-proxy-provider': minor +--- + +Release of `oauth2-proxy-provider` plugin diff --git a/.changeset/eighty-dodos-sing.md b/.changeset/eighty-dodos-sing.md new file mode 100644 index 0000000000..b7af0da473 --- /dev/null +++ b/.changeset/eighty-dodos-sing.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-node': minor +--- + +Added `EntitiesSearchFilter` and `EntityFilter` from `@backstage/plugin-catalog-backend`, for reuse diff --git a/.changeset/fair-plants-attack.md b/.changeset/fair-plants-attack.md new file mode 100644 index 0000000000..2e36ba6284 --- /dev/null +++ b/.changeset/fair-plants-attack.md @@ -0,0 +1,9 @@ +--- +'@backstage/plugin-kubernetes-react': minor +'@backstage/plugin-kubernetes': patch +--- + +Change `formatClusterLink` to be an API and make it async for further customization possibilities. + +**BREAKING** +If you have a custom k8s page and used `formatClusterLink` directly, you need to migrate to new `kubernetesClusterLinkFormatterApiRef` diff --git a/.changeset/famous-flies-kick.md b/.changeset/famous-flies-kick.md new file mode 100644 index 0000000000..abbd18f9c3 --- /dev/null +++ b/.changeset/famous-flies-kick.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-plugin-api': patch +--- + +The `createTranslationRef` function from the `/alpha` subpath can now also accept a nested object structure of default translation messages, which will be flatted using `.` separators. diff --git a/.changeset/famous-peas-reflect.md b/.changeset/famous-peas-reflect.md new file mode 100644 index 0000000000..c8aed3c16b --- /dev/null +++ b/.changeset/famous-peas-reflect.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +Toned down the warning message when git is not found diff --git a/.changeset/fifty-cameras-share.md b/.changeset/fifty-cameras-share.md new file mode 100644 index 0000000000..8aa103b218 --- /dev/null +++ b/.changeset/fifty-cameras-share.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-catalog-react': patch +'@backstage/plugin-catalog': patch +--- + +Ensure that passed-in icons are taken advantage of in the presentation API diff --git a/.changeset/five-feet-call.md b/.changeset/five-feet-call.md new file mode 100644 index 0000000000..528c21a932 --- /dev/null +++ b/.changeset/five-feet-call.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend': patch +--- + +Update the OpenAPI spec to support the use of `openapi-generator`. diff --git a/.changeset/forty-clocks-hug.md b/.changeset/forty-clocks-hug.md new file mode 100644 index 0000000000..ea0fd2480f --- /dev/null +++ b/.changeset/forty-clocks-hug.md @@ -0,0 +1,5 @@ +--- +'@backstage/catalog-client': minor +--- + +The internals of `CatalogClient` are now auto-generated using the `backstage-repo-tools schema openapi generate-client` command. diff --git a/.changeset/forty-insects-drop.md b/.changeset/forty-insects-drop.md new file mode 100644 index 0000000000..e2a94662ed --- /dev/null +++ b/.changeset/forty-insects-drop.md @@ -0,0 +1,5 @@ +--- +'@backstage/frontend-test-utils': patch +--- + +Updates for compatibility with the new extension IDs. diff --git a/.changeset/funny-bobcats-try.md b/.changeset/funny-bobcats-try.md new file mode 100644 index 0000000000..c8dd2384a6 --- /dev/null +++ b/.changeset/funny-bobcats-try.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-react': patch +--- + +Fixed bug in `ReviewState` where `enum` value was displayed in step review instead of the corresponding label when using `enumNames` diff --git a/.changeset/gold-humans-tease.md b/.changeset/gold-humans-tease.md new file mode 100644 index 0000000000..b3b40d0a7f --- /dev/null +++ b/.changeset/gold-humans-tease.md @@ -0,0 +1,5 @@ +--- +'@backstage/frontend-plugin-api': patch +--- + +Add feature flags to plugins and extension overrides. diff --git a/.changeset/gorgeous-snails-admire.md b/.changeset/gorgeous-snails-admire.md new file mode 100644 index 0000000000..45875ed366 --- /dev/null +++ b/.changeset/gorgeous-snails-admire.md @@ -0,0 +1,5 @@ +--- +'@backstage/repo-tools': minor +--- + +**BREAKING**: API Reports generated for sub-path exports now place the name as a suffix rather than prefix, for example `api-report-alpha.md` instead of `alpha-api-report.md`. When upgrading to this version you'll need to re-create any such API reports and delete the old ones. diff --git a/.changeset/green-seals-play.md b/.changeset/green-seals-play.md new file mode 100644 index 0000000000..5c5a31afcd --- /dev/null +++ b/.changeset/green-seals-play.md @@ -0,0 +1,5 @@ +--- +'@backstage/frontend-app-api': patch +--- + +Collect and register feature flags from plugins and extension overrides. diff --git a/.changeset/healthy-feet-kick.md b/.changeset/healthy-feet-kick.md new file mode 100644 index 0000000000..732f2164c3 --- /dev/null +++ b/.changeset/healthy-feet-kick.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-home': minor +--- + +Added view of entities grouped by kind to make it easier to distinguish entities with different kind but same name diff --git a/.changeset/honest-houses-hide.md b/.changeset/honest-houses-hide.md new file mode 100644 index 0000000000..7f2508184f --- /dev/null +++ b/.changeset/honest-houses-hide.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +Added a warning when starting a standalone backend plugin that hasn't been updated to the new backend system. diff --git a/.changeset/hungry-buckets-compare.md b/.changeset/hungry-buckets-compare.md new file mode 100644 index 0000000000..c70f38bfb7 --- /dev/null +++ b/.changeset/hungry-buckets-compare.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-app-api': patch +--- + +Ensure redaction of secrets that have accidental extra whitespace around them diff --git a/.changeset/itchy-camels-cough.md b/.changeset/itchy-camels-cough.md new file mode 100644 index 0000000000..2ec70c945c --- /dev/null +++ b/.changeset/itchy-camels-cough.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-auth-backend-module-oauth2-proxy-provider': patch +--- + +Change provider id from `oauth2ProxyProvider` to `oauth2Proxy` diff --git a/.changeset/khaki-clocks-happen.md b/.changeset/khaki-clocks-happen.md new file mode 100644 index 0000000000..ac20e42dfa --- /dev/null +++ b/.changeset/khaki-clocks-happen.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-app-api': patch +--- + +Add redacting for secrets in stack traces of logs diff --git a/.changeset/khaki-hounds-think.md b/.changeset/khaki-hounds-think.md new file mode 100644 index 0000000000..9c7145fc89 --- /dev/null +++ b/.changeset/khaki-hounds-think.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-github-actions': patch +--- + +Github Workflow Runs UI is modified to show in optional Card view instead of table, with branch selection option diff --git a/.changeset/late-eyes-serve.md b/.changeset/late-eyes-serve.md new file mode 100644 index 0000000000..37d8e7a22a --- /dev/null +++ b/.changeset/late-eyes-serve.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend': patch +--- + +Wrap single `pipelineLoop` of TaskPipeline in a span for better traces diff --git a/.changeset/little-suits-collect.md b/.changeset/little-suits-collect.md new file mode 100644 index 0000000000..2a1eb4c833 --- /dev/null +++ b/.changeset/little-suits-collect.md @@ -0,0 +1,5 @@ +--- +'@backstage/test-utils': patch +--- + +Deprecated `mockBreakpoint`, as it is now available from `@backstage/core-components/testUtils` instead. diff --git a/.changeset/long-cycles-grow.md b/.changeset/long-cycles-grow.md new file mode 100644 index 0000000000..4edc1905fe --- /dev/null +++ b/.changeset/long-cycles-grow.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-lighthouse': patch +--- + +Updated README diff --git a/.changeset/modern-mails-live.md b/.changeset/modern-mails-live.md new file mode 100644 index 0000000000..f8e12e8815 --- /dev/null +++ b/.changeset/modern-mails-live.md @@ -0,0 +1,5 @@ +--- +'@backstage/frontend-test-utils': patch +--- + +Re-export mock API implementations as well as `TestApiProvider`, `TestApiRegistry`, `withLogCollector`, and `setupRequestMockHandlers` from `@backstage/test-utils`. diff --git a/.changeset/nervous-ladybugs-end.md b/.changeset/nervous-ladybugs-end.md new file mode 100644 index 0000000000..5ee94ec637 --- /dev/null +++ b/.changeset/nervous-ladybugs-end.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-auth-backend': patch +--- + +fix static token issuer not being able to initialize diff --git a/.changeset/ninety-otters-report.md b/.changeset/ninety-otters-report.md new file mode 100644 index 0000000000..fbccc481f4 --- /dev/null +++ b/.changeset/ninety-otters-report.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-lighthouse-backend': minor +--- + +Fixed crashes faced with custom schedule configuration. The configuration schema has been update to leverage the TaskScheduleDefinition interface. It is highly recommended to move the `lighthouse.shedule` and `lighthouse.timeout` respectively to `lighthouse.schedule.frequency` and `lighthouse.schedule.timeout`. diff --git a/.changeset/orange-boats-hunt.md b/.changeset/orange-boats-hunt.md new file mode 100644 index 0000000000..1a3d9b1682 --- /dev/null +++ b/.changeset/orange-boats-hunt.md @@ -0,0 +1,6 @@ +--- +'@backstage/frontend-app-api': patch +'@backstage/core-compat-api': patch +--- + +Updates to match the new extension input wrapping. diff --git a/.changeset/perfect-crabs-help.md b/.changeset/perfect-crabs-help.md new file mode 100644 index 0000000000..831649ffe7 --- /dev/null +++ b/.changeset/perfect-crabs-help.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-react': patch +--- + +Add horizontal slider if stepper overflows diff --git a/.changeset/pre.json b/.changeset/pre.json index 5a151bfc2f..69d9cbf255 100644 --- a/.changeset/pre.json +++ b/.changeset/pre.json @@ -255,61 +255,130 @@ "@backstage/plugin-xcmetrics": "0.2.45", "@backstage/frontend-test-utils": "0.0.0", "@backstage/plugin-auth-backend-module-atlassian-provider": "0.0.0", - "@backstage/plugin-auth-backend-module-okta-provider": "0.0.0" + "@backstage/plugin-auth-backend-module-okta-provider": "0.0.0", + "@backstage/plugin-auth-backend-module-oauth2-proxy-provider": "0.0.0", + "@backstage/plugin-auth-backend-module-vmware-cloud-provider": "0.0.0" }, "changesets": [ "angry-eagles-end", "angry-gorillas-unite", "angry-walls-despacito", "angry-walls-juggle", + "big-swans-guess", + "blue-meals-chew", + "brave-parents-stare", "brave-socks-raise", "breezy-pans-glow", "bright-eyes-film", + "brown-books-carry", "chatty-actors-live", + "chilled-buses-love", "chilled-terms-breathe", + "chilly-bikes-kick", + "clever-wombats-sneeze", "cold-pans-crash", + "cool-knives-argue", "create-app-1700579510", "create-app-1700607979", "curly-walls-relate", "dry-readers-raise", "eleven-ants-pretend", "empty-dingos-grow", + "empty-fireants-study", + "famous-flies-kick", + "famous-peas-reflect", + "fifty-cameras-share", + "fifty-seas-grab", + "five-feet-call", + "forty-clocks-hug", + "four-needles-watch", + "funny-bobcats-try", "gentle-lies-greet", + "giant-files-fry", + "gold-humans-tease", "gold-shirts-yell", + "gorgeous-snails-admire", "great-rings-type", + "green-seals-play", + "healthy-feet-kick", + "healthy-poets-search", + "hip-berries-begin", + "honest-houses-hide", "hot-wolves-flash", + "hungry-buckets-compare", + "khaki-clocks-happen", + "khaki-hounds-think", "kind-badgers-rush", + "kind-cycles-happen", "large-weeks-accept", + "little-suits-collect", "long-wolves-return", "lovely-terms-search", "lucky-kids-cough", "metal-eggs-smile", "mighty-beans-wink", + "modern-mails-live", "nasty-tips-exercise", + "nervous-dancers-wait", + "nervous-penguins-sort", "perfect-apes-sing", + "perfect-crabs-help", "pink-dryers-repair", "plenty-numbers-enjoy", "polite-knives-build", "poor-pandas-dream", + "pretty-onions-knock", + "pretty-ravens-serve", "proud-flies-travel", + "proud-seahorses-explain", "quick-horses-reply", + "real-bees-thank", "renovate-07b1766", "renovate-169bdc2", + "renovate-1ed81a0", "renovate-240982c", "renovate-4891ee7", + "renovate-4b1eded", + "renovate-522261c", + "renovate-6075f87", "renovate-6c0fb81", + "renovate-6e5c790", + "renovate-796bd6f", + "renovate-8d8b90d", "renovate-9d2d52e", + "renovate-a6399d6", "renovate-b193efa", + "renovate-c4e012c", "renovate-c7270d5", + "renovate-dee7ad1", "renovate-ead3ba6", "renovate-f5b18be", + "rich-singers-join", + "rude-beans-drop", + "rude-ears-greet", + "silent-rats-reflect", "silly-numbers-wash", + "silver-rocks-deliver", "sixty-adults-kiss", "sixty-houses-agree", + "slow-trees-warn", + "smooth-frogs-decide", + "sour-pumas-reply", "spotty-olives-share", + "stale-bats-end", + "stale-walls-cross", + "strange-suits-glow", + "tame-pants-end", + "tender-peas-smoke", + "thick-snails-travel", + "thirty-fireants-cheer", "tiny-cobras-look", + "tricky-donkeys-do", "tricky-islands-wonder", "twenty-beans-laugh", - "violet-zebras-thank" + "unlucky-clouds-build", + "violet-zebras-thank", + "warm-plums-beg", + "wicked-elephants-think" ] } diff --git a/.changeset/pretty-onions-knock.md b/.changeset/pretty-onions-knock.md new file mode 100644 index 0000000000..701024cba4 --- /dev/null +++ b/.changeset/pretty-onions-knock.md @@ -0,0 +1,7 @@ +--- +'@backstage/plugin-azure-devops-backend': patch +'@backstage/plugin-azure-devops-common': patch +'@backstage/plugin-azure-devops': patch +--- + +Introduced new `AzureDevOpsAnnotatorProcessor` that adds the needed annotations automatically. Also, moved constants to common package so they can be shared more easily diff --git a/.changeset/pretty-ravens-serve.md b/.changeset/pretty-ravens-serve.md new file mode 100644 index 0000000000..95d1b629fd --- /dev/null +++ b/.changeset/pretty-ravens-serve.md @@ -0,0 +1,5 @@ +--- +'@backstage/frontend-app-api': patch +--- + +Migrate analytics route tracker component. diff --git a/.changeset/proud-seahorses-explain.md b/.changeset/proud-seahorses-explain.md new file mode 100644 index 0000000000..328e4bf838 --- /dev/null +++ b/.changeset/proud-seahorses-explain.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': minor +--- + +Updates the ESLint config to ignore issues created by generated files in `**/src/generated/**`. diff --git a/.changeset/real-bees-thank.md b/.changeset/real-bees-thank.md new file mode 100644 index 0000000000..e89dfc7a68 --- /dev/null +++ b/.changeset/real-bees-thank.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog': patch +--- + +Adding in spec.type chip to search results for clarity diff --git a/.changeset/red-readers-search.md b/.changeset/red-readers-search.md new file mode 100644 index 0000000000..c18d9672ba --- /dev/null +++ b/.changeset/red-readers-search.md @@ -0,0 +1,10 @@ +--- +'@backstage/plugin-catalog-react': patch +'@backstage/plugin-user-settings': patch +'@backstage/plugin-graphiql': patch +'@backstage/plugin-catalog': patch +'@backstage/plugin-search': patch +'@backstage/plugin-home': patch +--- + +Updates to the `/alpha` exports to match the extension input wrapping change. diff --git a/.changeset/renovate-0392ef9.md b/.changeset/renovate-0392ef9.md new file mode 100644 index 0000000000..3ee3fb0361 --- /dev/null +++ b/.changeset/renovate-0392ef9.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +Updated dependency `vite-plugin-node-polyfills` to `^0.17.0`. diff --git a/.changeset/renovate-0c44581.md b/.changeset/renovate-0c44581.md new file mode 100644 index 0000000000..afddf45fed --- /dev/null +++ b/.changeset/renovate-0c44581.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +Updated dependency `@rollup/plugin-node-resolve` to `^15.0.0`. diff --git a/.changeset/renovate-1252136.md b/.changeset/renovate-1252136.md new file mode 100644 index 0000000000..0be5143b30 --- /dev/null +++ b/.changeset/renovate-1252136.md @@ -0,0 +1,5 @@ +--- +'@backstage/repo-tools': patch +--- + +Updated dependency `@stoplight/types` to `^14.0.0`. diff --git a/.changeset/renovate-12c95fb.md b/.changeset/renovate-12c95fb.md new file mode 100644 index 0000000000..814817b8c2 --- /dev/null +++ b/.changeset/renovate-12c95fb.md @@ -0,0 +1,6 @@ +--- +'@backstage/backend-common': patch +--- + +Updated dependency `archiver` to `^6.0.0`. +Updated dependency `@types/archiver` to `^6.0.0`. diff --git a/.changeset/renovate-19742c3.md b/.changeset/renovate-19742c3.md new file mode 100644 index 0000000000..2dfee1ad96 --- /dev/null +++ b/.changeset/renovate-19742c3.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +Updated dependency `@rollup/plugin-json` to `^6.0.0`. diff --git a/.changeset/renovate-1ed81a0.md b/.changeset/renovate-1ed81a0.md new file mode 100644 index 0000000000..ae2e061f24 --- /dev/null +++ b/.changeset/renovate-1ed81a0.md @@ -0,0 +1,11 @@ +--- +'@backstage/plugin-home-react': patch +'@backstage/plugin-home': patch +'@backstage/plugin-scaffolder-react': patch +'@backstage/plugin-scaffolder': patch +--- + +Updated dependency `@rjsf/utils` to `5.14.3`. +Updated dependency `@rjsf/core` to `5.14.3`. +Updated dependency `@rjsf/material-ui` to `5.14.3`. +Updated dependency `@rjsf/validator-ajv8` to `5.14.3`. diff --git a/.changeset/renovate-522261c.md b/.changeset/renovate-522261c.md new file mode 100644 index 0000000000..fb25fe0078 --- /dev/null +++ b/.changeset/renovate-522261c.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend': patch +--- + +Updated dependency `@types/glob` to `^8.0.0`. diff --git a/.changeset/renovate-796bd6f.md b/.changeset/renovate-796bd6f.md new file mode 100644 index 0000000000..561f6b8b03 --- /dev/null +++ b/.changeset/renovate-796bd6f.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +Updated dependency `@typescript-eslint/eslint-plugin` to `6.12.0`. diff --git a/.changeset/renovate-8d8b90d.md b/.changeset/renovate-8d8b90d.md new file mode 100644 index 0000000000..169d480dec --- /dev/null +++ b/.changeset/renovate-8d8b90d.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-components': patch +--- + +Updated dependency `linkifyjs` to `4.1.3`. diff --git a/.changeset/renovate-a277605.md b/.changeset/renovate-a277605.md new file mode 100644 index 0000000000..2bb2274e49 --- /dev/null +++ b/.changeset/renovate-a277605.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +Updated dependency `@rollup/plugin-commonjs` to `^25.0.0`. diff --git a/.changeset/renovate-c4e012c.md b/.changeset/renovate-c4e012c.md new file mode 100644 index 0000000000..eb26b9fb58 --- /dev/null +++ b/.changeset/renovate-c4e012c.md @@ -0,0 +1,10 @@ +--- +'@backstage/core-components': patch +'@backstage/plugin-entity-validation': patch +'@backstage/plugin-gcp-projects': patch +'@backstage/plugin-scaffolder-react': patch +'@backstage/plugin-scaffolder': patch +'@backstage/plugin-techdocs-module-addons-contrib': patch +--- + +Updated dependency `@react-hookz/web` to `^23.0.0`. diff --git a/.changeset/renovate-c727667.md b/.changeset/renovate-c727667.md new file mode 100644 index 0000000000..82b7d0a8f5 --- /dev/null +++ b/.changeset/renovate-c727667.md @@ -0,0 +1,12 @@ +--- +'@backstage/plugin-auth-backend-module-atlassian-provider': patch +'@backstage/plugin-auth-backend-module-gitlab-provider': patch +'@backstage/plugin-auth-backend-module-microsoft-provider': patch +'@backstage/plugin-auth-backend-module-oauth2-provider': patch +'@backstage/plugin-auth-backend-module-okta-provider': patch +'@backstage/plugin-auth-backend-module-pinniped-provider': patch +'@backstage/plugin-auth-backend': patch +'@backstage/plugin-auth-node': patch +--- + +Updated dependency `passport` to `^0.7.0`. diff --git a/.changeset/rich-bees-breathe.md b/.changeset/rich-bees-breathe.md new file mode 100644 index 0000000000..7a40e3df2f --- /dev/null +++ b/.changeset/rich-bees-breathe.md @@ -0,0 +1,17 @@ +--- +'@backstage/plugin-catalog-import': patch +'@backstage/plugin-stack-overflow': patch +'@backstage/plugin-catalog-react': patch +'@backstage/plugin-user-settings': patch +'@backstage/plugin-search-react': patch +'@backstage/plugin-tech-radar': patch +'@backstage/plugin-graphiql': patch +'@backstage/plugin-techdocs': patch +'@backstage/plugin-catalog': patch +'@backstage/plugin-explore': patch +'@backstage/plugin-search': patch +'@backstage/plugin-home': patch +'@backstage/plugin-adr': patch +--- + +Refactor of the alpha exports due to API change in how extension IDs are constructed. diff --git a/.changeset/rich-singers-join.md b/.changeset/rich-singers-join.md new file mode 100644 index 0000000000..1a21fa9fc8 --- /dev/null +++ b/.changeset/rich-singers-join.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend-module-incremental-ingestion': patch +--- + +Ensure that cursors always come back as JSON on sqlite too diff --git a/.changeset/rude-ears-greet.md b/.changeset/rude-ears-greet.md new file mode 100644 index 0000000000..b5dd684c16 --- /dev/null +++ b/.changeset/rude-ears-greet.md @@ -0,0 +1,6 @@ +--- +'@backstage/create-app': patch +'@backstage/plugin-circleci': patch +--- + +CircelCI plugin moved permanently diff --git a/.changeset/silent-rats-reflect.md b/.changeset/silent-rats-reflect.md new file mode 100644 index 0000000000..b7884f06d0 --- /dev/null +++ b/.changeset/silent-rats-reflect.md @@ -0,0 +1,5 @@ +--- +'@backstage/repo-tools': minor +--- + +Adds a new command `schema openapi generate-client` that creates a Typescript client with Backstage flavor, including the discovery API and fetch API. This command doesn't currently generate a complete client and needs to be wrapped or exported manually by a separate Backstage plugin. See `@backstage/catalog-client/src/generated` for example output. diff --git a/.changeset/silver-rocks-deliver.md b/.changeset/silver-rocks-deliver.md new file mode 100644 index 0000000000..eb132199f3 --- /dev/null +++ b/.changeset/silver-rocks-deliver.md @@ -0,0 +1,7 @@ +--- +'@backstage/plugin-user-settings': patch +'@backstage/plugin-catalog': patch +'@backstage/plugin-adr': patch +--- + +Updated alpha translation message keys to use nested format and camel case. diff --git a/.changeset/smooth-frogs-decide.md b/.changeset/smooth-frogs-decide.md new file mode 100644 index 0000000000..942a3a80bf --- /dev/null +++ b/.changeset/smooth-frogs-decide.md @@ -0,0 +1,5 @@ +--- +'@backstage/frontend-plugin-api': patch +--- + +Migrate analytics api and context files. diff --git a/.changeset/sour-pumas-reply.md b/.changeset/sour-pumas-reply.md new file mode 100644 index 0000000000..99b812acdd --- /dev/null +++ b/.changeset/sour-pumas-reply.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder': patch +--- + +Fixing `headerOptions` not being passed through the `TemplatePage` component diff --git a/.changeset/stale-bats-end.md b/.changeset/stale-bats-end.md new file mode 100644 index 0000000000..e6715deba9 --- /dev/null +++ b/.changeset/stale-bats-end.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-techdocs-node': patch +--- + +Bumped the default TechDocs docker image version to the latest which was released several month ago diff --git a/.changeset/stale-frogs-agree.md b/.changeset/stale-frogs-agree.md new file mode 100644 index 0000000000..84052bded8 --- /dev/null +++ b/.changeset/stale-frogs-agree.md @@ -0,0 +1,5 @@ +--- +'@backstage/frontend-app-api': patch +--- + +Updates to match the introduction of `ExtensionDefinition` and new extension ID naming patterns. diff --git a/.changeset/strange-suits-glow.md b/.changeset/strange-suits-glow.md new file mode 100644 index 0000000000..6f1ba31127 --- /dev/null +++ b/.changeset/strange-suits-glow.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +Switched the `@typescript-eslint/eslint-plugin` dependency back to using a `^` version range. diff --git a/.changeset/stupid-wasps-fold.md b/.changeset/stupid-wasps-fold.md new file mode 100644 index 0000000000..260dd9f5f1 --- /dev/null +++ b/.changeset/stupid-wasps-fold.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-compat-api': patch +--- + +Updates for compatibility with the new extension IDs. diff --git a/.changeset/tame-pants-end.md b/.changeset/tame-pants-end.md new file mode 100644 index 0000000000..b80fcb7fe3 --- /dev/null +++ b/.changeset/tame-pants-end.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-pagerduty': minor +--- + +This package has been deprecated, consider using [@pagerduty/backstage-plugin](https://github.com/pagerduty/backstage-plugin) instead. diff --git a/.changeset/tasty-dolphins-unite.md b/.changeset/tasty-dolphins-unite.md new file mode 100644 index 0000000000..b367b0c554 --- /dev/null +++ b/.changeset/tasty-dolphins-unite.md @@ -0,0 +1,5 @@ +--- +'@backstage/frontend-plugin-api': minor +--- + +Extension inputs are now wrapped into an additional object when passed to the extension factory, with the previous values being available at the `output` property. The `ExtensionInputValues` type has also been replaced by `ResolvedExtensionInputs`. diff --git a/.changeset/ten-snakes-wait.md b/.changeset/ten-snakes-wait.md new file mode 100644 index 0000000000..1c15cb8d33 --- /dev/null +++ b/.changeset/ten-snakes-wait.md @@ -0,0 +1,7 @@ +--- +'@backstage/frontend-plugin-api': minor +--- + +**BREAKING**: This version changes how extensions are created and how their IDs are determined. The `createExtension` function now accepts `kind`, `namespace` and `name` instead of `id`. All of the new options are optional, and are used to construct the final extension ID. By convention extension creators should set the `kind` to match their own name, for example `createNavItemExtension` sets the kind `nav-item`. + +The `createExtension` function as well as all extension creators now also return an `ExtensionDefinition` rather than an `Extension`, which in turn needs to be passed to `createPlugin` or `createExtensionOverrides` to be used. diff --git a/.changeset/tender-peas-smoke.md b/.changeset/tender-peas-smoke.md new file mode 100644 index 0000000000..5c84e1a131 --- /dev/null +++ b/.changeset/tender-peas-smoke.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend-module-sentry': patch +--- + +Added examples for `sentry:project:create` scaffolder action and unit tests. diff --git a/.changeset/thick-snails-travel.md b/.changeset/thick-snails-travel.md new file mode 100644 index 0000000000..bfd14d55e1 --- /dev/null +++ b/.changeset/thick-snails-travel.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog': minor +--- + +Properly support both function- and string-form visibility filter expressions in the new extensions exported via `/alpha`. diff --git a/.changeset/thirty-fireants-cheer.md b/.changeset/thirty-fireants-cheer.md new file mode 100644 index 0000000000..abda872570 --- /dev/null +++ b/.changeset/thirty-fireants-cheer.md @@ -0,0 +1,7 @@ +--- +'@backstage/plugin-catalog-react': patch +--- + +Breaking alpha-API change to entity visibility filter functions to accept a bare entity as their first argument, instead of an object with an entity property. + +Functions that accept such filters now also support the string expression form of filters. diff --git a/.changeset/tricky-donkeys-do.md b/.changeset/tricky-donkeys-do.md new file mode 100644 index 0000000000..1a810e9f3b --- /dev/null +++ b/.changeset/tricky-donkeys-do.md @@ -0,0 +1,5 @@ +--- +'@techdocs/cli': minor +--- + +Support passing additional `mkdocs-server` CLI parameters (`--dirtyreload`, `--strict` and `--clean`) when run in containerized mode. diff --git a/.changeset/unlucky-clouds-build.md b/.changeset/unlucky-clouds-build.md new file mode 100644 index 0000000000..35aa277653 --- /dev/null +++ b/.changeset/unlucky-clouds-build.md @@ -0,0 +1,7 @@ +--- +'@backstage/plugin-azure-devops-backend': minor +--- + +**BREAKING** New `fromConfig` static method must be used now when creating an instance of the `AzureDevOpsApi` + +Added support for using the `AzureDevOpsCredentialsProvider` diff --git a/.changeset/wet-bananas-agree.md b/.changeset/wet-bananas-agree.md new file mode 100644 index 0000000000..4bf47ca664 --- /dev/null +++ b/.changeset/wet-bananas-agree.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-react': patch +--- + +Fixed issue for showing undefined for hidden form items diff --git a/.changeset/wicked-pants-end.md b/.changeset/wicked-pants-end.md new file mode 100644 index 0000000000..ecb42d0957 --- /dev/null +++ b/.changeset/wicked-pants-end.md @@ -0,0 +1,35 @@ +--- +'@backstage/plugin-catalog-backend-module-scaffolder-entity-model': patch +'@backstage/plugin-search-backend-module-stack-overflow-collator': patch +'@backstage/plugin-catalog-backend-module-incremental-ingestion': patch +'@backstage/plugin-permission-backend-module-allow-all-policy': patch +'@backstage/plugin-auth-backend-module-oauth2-proxy-provider': patch +'@backstage/plugin-catalog-backend-module-bitbucket-server': patch +'@backstage/plugin-catalog-backend-module-bitbucket-cloud': patch +'@backstage/plugin-events-backend-module-bitbucket-cloud': patch +'@backstage/plugin-auth-backend-module-gcp-iap-provider': patch +'@backstage/plugin-auth-backend-module-google-provider': patch +'@backstage/plugin-search-backend-module-elasticsearch': patch +'@backstage/plugin-catalog-backend-module-unprocessed': patch +'@backstage/plugin-catalog-backend-module-github-org': patch +'@backstage/plugin-catalog-backend-module-puppetdb': patch +'@backstage/plugin-search-backend-module-techdocs': patch +'@backstage/plugin-catalog-backend-module-gerrit': patch +'@backstage/plugin-catalog-backend-module-github': patch +'@backstage/plugin-catalog-backend-module-gitlab': patch +'@backstage/plugin-events-backend-module-aws-sqs': patch +'@backstage/plugin-search-backend-module-catalog': patch +'@backstage/plugin-search-backend-module-explore': patch +'@backstage/plugin-catalog-backend-module-azure': patch +'@backstage/plugin-events-backend-module-gerrit': patch +'@backstage/plugin-events-backend-module-github': patch +'@backstage/plugin-events-backend-module-gitlab': patch +'@backstage/plugin-events-backend-module-azure': patch +'@backstage/plugin-catalog-backend-module-aws': patch +'@backstage/plugin-catalog-backend-module-gcp': patch +'@backstage/plugin-search-backend-module-pg': patch +'@backstage/backend-test-utils': patch +'@backstage/plugin-events-backend': patch +--- + +Switched module ID to use kebab-case. diff --git a/.changeset/wild-knives-wait.md b/.changeset/wild-knives-wait.md new file mode 100644 index 0000000000..6aafbe7acd --- /dev/null +++ b/.changeset/wild-knives-wait.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-catalog-backend': minor +'@backstage/plugin-catalog-node': minor +--- + +Permission rules can now be added for the Catalog plugin through the `CatalogPermissionExtensionPoint` interface. diff --git a/.changeset/witty-cars-wash.md b/.changeset/witty-cars-wash.md new file mode 100644 index 0000000000..301ed5e35a --- /dev/null +++ b/.changeset/witty-cars-wash.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend': patch +--- + +Deprecated `EntitiesSearchFilter` and `EntityFilter`, which can now be imported from `@backstage/plugin-catalog-node` instead diff --git a/.github/workflows/deploy_docker-image.yml b/.github/workflows/deploy_docker-image.yml index 7fd15426e5..9ceed78685 100644 --- a/.github/workflows/deploy_docker-image.yml +++ b/.github/workflows/deploy_docker-image.yml @@ -61,7 +61,7 @@ jobs: with: context: './example-app' file: ./example-app/packages/backend/Dockerfile - push: ${{ github.event_name == "repository_dispatch" && github.event.action == "release-published" }} + push: ${{ (github.event_name == "repository_dispatch") && (github.event.action == "release-published") }} platforms: linux/amd64,linux/arm64 tags: | ghcr.io/${{ github.repository_owner }}/backstage:latest diff --git a/.github/workflows/deploy_microsite.yml b/.github/workflows/deploy_microsite.yml index d8e5789a09..21a15ae475 100644 --- a/.github/workflows/deploy_microsite.yml +++ b/.github/workflows/deploy_microsite.yml @@ -66,7 +66,7 @@ jobs: run: ls microsite/build && ls microsite/build/storybook - name: Deploy both microsite and storybook to gh-pages - uses: JamesIves/github-pages-deploy-action@a1ea191d508feb8485aceba848389d49f80ca2dc # v4.4.3 + uses: JamesIves/github-pages-deploy-action@65b5dfd4f5bcd3a7403bbc2959c144256167464e # v4.5.0 with: branch: gh-pages folder: microsite/build diff --git a/.github/workflows/uffizzi-build.yml b/.github/workflows/uffizzi-build.yml index 6bc36c796a..4eaf2c2dc0 100644 --- a/.github/workflows/uffizzi-build.yml +++ b/.github/workflows/uffizzi-build.yml @@ -60,7 +60,7 @@ jobs: - name: Docker metadata id: meta - uses: docker/metadata-action@96383f45573cb7f253c731d3b3ab81c87ef81934 # v5.0.0 + uses: docker/metadata-action@e6428a5c4e294a61438ed7f43155db912025b6b3 # v5.2.0 with: images: registry.uffizzi.com/${{ env.UUID_TAG_APP }} tags: type=raw,value=60d diff --git a/.prettierignore b/.prettierignore index ad2bd355ed..c54ee5d821 100644 --- a/.prettierignore +++ b/.prettierignore @@ -5,7 +5,7 @@ coverage *.hbs templates api-report.md -*-api-report.md +api-report-*.md cli-report.md plugins/scaffolder-backend/sample-templates .vscode diff --git a/REVIEWING.md b/REVIEWING.md index 7ed3fcda57..c443c15d77 100644 --- a/REVIEWING.md +++ b/REVIEWING.md @@ -349,3 +349,13 @@ type LabelStyle = 'normal' | 'thin'; // Output, since it's an exported constant const LABEL_SIZE: number; ``` + +## Plugin Directory Submissions + +When reviewing Plugin Directory submissions please consider the following: + +- Check to make sure they have the rights for any icon being used. This is mostly for clearly copyrighted logos, for example the Microsoft Azure DevOps logo +- Make sure the package has been published on the NPM registry. +- Make sure the package on NPM has a link back to the code repo, this helps provide confidence that it's the right package. +- If they use an [NPM scope](https://docs.npmjs.com/about-scopes) make sure it that matches either the Organization name or user name, this provides trust in the plugin +- If the plugin has both a frontend and backend make sure that the documentation notes that. diff --git a/contrib/chart/backstage/Chart.lock b/contrib/chart/backstage/Chart.lock index b6068caacf..c835a4dcd1 100644 --- a/contrib/chart/backstage/Chart.lock +++ b/contrib/chart/backstage/Chart.lock @@ -1,6 +1,6 @@ dependencies: - name: postgresql repository: https://charts.bitnami.com/bitnami - version: 13.2.12 -digest: sha256:3add6e2f2c055e415403ebbba7740113ee052b87ad72e59b35b301df2098d41e -generated: "2023-11-20T12:09:32.786270903Z" + version: 13.2.21 +digest: sha256:8f8b0fe6c2e8f640237f61dcfab5821865d67753af06a782db7d61372cbbb9b9 +generated: "2023-11-27T22:30:42.837126565Z" diff --git a/contrib/chart/backstage/Chart.yaml b/contrib/chart/backstage/Chart.yaml index 80ff44462c..efd84f1d38 100644 --- a/contrib/chart/backstage/Chart.yaml +++ b/contrib/chart/backstage/Chart.yaml @@ -18,7 +18,7 @@ sources: dependencies: - name: postgresql condition: postgresql.enabled - version: 13.2.12 + version: 13.2.21 repository: https://charts.bitnami.com/bitnami maintainers: diff --git a/docs/auth/identity-resolver.md b/docs/auth/identity-resolver.md index a65f3d7899..0a626ef175 100644 --- a/docs/auth/identity-resolver.md +++ b/docs/auth/identity-resolver.md @@ -236,6 +236,12 @@ export default async function createPlugin( // an entity you will need to replace this step as well. // // You might also replace it if you for example want to filter out certain groups. + // + // Note that `getDefaultOwnershipEntityRefs` only includes groups to which the + // user has a direct MEMBER_OF relationship. It's perfectly fine to include + // groups that the user is transitively part of in the claims array, but the + // catalog doesn't currently provide a direct way of accessing this list of + // groups. const ownershipRefs = getDefaultOwnershipEntityRefs(entity); // The last step is to issue the token, where we might provide more options in the future. diff --git a/docs/backend-system/architecture/06-modules.md b/docs/backend-system/architecture/06-modules.md index e916e4fdf6..b15c873c39 100644 --- a/docs/backend-system/architecture/06-modules.md +++ b/docs/backend-system/architecture/06-modules.md @@ -25,8 +25,8 @@ import { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node' import { MyCustomProcessor } from './MyCustomProcessor'; export const catalogModuleExampleCustomProcessor = createBackendModule({ - moduleId: 'exampleCustomProcessor', pluginId: 'catalog', + moduleId: 'example-custom-processor', register(env) { env.registerInit({ deps: { diff --git a/docs/backend-system/architecture/07-naming-patterns.md b/docs/backend-system/architecture/07-naming-patterns.md index 3e57fa6863..9af1e09a27 100644 --- a/docs/backend-system/architecture/07-naming-patterns.md +++ b/docs/backend-system/architecture/07-naming-patterns.md @@ -10,18 +10,20 @@ description: Naming patterns in the backend system These are the naming patterns to adhere to within the backend system. They help us keep exports consistent across packages and make it easier to understand the usage and intent of exports. +As a rule, all names should be camel case, with the exceptions of plugin and module IDs, which should be kebab case. + ### Plugins -| Description | Pattern | Examples | -| ----------- | ------------ | ----------------------------------- | -| export | `Plugin` | `catalogPlugin`, `scaffolderPlugin` | -| ID | `''` | `'catalog'`, `'scaffolder'` | +| Description | Pattern | Examples | +| ----------- | ----------------- | ------------------------------------- | +| export | `Plugin` | `catalogPlugin`, `userSettingsPlugin` | +| ID | `''` | `'catalog'`, `'user-settings'` | Example: ```ts -export const catalogPlugin = createBackendPlugin({ - pluginId: 'catalog', +export const userSettingsPlugin = createBackendPlugin({ + pluginId: 'user-settings', ... }) ``` @@ -31,14 +33,14 @@ export const catalogPlugin = createBackendPlugin({ | Description | Pattern | Examples | | ----------- | ---------------------------- | ----------------------------------- | | export | `Module` | `catalogModuleGithubEntityProvider` | -| ID | `''` | `'githubEntityProvider'` | +| ID | `''` | `'github-entity-provider'` | Example: ```ts export const catalogModuleGithubEntityProvider = createBackendModule({ pluginId: 'catalog', - moduleId: 'githubEntityProvider', + moduleId: 'github-entity-provider', ... }) ``` diff --git a/docs/backend-system/building-backends/08-migrating.md b/docs/backend-system/building-backends/08-migrating.md index 98dde46d38..a105f806b0 100644 --- a/docs/backend-system/building-backends/08-migrating.md +++ b/docs/backend-system/building-backends/08-migrating.md @@ -297,7 +297,7 @@ import { microsoftGraphOrgEntityProviderTransformExtensionPoint } from '@backsta backend.add( createBackendModule({ pluginId: 'catalog', - moduleId: 'microsoftGraphExtensions', + moduleId: 'microsoft-graph-extensions', register(env) { env.registerInit({ deps: { @@ -337,7 +337,7 @@ import { createBackendModule } from '@backstage/backend-plugin-api'; /* highlight-add-start */ const catalogModuleCustomExtensions = createBackendModule({ pluginId: 'catalog', // name of the plugin that the module is targeting - moduleId: 'customExtensions', + moduleId: 'custom-extensions', register(env) { env.registerInit({ deps: { @@ -406,7 +406,7 @@ import { createBackendModule } from '@backstage/backend-plugin-api'; /* highlight-add-start */ const eventsModuleCustomExtensions = createBackendModule({ pluginId: 'events', // name of the plugin that the module is targeting - moduleId: 'customExtensions', + moduleId: 'custom-extensions', register(env) { env.registerInit({ deps: { @@ -471,7 +471,7 @@ import { createBackendModule } from '@backstage/backend-plugin-api'; /* highlight-add-start */ const scaffolderModuleCustomExtensions = createBackendModule({ pluginId: 'scaffolder', // name of the plugin that the module is targeting - moduleId: 'customExtensions', + moduleId: 'custom-extensions', register(env) { env.registerInit({ deps: { diff --git a/docs/backend-system/building-plugins-and-modules/01-index.md b/docs/backend-system/building-plugins-and-modules/01-index.md index 7d97694aec..0f12e81a8c 100644 --- a/docs/backend-system/building-plugins-and-modules/01-index.md +++ b/docs/backend-system/building-plugins-and-modules/01-index.md @@ -104,8 +104,8 @@ import { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node' import { MyCustomProcessor } from './MyCustomProcessor'; export const catalogModuleExampleCustomProcessor = createBackendModule({ - moduleId: 'exampleCustomProcessor', pluginId: 'catalog', + moduleId: 'example-custom-processor', register(env) { env.registerInit({ deps: { diff --git a/docs/backend-system/building-plugins-and-modules/08-migrating.md b/docs/backend-system/building-plugins-and-modules/08-migrating.md index 269a09b55c..cc920c576e 100644 --- a/docs/backend-system/building-plugins-and-modules/08-migrating.md +++ b/docs/backend-system/building-plugins-and-modules/08-migrating.md @@ -196,7 +196,7 @@ import { GoogleContainerEngineSupplier } from './GoogleContainerEngineSupplier'; export default createBackendModule({ pluginId: 'kubernetes', - moduleId: 'gke.supplier', + moduleId: 'gke-supplier', register(env) { env.registerInit({ deps: { diff --git a/docs/features/software-catalog/api.md b/docs/features/software-catalog/api.md index 3ccd89b286..97d43209d6 100644 --- a/docs/features/software-catalog/api.md +++ b/docs/features/software-catalog/api.md @@ -31,8 +31,8 @@ with a `Bearer` token, which should then be the Backstage token returned by the These are the endpoints that deal with reading of entities directly. What it exposes are final entities - i.e. the output of all processing and the stitching -process, not the raw originally ingested entity data. See [The Life of an -Entity](life-of-an-entity.md) for more details about this process and +process, not the raw originally ingested entity data. See +[The Life of an Entity](./life-of-an-entity.md) for more details about this process and distinction. ### `GET /entities` diff --git a/docs/features/techdocs/cli.md b/docs/features/techdocs/cli.md index cc356f68d1..c824eb7d7d 100644 --- a/docs/features/techdocs/cli.md +++ b/docs/features/techdocs/cli.md @@ -91,6 +91,9 @@ Options: --docker-option Extra options to pass to the docker run command, e.g. "--add-host=internal.host:192.168.11.12" (can be added multiple times). --no-docker Do not use Docker, use MkDocs executable in current user environment. + --mkdocs-parameter-clean Pass "--clean" parameter to mkdocs server running in containerized environment. + --mkdocs-parameter-dirtyreload Pass "--dirtyreload" parameter to mkdocs server running in containerized environment. + --mkdocs-parameter-strict Pass "--strict" parameter to mkdocs server running in containerized environment. --mkdocs-port Port for MkDocs server to use (default: "8000") --preview-app-bundle-path Preview documentation using a web app other than the included one. --preview-app-port Port where the preview will be served. diff --git a/docs/getting-started/app-custom-theme.md b/docs/getting-started/app-custom-theme.md index 3b042d5eb4..d6a13ca6e3 100644 --- a/docs/getting-started/app-custom-theme.md +++ b/docs/getting-started/app-custom-theme.md @@ -484,3 +484,7 @@ You can see more ways to use this in the [Storybook Sidebar examples](https://ba In addition to a custom theme, a custom logo, you can also customize the homepage of your app. Read the full guide on the [next page](homepage.md). + +## Migrating to Material UI v5 + +We now support Material UI v5 in Backstage. Check out our [migration guide](../tutorials/migrate-to-mui5.md) to get started. diff --git a/docs/getting-started/configuration.md b/docs/getting-started/configuration.md index 17f07811c0..b7bf7d448d 100644 --- a/docs/getting-started/configuration.md +++ b/docs/getting-started/configuration.md @@ -290,6 +290,11 @@ otherwise something went terribly wrong. ## Create a new component using a software template +> Note: if you're running Backstage with Node 20 or later, you'll need to pass the flag `--no-node-snapshot` to Node in order to +> use the templates feature. +> One way to do this is to specify the `NODE_OPTIONS` environment variable before starting Backstage: +> `export NODE_OPTIONS=--no-node-snapshot` + - Go to `create` and choose to create a website with the `Example Node.js Template` - Type in a name, let's use `tutorial` and click `Next Step` diff --git a/docs/getting-started/configure-app-with-plugins.md b/docs/getting-started/configure-app-with-plugins.md index b44f7877a7..5611117636 100644 --- a/docs/getting-started/configure-app-with-plugins.md +++ b/docs/getting-started/configure-app-with-plugins.md @@ -15,7 +15,7 @@ The following steps assume that you have to it. We are using the -[CircleCI](https://github.com/backstage/backstage/blob/master/plugins/circleci/README.md) +[CircleCI](https://github.com/CircleCI-Public/backstage-plugin/tree/main/plugins/circleci) plugin in this example, which is designed to show CI/CD pipeline information attached to an entity in the software catalog. @@ -23,7 +23,7 @@ to an entity in the software catalog. ```bash # From your Backstage root directory - yarn add --cwd packages/app @backstage/plugin-circleci + yarn add --cwd packages/app @circleci/backstage-plugin ``` Note the plugin is added to the `app` package, rather than the root @@ -38,7 +38,7 @@ to an entity in the software catalog. import { EntityCircleCIContent, isCircleCIAvailable, - } from '@backstage/plugin-circleci'; + } from '@circleci/backstage-plugin'; /* highlight-add-end */ const cicdContent = ( diff --git a/docs/local-dev/debugging.md b/docs/local-dev/debugging.md index 68e37374f6..17a98b1c1c 100644 --- a/docs/local-dev/debugging.md +++ b/docs/local-dev/debugging.md @@ -57,6 +57,7 @@ In your `launch.json`, add a new entry with the following, ```jsonc { "name": "Start Backend", + "type": "node", "request": "launch", "args": [ "package", @@ -67,8 +68,6 @@ In your `launch.json`, add a new entry with the following, "skipFiles": [ "/**" ], - "type": "node" + "console": "integratedTerminal" }, ``` - -You may notice that the normal logs mentioned above do not get logged, this is an issue with the logging library we're using, `winston`, and is not easily solved. See [this thread](https://github.com/winstonjs/winston/issues/1544) for more information. diff --git a/docs/openapi/generate-client.md b/docs/openapi/generate-client.md new file mode 100644 index 0000000000..10e6ec3b2f --- /dev/null +++ b/docs/openapi/generate-client.md @@ -0,0 +1,25 @@ +## How to generate a client with `repo-tools schema openapi generate-client`? + +### Prerequisites + +1. Add your plugin ID as the last `servers` item, like this, + +```yaml +servers: + # first value, used for OpenAPI router validation. + - url: / + + # final value, pluginId. + - url: catalog +``` + +2. Find or create a new plugin to house your new generated client. Currently, we do not support generating an entirely new plugin and instead just generate client files. + +### Generating your client + +1. Run `yarn backstage-repo-tools schema openapi generate-client --input-spec --output-directory `. This will create a new folder in `/src/generated` to house the generated content. +2. You should use the generated files as follows, + +- `apis/DefaultApi.client.ts` - this is the client that you should use. It has types for all of the various operations on your API. +- `models/*` - These are the types generated from your OpenAPI file, ideally you should not need to use these directly and can instead use the inferred types from `apis/DefaultApi.client.ts`. +- everything else is directory specific and shouldn't be touched. diff --git a/docs/plugins/add-to-directory.md b/docs/plugins/add-to-directory.md index dfa43c2ed7..8be0d764dd 100644 --- a/docs/plugins/add-to-directory.md +++ b/docs/plugins/add-to-directory.md @@ -21,7 +21,18 @@ description: A brief description of the plugin. # Max 170 characters documentation: # A link to your documentation E.g. Your github README iconUrl: # Used as the src attribute for your logo. # You can provide an external url or add your logo under static/img and provide a path -# relative to static/ e.g. img/my-logo.png +# relative to static/ e.g. /img/my-logo.png npmPackageName: # Your npm package name E.g. '@backstage/plugin-' quotes are required addedDate: # The date plugin added to directory E.g. '2022-10-01' quotes are required ``` + +## Submission Tips + +Here are a few tips to help speed up the review process when you submit your plugin: + +- For any icon that you use make sure you have the proper rights to use it. +- Make sure that your package had been published on the NPM registry and that it's public. +- Make sure your package on NPM has a link back to your code repo, this helps provide confidence that it's the right package. +- Where possible, please use an [NPM scope](https://docs.npmjs.com/about-scopes) that matches either your Organization name or user name, this provides trust in the plugin +- If your plugin has both a frontend and backend link the documentation to the frontend package but make sure it mentioned needing to install the backend package. +- Where possible include a screenshot of the features in you plugin documentation, it really does help when deciding to use a plugin. diff --git a/docs/plugins/internationalization.md b/docs/plugins/internationalization.md index 78b7de6342..13bb098262 100644 --- a/docs/plugins/internationalization.md +++ b/docs/plugins/internationalization.md @@ -10,7 +10,7 @@ The Backstage core function provides internationalization for plugins ## For a plugin developer -When you are creating your plugin, you have the possibility to use `createTranslationRef` to define all messages for your plugin. For example +When you are creating your plugin, you have the possibility to use `createTranslationRef` to define all messages for your plugin. For example: ```ts import { createTranslationRef } from '@backstage/core-plugin-api/alpha'; @@ -19,8 +19,13 @@ import { createTranslationRef } from '@backstage/core-plugin-api/alpha'; export const myPluginTranslationRef = createTranslationRef({ id: 'plugin.my-plugin', messages: { - index_page_title: 'All your components', - create_component_button_label: 'Create new component', + indexPage: { + title: 'All your components', + createButtonTitle: 'Create new component', + }, + entityPage: { + notFound: 'Entity not found', + }, }, }); ``` @@ -33,9 +38,9 @@ import { useTranslationRef } from '@backstage/core-plugin-api/alpha'; const { t } = useTranslationRef(myPluginTranslationRef); return ( - + ); @@ -53,7 +58,7 @@ In an app you can both override the default messages, as well as register transl + createTranslationMessages({ + ref: myPluginTranslationRef, + messages: { -+ create_component_button_label: 'Create new entity', ++ 'indexPage.createButtonTitle': 'Create new entity', + }, + }), + createTranslationResource({ diff --git a/docs/plugins/new-backend-system.md b/docs/plugins/new-backend-system.md index be27e9427d..b2c73af33b 100644 --- a/docs/plugins/new-backend-system.md +++ b/docs/plugins/new-backend-system.md @@ -147,8 +147,8 @@ import { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node' import { MyCustomProcessor } from './processor'; export const exampleCustomProcessorCatalogModule = createBackendModule({ - moduleId: 'exampleCustomProcessor', pluginId: 'catalog', + moduleId: 'example-custom-processor', register(env) { env.registerInit({ deps: { diff --git a/docs/releases/v1.21.0-next.2-changelog.md b/docs/releases/v1.21.0-next.2-changelog.md new file mode 100644 index 0000000000..ba5c3c007d --- /dev/null +++ b/docs/releases/v1.21.0-next.2-changelog.md @@ -0,0 +1,3088 @@ +# Release v1.21.0-next.2 + +## @backstage/catalog-client@1.5.0-next.0 + +### Minor Changes + +- 38340678c3: The internals of `CatalogClient` are now auto-generated using the `backstage-repo-tools schema openapi generate-client` command. + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + +## @backstage/cli@0.25.0-next.1 + +### Minor Changes + +- 38340678c3: Updates the ESLint config to ignore issues created by generated files in `**/src/generated/**`. + +### Patch Changes + +- 0ffee55010: Toned down the warning message when git is not found +- c6f3743172: Added a warning when starting a standalone backend plugin that hasn't been updated to the new backend system. +- 3e358b0dff: Added deprecation warning for React Router v6 beta, please make sure you have migrated your apps to use React Router v6 stable as support for the beta version will be removed. See the [migration tutorial](https://backstage.io/docs/tutorials/react-router-stable-migration) for more information. +- 8056425e09: Updated dependency `@typescript-eslint/eslint-plugin` to `6.12.0`. +- 33e96e59e7: Switched the `@typescript-eslint/eslint-plugin` dependency back to using a `^` version range. +- Updated dependencies + - @backstage/eslint-plugin@0.1.4-next.0 + - @backstage/integration@1.8.0-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/cli-common@0.1.13 + - @backstage/cli-node@0.2.0 + - @backstage/config@1.1.1 + - @backstage/config-loader@1.5.3 + - @backstage/errors@1.2.3 + - @backstage/release-manifests@0.0.11 + - @backstage/types@1.1.1 + +## @backstage/frontend-app-api@0.4.0-next.1 + +### Minor Changes + +- e539735435: Updated core extension structure to make space for the sign-in page by adding `core.router`. + +### Patch Changes + +- 5eb6b8a7bc: Added the nav logo extension for customization of sidebar logo +- 1f12fb762c: Create a core components extension that allows adopters to override core app components such as `Progress`, `BootErrorPage`, `NotFoundErrorPage` and `ErrorBoundaryFallback`. +- 59709286b3: Collect and register feature flags from plugins and extension overrides. +- f27ee7d937: Migrate analytics route tracker component. +- a5a04739e1: Updates to provide `node` to extension factories instead of `id` and `source`. +- Updated dependencies + - @backstage/frontend-plugin-api@0.4.0-next.1 + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/core-app-api@1.11.2-next.1 + - @backstage/config@1.1.1 + - @backstage/theme@0.5.0-next.0 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.7 + +## @backstage/frontend-plugin-api@0.4.0-next.1 + +### Minor Changes + +- a5a04739e1: The extension `factory` function now longer receives `id` or `source`, but instead now provides the extension's `AppNode` as `node`. The `ExtensionBoundary` component has also been updated to receive a `node` prop rather than `id` and `source`. + +### Patch Changes + +- 5eb6b8a7bc: Added the nav logo extension for customization of sidebar logo +- 1f12fb762c: Create factories for overriding default core components extensions. +- 59709286b3: Add feature flags to plugins and extension overrides. +- e539735435: Added `createSignInPageExtension`. +- f27ee7d937: Migrate analytics api and context files. +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.7 + +## @backstage/repo-tools@0.5.0-next.0 + +### Minor Changes + +- aea8f8d329: **BREAKING**: API Reports generated for sub-path exports now place the name as a suffix rather than prefix, for example `api-report-alpha.md` instead of `alpha-api-report.md`. When upgrading to this version you'll need to re-create any such API reports and delete the old ones. +- 38340678c3: Adds a new command `schema openapi generate-client` that creates a Typescript client with Backstage flavor, including the discovery API and fetch API. This command doesn't currently generate a complete client and needs to be wrapped or exported manually by a separate Backstage plugin. See `@backstage/catalog-client/src/generated` for example output. + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.3 + - @backstage/cli-common@0.1.13 + - @backstage/cli-node@0.2.0 + - @backstage/errors@1.2.3 + +## @techdocs/cli@1.8.0-next.1 + +### Minor Changes + +- b2dccad7b3: Support passing additional `mkdocs-server` CLI parameters (`--dirtyreload`, `--strict` and `--clean`) when run in containerized mode. + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-techdocs-node@1.11.0-next.1 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/cli-common@0.1.13 + - @backstage/config@1.1.1 + +## @backstage/plugin-auth-backend-module-oauth2-proxy-provider@0.1.0-next.0 + +### Minor Changes + +- 271aa12c7c: Release of `oauth2-proxy-provider` plugin + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-auth-node@0.4.2-next.1 + +## @backstage/plugin-auth-backend-module-vmware-cloud-provider@0.1.0-next.0 + +### Minor Changes + +- ed02c69a3c: Add VMware Cloud auth backend module provider + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/plugin-auth-node@0.4.2-next.1 + +## @backstage/plugin-azure-devops-backend@0.5.0-next.1 + +### Minor Changes + +- 844969cd97: **BREAKING** New `fromConfig` static method must be used now when creating an instance of the `AzureDevOpsApi` + + Added support for using the `AzureDevOpsCredentialsProvider` + +### Patch Changes + +- 043b724c56: Introduced new `AzureDevOpsAnnotatorProcessor` that adds the needed annotations automatically. Also, moved constants to common package so they can be shared more easily +- Updated dependencies + - @backstage/plugin-azure-devops-common@0.3.2-next.0 + - @backstage/integration@1.8.0-next.1 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/plugin-catalog-common@1.0.18 + - @backstage/plugin-catalog-node@1.5.1-next.1 + +## @backstage/plugin-catalog@1.16.0-next.2 + +### Minor Changes + +- e223f2264d: Properly support both function- and string-form visibility filter expressions in the new extensions exported via `/alpha`. + +### Patch Changes + +- 53600976bb: Ensure that passed-in icons are taken advantage of in the presentation API +- a5a04739e1: Internal refactor of alpha exports due to a change in how extension factories are defined. +- 78a10bb085: Adding in spec.type chip to search results for clarity +- fb8f3bdbc2: Updated alpha translation message keys to use nested format and camel case. +- 531e1a2a79: Updated alpha plugin to include the `unregisterRedirect` external route. +- Updated dependencies + - @backstage/frontend-plugin-api@0.4.0-next.1 + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-client@1.5.0-next.0 + - @backstage/plugin-search-react@1.7.4-next.1 + - @backstage/core-compat-api@0.0.1-next.1 + - @backstage/integration-react@1.1.22-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.18 + - @backstage/plugin-scaffolder-common@1.4.3 + - @backstage/plugin-search-common@1.2.8 + +## @backstage/plugin-home@0.6.0-next.1 + +### Minor Changes + +- 5a317f59c0: Added view of entities grouped by kind to make it easier to distinguish entities with different kind but same name + +### Patch Changes + +- 2b725913c1: Updated dependency `@rjsf/utils` to `5.14.3`. + Updated dependency `@rjsf/core` to `5.14.3`. + Updated dependency `@rjsf/material-ui` to `5.14.3`. + Updated dependency `@rjsf/validator-ajv8` to `5.14.3`. +- Updated dependencies + - @backstage/frontend-plugin-api@0.4.0-next.1 + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-client@1.5.0-next.0 + - @backstage/core-app-api@1.11.2-next.1 + - @backstage/plugin-home-react@0.1.6-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/theme@0.5.0-next.0 + - @backstage/types@1.1.1 + +## @backstage/plugin-pagerduty@0.7.0-next.1 + +### Minor Changes + +- 5fca16fdf6: This package has been deprecated, consider using [@pagerduty/backstage-plugin](https://github.com/pagerduty/backstage-plugin) instead. + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/plugin-home-react@0.1.6-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + +## @backstage/app-defaults@1.4.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/core-app-api@1.11.2-next.1 + - @backstage/plugin-permission-react@0.4.18-next.1 + - @backstage/theme@0.5.0-next.0 + +## @backstage/backend-app-api@0.5.9-next.1 + +### Patch Changes + +- 1da5f434f3: Ensure redaction of secrets that have accidental extra whitespace around them +- 9f8f266ff4: Add redacting for secrets in stack traces of logs +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/backend-tasks@0.5.13-next.1 + - @backstage/cli-common@0.1.13 + - @backstage/cli-node@0.2.0 + - @backstage/config@1.1.1 + - @backstage/config-loader@1.5.3 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.2-next.1 + - @backstage/plugin-permission-node@0.7.19-next.1 + +## @backstage/backend-common@0.20.0-next.1 + +### Patch Changes + +- 2666675457: Updated dependency `@google-cloud/storage` to `^7.0.0`. +- Updated dependencies + - @backstage/backend-app-api@0.5.9-next.1 + - @backstage/integration@1.8.0-next.1 + - @backstage/backend-dev-utils@0.1.2 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/cli-common@0.1.13 + - @backstage/config@1.1.1 + - @backstage/config-loader@1.5.3 + - @backstage/errors@1.2.3 + - @backstage/integration-aws-node@0.1.8 + - @backstage/types@1.1.1 + +## @backstage/backend-defaults@0.2.8-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-app-api@0.5.9-next.1 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + +## @backstage/backend-openapi-utils@0.1.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + +## @backstage/backend-plugin-api@0.6.8-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-tasks@0.5.13-next.1 + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.2-next.1 + - @backstage/plugin-permission-common@0.7.10 + +## @backstage/backend-tasks@0.5.13-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + +## @backstage/backend-test-utils@0.2.9-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-app-api@0.5.9-next.1 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.2-next.1 + +## @backstage/core-app-api@1.11.2-next.1 + +### Patch Changes + +- 3e358b0dff: Added deprecation warning for React Router v6 beta, please make sure you have migrated your apps to use React Router v6 stable as support for the beta version will be removed. See the [migration tutorial](https://backstage.io/docs/tutorials/react-router-stable-migration) for more information. +- Updated dependencies + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.7 + +## @backstage/core-compat-api@0.0.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.4.0-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/core-app-api@1.11.2-next.1 + +## @backstage/core-components@0.13.9-next.1 + +### Patch Changes + +- e8f2acef80: Added a new `/testUtils` sub-path that initially exports a `mockBreakpoint` helper. +- 07dfdf3702: Updated dependency `linkifyjs` to `4.1.3`. +- a518c5a25b: Updated dependency `@react-hookz/web` to `^23.0.0`. +- f291757e70: Update `linkify-react` to version `4.1.3` +- Updated dependencies + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + - @backstage/version-bridge@1.0.7 + +## @backstage/core-plugin-api@1.8.1-next.1 + +### Patch Changes + +- 0c93dc37b2: The `createTranslationRef` function from the `/alpha` subpath can now also accept a nested object structure of default translation messages, which will be flatted using `.` separators. +- Updated dependencies + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.7 + +## @backstage/create-app@0.5.8-next.2 + +### Patch Changes + +- 375b6f7d68: CircelCI plugin moved permanently +- Updated dependencies + - @backstage/cli-common@0.1.13 + +## @backstage/dev-utils@1.0.25-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/core-app-api@1.11.2-next.1 + - @backstage/app-defaults@1.4.6-next.1 + - @backstage/integration-react@1.1.22-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/theme@0.5.0-next.0 + +## @backstage/eslint-plugin@0.1.4-next.0 + +### Patch Changes + +- 107dc46ab1: The `no-undeclared-imports` rule will now prefer using version queries that already exist en the repo for the same dependency type when installing new packages. + +## @backstage/frontend-test-utils@0.1.0-next.1 + +### Patch Changes + +- e539735435: Updates for `core.router` addition. +- c21c9cf07b: Re-export mock API implementations as well as `TestApiProvider`, `TestApiRegistry`, `withLogCollector`, and `setupRequestMockHandlers` from `@backstage/test-utils`. +- Updated dependencies + - @backstage/frontend-plugin-api@0.4.0-next.1 + - @backstage/frontend-app-api@0.4.0-next.1 + - @backstage/test-utils@1.4.6-next.1 + - @backstage/types@1.1.1 + +## @backstage/integration@1.8.0-next.1 + +### Patch Changes + +- 99fb54183b: Updated dependency `@azure/identity` to `^4.0.0`. +- Updated dependencies + - @backstage/config@1.1.1 + +## @backstage/integration-react@1.1.22-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/integration@1.8.0-next.1 + - @backstage/config@1.1.1 + +## @backstage/test-utils@1.4.6-next.1 + +### Patch Changes + +- e8f2acef80: Deprecated `mockBreakpoint`, as it is now available from `@backstage/core-components/testUtils` instead. +- Updated dependencies + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/core-app-api@1.11.2-next.1 + - @backstage/plugin-permission-react@0.4.18-next.1 + - @backstage/config@1.1.1 + - @backstage/theme@0.5.0-next.0 + - @backstage/types@1.1.1 + - @backstage/plugin-permission-common@0.7.10 + +## @backstage/plugin-adr@0.6.11-next.1 + +### Patch Changes + +- fb8f3bdbc2: Updated alpha translation message keys to use nested format and camel case. +- Updated dependencies + - @backstage/frontend-plugin-api@0.4.0-next.1 + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/plugin-search-react@1.7.4-next.1 + - @backstage/integration-react@1.1.22-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/theme@0.5.0-next.0 + - @backstage/plugin-adr-common@0.2.18-next.1 + - @backstage/plugin-search-common@1.2.8 + +## @backstage/plugin-adr-backend@0.4.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.5.0-next.0 + - @backstage/integration@1.8.0-next.1 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-adr-common@0.2.18-next.1 + - @backstage/plugin-search-common@1.2.8 + +## @backstage/plugin-adr-common@0.2.18-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.8.0-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/plugin-search-common@1.2.8 + +## @backstage/plugin-airbrake@0.3.28-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/test-utils@1.4.6-next.1 + - @backstage/dev-utils@1.0.25-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/theme@0.5.0-next.0 + +## @backstage/plugin-airbrake-backend@0.3.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/config@1.1.1 + +## @backstage/plugin-allure@0.1.44-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/theme@0.5.0-next.0 + +## @backstage/plugin-analytics-module-ga@0.1.36-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/config@1.1.1 + - @backstage/theme@0.5.0-next.0 + +## @backstage/plugin-analytics-module-ga4@0.1.7-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/config@1.1.1 + - @backstage/theme@0.5.0-next.0 + +## @backstage/plugin-analytics-module-newrelic-browser@0.0.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/config@1.1.1 + +## @backstage/plugin-apache-airflow@0.2.18-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + +## @backstage/plugin-api-docs@0.10.2-next.2 + +### Patch Changes + +- e16e7ce6a5: Updated dependency `@asyncapi/react-component` to `1.2.2`. +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/plugin-catalog@1.16.0-next.2 + - @backstage/catalog-model@1.4.3 + - @backstage/theme@0.5.0-next.0 + +## @backstage/plugin-apollo-explorer@0.1.18-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/theme@0.5.0-next.0 + +## @backstage/plugin-app-backend@0.3.56-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/config@1.1.1 + - @backstage/config-loader@1.5.3 + - @backstage/types@1.1.1 + - @backstage/plugin-app-node@0.1.8-next.1 + +## @backstage/plugin-app-node@0.1.8-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.8-next.1 + +## @backstage/plugin-auth-backend@0.20.1-next.1 + +### Patch Changes + +- 7ac25759a5: `oauth2-proxy` auth implementation has been moved to `@backstage/plugin-auth-backend-module-oauth2-proxy-provider` +- bcbbf8e042: Updated dependency `@google-cloud/firestore` to `^7.0.0`. +- Updated dependencies + - @backstage/plugin-auth-backend-module-oauth2-proxy-provider@0.1.0-next.0 + - @backstage/catalog-client@1.5.0-next.0 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/plugin-auth-backend-module-atlassian-provider@0.1.0-next.1 + - @backstage/plugin-auth-backend-module-github-provider@0.1.5-next.1 + - @backstage/plugin-auth-backend-module-gitlab-provider@0.1.5-next.1 + - @backstage/plugin-auth-backend-module-google-provider@0.1.5-next.1 + - @backstage/plugin-auth-backend-module-oauth2-provider@0.1.5-next.1 + - @backstage/plugin-auth-backend-module-okta-provider@0.0.1-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-backend-module-gcp-iap-provider@0.2.2-next.1 + - @backstage/plugin-auth-node@0.4.2-next.1 + - @backstage/plugin-catalog-node@1.5.1-next.1 + +## @backstage/plugin-auth-backend-module-atlassian-provider@0.1.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/plugin-auth-node@0.4.2-next.1 + +## @backstage/plugin-auth-backend-module-gcp-iap-provider@0.2.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.2-next.1 + +## @backstage/plugin-auth-backend-module-github-provider@0.1.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/plugin-auth-node@0.4.2-next.1 + +## @backstage/plugin-auth-backend-module-gitlab-provider@0.1.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/plugin-auth-node@0.4.2-next.1 + +## @backstage/plugin-auth-backend-module-google-provider@0.1.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/plugin-auth-node@0.4.2-next.1 + +## @backstage/plugin-auth-backend-module-microsoft-provider@0.1.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/plugin-auth-node@0.4.2-next.1 + +## @backstage/plugin-auth-backend-module-oauth2-provider@0.1.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/plugin-auth-node@0.4.2-next.1 + +## @backstage/plugin-auth-backend-module-okta-provider@0.0.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/plugin-auth-node@0.4.2-next.1 + +## @backstage/plugin-auth-backend-module-pinniped-provider@0.1.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/config@1.1.1 + - @backstage/plugin-auth-node@0.4.2-next.1 + +## @backstage/plugin-auth-node@0.4.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.5.0-next.0 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + +## @backstage/plugin-azure-devops@0.3.10-next.1 + +### Patch Changes + +- 043b724c56: Introduced new `AzureDevOpsAnnotatorProcessor` that adds the needed annotations automatically. Also, moved constants to common package so they can be shared more easily +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/plugin-azure-devops-common@0.3.2-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + +## @backstage/plugin-azure-devops-common@0.3.2-next.0 + +### Patch Changes + +- 043b724c56: Introduced new `AzureDevOpsAnnotatorProcessor` that adds the needed annotations automatically. Also, moved constants to common package so they can be shared more easily + +## @backstage/plugin-azure-sites@0.1.17-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/theme@0.5.0-next.0 + - @backstage/plugin-azure-sites-common@0.1.1 + +## @backstage/plugin-azure-sites-backend@0.1.18-next.1 + +### Patch Changes + +- 99fb54183b: Updated dependency `@azure/identity` to `^4.0.0`. +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/config@1.1.1 + - @backstage/plugin-azure-sites-common@0.1.1 + +## @backstage/plugin-badges@0.2.52-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + +## @backstage/plugin-badges-backend@0.3.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.5.0-next.0 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-auth-node@0.4.2-next.1 + +## @backstage/plugin-bazaar@0.2.20-next.2 + +### Patch Changes + +- 5d796829bb: Internalize 'AboutField' to break catalog dependency +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-client@1.5.0-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + +## @backstage/plugin-bazaar-backend@0.3.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-auth-node@0.4.2-next.1 + +## @backstage/plugin-bitbucket-cloud-common@0.2.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.8.0-next.1 + +## @backstage/plugin-bitrise@0.1.55-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/theme@0.5.0-next.0 + +## @backstage/plugin-catalog-backend@1.15.1-next.1 + +### Patch Changes + +- 38340678c3: Update the OpenAPI spec to support the use of `openapi-generator`. +- 7123c58b3d: Updated dependency `@types/glob` to `^8.0.0`. +- Updated dependencies + - @backstage/catalog-client@1.5.0-next.0 + - @backstage/integration@1.8.0-next.1 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-openapi-utils@0.1.1-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/backend-tasks@0.5.13-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.2-next.1 + - @backstage/plugin-catalog-common@1.0.18 + - @backstage/plugin-catalog-node@1.5.1-next.1 + - @backstage/plugin-events-node@0.2.17-next.1 + - @backstage/plugin-permission-common@0.7.10 + - @backstage/plugin-permission-node@0.7.19-next.1 + - @backstage/plugin-search-backend-module-catalog@0.1.12-next.1 + +## @backstage/plugin-catalog-backend-module-aws@0.3.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.8.0-next.1 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/plugin-kubernetes-common@0.7.2-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/backend-tasks@0.5.13-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/integration-aws-node@0.1.8 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.18 + - @backstage/plugin-catalog-node@1.5.1-next.1 + +## @backstage/plugin-catalog-backend-module-azure@0.1.27-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.8.0-next.1 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/backend-tasks@0.5.13-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.18 + - @backstage/plugin-catalog-node@1.5.1-next.1 + +## @backstage/plugin-catalog-backend-module-backstage-openapi@0.1.1-next.1 + +### Patch Changes + +- eb44e92898: Support authenticated backends +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-openapi-utils@0.1.1-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/backend-tasks@0.5.13-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-catalog-node@1.5.1-next.1 + +## @backstage/plugin-catalog-backend-module-bitbucket@0.2.23-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.8.0-next.1 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-bitbucket-cloud-common@0.2.15-next.1 + - @backstage/plugin-catalog-node@1.5.1-next.1 + +## @backstage/plugin-catalog-backend-module-bitbucket-cloud@0.1.23-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.5.0-next.0 + - @backstage/integration@1.8.0-next.1 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/backend-tasks@0.5.13-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/plugin-bitbucket-cloud-common@0.2.15-next.1 + - @backstage/plugin-catalog-common@1.0.18 + - @backstage/plugin-catalog-node@1.5.1-next.1 + - @backstage/plugin-events-node@0.2.17-next.1 + +## @backstage/plugin-catalog-backend-module-bitbucket-server@0.1.21-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.8.0-next.1 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/backend-tasks@0.5.13-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-catalog-node@1.5.1-next.1 + +## @backstage/plugin-catalog-backend-module-gcp@0.1.8-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/plugin-kubernetes-common@0.7.2-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/backend-tasks@0.5.13-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/plugin-catalog-node@1.5.1-next.1 + +## @backstage/plugin-catalog-backend-module-gerrit@0.1.24-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.8.0-next.1 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/backend-tasks@0.5.13-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-catalog-node@1.5.1-next.1 + +## @backstage/plugin-catalog-backend-module-github@0.4.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.15.1-next.1 + - @backstage/catalog-client@1.5.0-next.0 + - @backstage/integration@1.8.0-next.1 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/backend-tasks@0.5.13-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/plugin-catalog-common@1.0.18 + - @backstage/plugin-catalog-node@1.5.1-next.1 + - @backstage/plugin-events-node@0.2.17-next.1 + +## @backstage/plugin-catalog-backend-module-github-org@0.1.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/backend-tasks@0.5.13-next.1 + - @backstage/config@1.1.1 + - @backstage/plugin-catalog-backend-module-github@0.4.6-next.1 + - @backstage/plugin-catalog-node@1.5.1-next.1 + +## @backstage/plugin-catalog-backend-module-gitlab@0.3.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.8.0-next.1 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/backend-tasks@0.5.13-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/plugin-catalog-node@1.5.1-next.1 + +## @backstage/plugin-catalog-backend-module-incremental-ingestion@0.4.12-next.1 + +### Patch Changes + +- 43b2eb8f70: Ensure that cursors always come back as JSON on sqlite too +- Updated dependencies + - @backstage/plugin-catalog-backend@1.15.1-next.1 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/backend-tasks@0.5.13-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-catalog-node@1.5.1-next.1 + - @backstage/plugin-events-node@0.2.17-next.1 + - @backstage/plugin-permission-common@0.7.10 + +## @backstage/plugin-catalog-backend-module-ldap@0.5.23-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-tasks@0.5.13-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.18 + - @backstage/plugin-catalog-node@1.5.1-next.1 + +## @backstage/plugin-catalog-backend-module-msgraph@0.5.15-next.1 + +### Patch Changes + +- 99fb54183b: Updated dependency `@azure/identity` to `^4.0.0`. +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/backend-tasks@0.5.13-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/plugin-catalog-common@1.0.18 + - @backstage/plugin-catalog-node@1.5.1-next.1 + +## @backstage/plugin-catalog-backend-module-openapi@0.1.25-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.15.1-next.1 + - @backstage/integration@1.8.0-next.1 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.18 + - @backstage/plugin-catalog-node@1.5.1-next.1 + +## @backstage/plugin-catalog-backend-module-puppetdb@0.1.13-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/backend-tasks@0.5.13-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-node@1.5.1-next.1 + +## @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.1.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/plugin-catalog-common@1.0.18 + - @backstage/plugin-catalog-node@1.5.1-next.1 + - @backstage/plugin-scaffolder-common@1.4.3 + +## @backstage/plugin-catalog-backend-module-unprocessed@0.3.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/plugin-auth-node@0.4.2-next.1 + +## @backstage/plugin-catalog-graph@0.3.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-client@1.5.0-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/theme@0.5.0-next.0 + - @backstage/types@1.1.1 + +## @backstage/plugin-catalog-import@0.10.4-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.4.0-next.1 + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-client@1.5.0-next.0 + - @backstage/integration@1.8.0-next.1 + - @backstage/core-compat-api@0.0.1-next.1 + - @backstage/integration-react@1.1.22-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-catalog-common@1.0.18 + +## @backstage/plugin-catalog-node@1.5.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.5.0-next.0 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.18 + +## @backstage/plugin-catalog-react@1.9.2-next.1 + +### Patch Changes + +- 53600976bb: Ensure that passed-in icons are taken advantage of in the presentation API + +- 08d9e67199: Add default icon for kind resource. + +- a5a04739e1: Internal refactor of alpha exports due to a change in how extension factories are defined. + +- e223f2264d: Breaking alpha-API change to entity visibility filter functions to accept a bare entity as their first argument, instead of an object with an entity property. + + Functions that accept such filters now also support the string expression form of filters. + +- Updated dependencies + - @backstage/frontend-plugin-api@0.4.0-next.1 + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/catalog-client@1.5.0-next.0 + - @backstage/integration-react@1.1.22-next.1 + - @backstage/plugin-permission-react@0.4.18-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.7 + - @backstage/plugin-catalog-common@1.0.18 + - @backstage/plugin-permission-common@0.7.10 + +## @backstage/plugin-catalog-unprocessed-entities@0.1.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + +## @backstage/plugin-cicd-statistics@0.1.30-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + +## @backstage/plugin-cicd-statistics-module-gitlab@0.1.24-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-cicd-statistics@0.1.30-next.1 + - @backstage/catalog-model@1.4.3 + +## @backstage/plugin-circleci@0.3.28-next.1 + +### Patch Changes + +- 375b6f7d68: CircelCI plugin moved permanently +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/theme@0.5.0-next.0 + +## @backstage/plugin-cloudbuild@0.3.28-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/theme@0.5.0-next.0 + +## @backstage/plugin-code-climate@0.1.28-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/theme@0.5.0-next.0 + +## @backstage/plugin-code-coverage@0.2.21-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + +## @backstage/plugin-code-coverage-backend@0.2.22-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.5.0-next.0 + - @backstage/integration@1.8.0-next.1 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-auth-node@0.4.2-next.1 + +## @backstage/plugin-codescene@0.1.20-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + +## @backstage/plugin-config-schema@0.1.48-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + - @backstage/types@1.1.1 + +## @backstage/plugin-cost-insights@0.12.17-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/theme@0.5.0-next.0 + - @backstage/plugin-cost-insights-common@0.1.2 + +## @backstage/plugin-devtools@0.1.7-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-permission-react@0.4.18-next.1 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + - @backstage/types@1.1.1 + - @backstage/plugin-devtools-common@0.1.6 + +## @backstage/plugin-devtools-backend@0.2.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/cli-common@0.1.13 + - @backstage/config@1.1.1 + - @backstage/config-loader@1.5.3 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.2-next.1 + - @backstage/plugin-devtools-common@0.1.6 + - @backstage/plugin-permission-common@0.7.10 + - @backstage/plugin-permission-node@0.7.19-next.1 + +## @backstage/plugin-dynatrace@8.0.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/theme@0.5.0-next.0 + +## @backstage/plugin-entity-feedback@0.2.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + - @backstage/plugin-entity-feedback-common@0.1.3 + +## @backstage/plugin-entity-feedback-backend@0.2.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.5.0-next.0 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/plugin-auth-node@0.4.2-next.1 + - @backstage/plugin-entity-feedback-common@0.1.3 + +## @backstage/plugin-entity-validation@0.1.13-next.1 + +### Patch Changes + +- a518c5a25b: Updated dependency `@react-hookz/web` to `^23.0.0`. +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-client@1.5.0-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + - @backstage/plugin-catalog-common@1.0.18 + +## @backstage/plugin-events-backend@0.2.17-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/config@1.1.1 + - @backstage/plugin-events-node@0.2.17-next.1 + +## @backstage/plugin-events-backend-module-aws-sqs@0.2.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/backend-tasks@0.5.13-next.1 + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + - @backstage/plugin-events-node@0.2.17-next.1 + +## @backstage/plugin-events-backend-module-azure@0.1.18-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/plugin-events-node@0.2.17-next.1 + +## @backstage/plugin-events-backend-module-bitbucket-cloud@0.1.18-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/plugin-events-node@0.2.17-next.1 + +## @backstage/plugin-events-backend-module-gerrit@0.1.18-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/plugin-events-node@0.2.17-next.1 + +## @backstage/plugin-events-backend-module-github@0.1.18-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/config@1.1.1 + - @backstage/plugin-events-node@0.2.17-next.1 + +## @backstage/plugin-events-backend-module-gitlab@0.1.18-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/config@1.1.1 + - @backstage/plugin-events-node@0.2.17-next.1 + +## @backstage/plugin-events-backend-test-utils@0.1.18-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.2.17-next.1 + +## @backstage/plugin-events-node@0.2.17-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.8-next.1 + +## @backstage/plugin-explore@0.4.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.4.0-next.1 + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/plugin-search-react@1.7.4-next.1 + - @backstage/plugin-explore-react@0.0.34-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + - @backstage/plugin-explore-common@0.0.2 + - @backstage/plugin-search-common@1.2.8 + +## @backstage/plugin-explore-backend@0.0.18-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + - @backstage/plugin-explore-common@0.0.2 + - @backstage/plugin-search-backend-module-explore@0.1.12-next.1 + - @backstage/plugin-search-common@1.2.8 + +## @backstage/plugin-explore-react@0.0.34-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-explore-common@0.0.2 + +## @backstage/plugin-firehydrant@0.2.12-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/theme@0.5.0-next.0 + +## @backstage/plugin-fossa@0.2.60-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + +## @backstage/plugin-gcalendar@0.3.21-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + +## @backstage/plugin-gcp-projects@0.3.44-next.1 + +### Patch Changes + +- a518c5a25b: Updated dependency `@react-hookz/web` to `^23.0.0`. +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/theme@0.5.0-next.0 + +## @backstage/plugin-git-release-manager@0.3.40-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/integration@1.8.0-next.1 + - @backstage/theme@0.5.0-next.0 + +## @backstage/plugin-github-actions@0.6.9-next.1 + +### Patch Changes + +- 08d7e4676a: Github Workflow Runs UI is modified to show in optional Card view instead of table, with branch selection option +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/integration@1.8.0-next.1 + - @backstage/integration-react@1.1.22-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/theme@0.5.0-next.0 + +## @backstage/plugin-github-deployments@0.1.59-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/integration@1.8.0-next.1 + - @backstage/integration-react@1.1.22-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + +## @backstage/plugin-github-issues@0.2.17-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/integration@1.8.0-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + +## @backstage/plugin-github-pull-requests-board@0.1.22-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/integration@1.8.0-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/theme@0.5.0-next.0 + +## @backstage/plugin-gitops-profiles@0.3.43-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/config@1.1.1 + - @backstage/theme@0.5.0-next.0 + +## @backstage/plugin-gocd@0.1.34-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + +## @backstage/plugin-graphiql@0.3.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.4.0-next.1 + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/core-compat-api@0.0.1-next.1 + - @backstage/theme@0.5.0-next.0 + +## @backstage/plugin-graphql-voyager@0.1.10-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/theme@0.5.0-next.0 + +## @backstage/plugin-home-react@0.1.6-next.1 + +### Patch Changes + +- 2b725913c1: Updated dependency `@rjsf/utils` to `5.14.3`. + Updated dependency `@rjsf/core` to `5.14.3`. + Updated dependency `@rjsf/material-ui` to `5.14.3`. + Updated dependency `@rjsf/validator-ajv8` to `5.14.3`. +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + +## @backstage/plugin-ilert@0.2.17-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + +## @backstage/plugin-jenkins@0.9.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + - @backstage/plugin-jenkins-common@0.1.21 + +## @backstage/plugin-jenkins-backend@0.3.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.5.0-next.0 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-auth-node@0.4.2-next.1 + - @backstage/plugin-catalog-node@1.5.1-next.1 + - @backstage/plugin-jenkins-common@0.1.21 + - @backstage/plugin-permission-common@0.7.10 + - @backstage/plugin-permission-node@0.7.19-next.1 + +## @backstage/plugin-kafka@0.3.28-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/theme@0.5.0-next.0 + +## @backstage/plugin-kafka-backend@0.3.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + +## @backstage/plugin-kubernetes@0.11.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/plugin-kubernetes-common@0.7.2-next.1 + - @backstage/plugin-kubernetes-react@0.1.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + - @backstage/types@1.1.1 + +## @backstage/plugin-kubernetes-backend@0.14.0-next.1 + +### Patch Changes + +- ae94d3ce6f: Updated dependency `@aws-crypto/sha256-js` to `^5.0.0`. +- 99fb54183b: Updated dependency `@azure/identity` to `^4.0.0`. +- Updated dependencies + - @backstage/catalog-client@1.5.0-next.0 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/plugin-kubernetes-common@0.7.2-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/integration-aws-node@0.1.8 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.2-next.1 + - @backstage/plugin-catalog-node@1.5.1-next.1 + - @backstage/plugin-kubernetes-node@0.1.2-next.1 + - @backstage/plugin-permission-common@0.7.10 + - @backstage/plugin-permission-node@0.7.19-next.1 + +## @backstage/plugin-kubernetes-cluster@0.0.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/plugin-kubernetes-common@0.7.2-next.1 + - @backstage/plugin-kubernetes-react@0.1.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + +## @backstage/plugin-kubernetes-common@0.7.2-next.1 + +### Patch Changes + +- 5d796829bb: Remove unused dependency +- Updated dependencies + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-permission-common@0.7.10 + +## @backstage/plugin-kubernetes-node@0.1.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-kubernetes-common@0.7.2-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/catalog-model@1.4.3 + +## @backstage/plugin-kubernetes-react@0.1.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-kubernetes-common@0.7.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + +## @backstage/plugin-lighthouse@0.4.13-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/theme@0.5.0-next.0 + - @backstage/plugin-lighthouse-common@0.1.4 + +## @backstage/plugin-lighthouse-backend@0.3.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.5.0-next.0 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/backend-tasks@0.5.13-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-node@1.5.1-next.1 + - @backstage/plugin-lighthouse-common@0.1.4 + +## @backstage/plugin-linguist@0.1.13-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + - @backstage/plugin-linguist-common@0.1.2 + +## @backstage/plugin-linguist-backend@0.5.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.5.0-next.0 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/backend-tasks@0.5.13-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.2-next.1 + - @backstage/plugin-catalog-node@1.5.1-next.1 + - @backstage/plugin-linguist-common@0.1.2 + +## @backstage/plugin-microsoft-calendar@0.1.10-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + +## @backstage/plugin-newrelic@0.3.43-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/theme@0.5.0-next.0 + +## @backstage/plugin-newrelic-dashboard@0.3.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + +## @backstage/plugin-nomad@0.1.9-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/theme@0.5.0-next.0 + +## @backstage/plugin-nomad-backend@0.1.10-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + +## @backstage/plugin-octopus-deploy@0.2.10-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/theme@0.5.0-next.0 + +## @backstage/plugin-opencost@0.2.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/theme@0.5.0-next.0 + +## @backstage/plugin-org@0.6.18-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/theme@0.5.0-next.0 + +## @backstage/plugin-org-react@0.1.17-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-client@1.5.0-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/theme@0.5.0-next.0 + +## @backstage/plugin-periskop@0.1.26-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + +## @backstage/plugin-periskop-backend@0.2.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/config@1.1.1 + +## @backstage/plugin-permission-backend@0.5.31-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-auth-node@0.4.2-next.1 + - @backstage/plugin-permission-common@0.7.10 + - @backstage/plugin-permission-node@0.7.19-next.1 + +## @backstage/plugin-permission-backend-module-allow-all-policy@0.1.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/plugin-auth-node@0.4.2-next.1 + - @backstage/plugin-permission-common@0.7.10 + - @backstage/plugin-permission-node@0.7.19-next.1 + +## @backstage/plugin-permission-node@0.7.19-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-auth-node@0.4.2-next.1 + - @backstage/plugin-permission-common@0.7.10 + +## @backstage/plugin-permission-react@0.4.18-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/config@1.1.1 + - @backstage/plugin-permission-common@0.7.10 + +## @backstage/plugin-playlist@0.2.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/plugin-search-react@1.7.4-next.1 + - @backstage/plugin-permission-react@0.4.18-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + - @backstage/plugin-catalog-common@1.0.18 + - @backstage/plugin-permission-common@0.7.10 + - @backstage/plugin-playlist-common@0.1.12 + +## @backstage/plugin-playlist-backend@0.3.12-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.5.0-next.0 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-auth-node@0.4.2-next.1 + - @backstage/plugin-permission-common@0.7.10 + - @backstage/plugin-permission-node@0.7.19-next.1 + - @backstage/plugin-playlist-common@0.1.12 + +## @backstage/plugin-proxy-backend@0.4.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/config@1.1.1 + +## @backstage/plugin-puppetdb@0.1.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + +## @backstage/plugin-rollbar@0.4.28-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/theme@0.5.0-next.0 + +## @backstage/plugin-rollbar-backend@0.1.53-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/config@1.1.1 + +## @backstage/plugin-scaffolder@1.16.2-next.1 + +### Patch Changes + +- 2b725913c1: Updated dependency `@rjsf/utils` to `5.14.3`. + Updated dependency `@rjsf/core` to `5.14.3`. + Updated dependency `@rjsf/material-ui` to `5.14.3`. + Updated dependency `@rjsf/validator-ajv8` to `5.14.3`. +- a518c5a25b: Updated dependency `@react-hookz/web` to `^23.0.0`. +- b5fa6918dc: Fixing `headerOptions` not being passed through the `TemplatePage` component +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-client@1.5.0-next.0 + - @backstage/plugin-scaffolder-react@1.6.2-next.1 + - @backstage/integration@1.8.0-next.1 + - @backstage/integration-react@1.1.22-next.1 + - @backstage/plugin-permission-react@0.4.18-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.18 + - @backstage/plugin-scaffolder-common@1.4.3 + +## @backstage/plugin-scaffolder-backend@1.19.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.15.1-next.1 + - @backstage/catalog-client@1.5.0-next.0 + - @backstage/integration@1.8.0-next.1 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/backend-tasks@0.5.13-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.2-next.1 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.1.5-next.1 + - @backstage/plugin-catalog-common@1.0.18 + - @backstage/plugin-catalog-node@1.5.1-next.1 + - @backstage/plugin-permission-common@0.7.10 + - @backstage/plugin-permission-node@0.7.19-next.1 + - @backstage/plugin-scaffolder-common@1.4.3 + - @backstage/plugin-scaffolder-node@0.2.9-next.1 + +## @backstage/plugin-scaffolder-backend-module-confluence-to-markdown@0.2.9-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.8.0-next.1 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-scaffolder-node@0.2.9-next.1 + +## @backstage/plugin-scaffolder-backend-module-cookiecutter@0.2.32-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.8.0-next.1 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-scaffolder-node@0.2.9-next.1 + +## @backstage/plugin-scaffolder-backend-module-gitlab@0.2.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.8.0-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-scaffolder-node@0.2.9-next.1 + +## @backstage/plugin-scaffolder-backend-module-rails@0.4.25-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.8.0-next.1 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-scaffolder-node@0.2.9-next.1 + +## @backstage/plugin-scaffolder-backend-module-sentry@0.1.16-next.1 + +### Patch Changes + +- 7f8a801e6d: Added examples for `sentry:project:create` scaffolder action and unit tests. +- Updated dependencies + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-scaffolder-node@0.2.9-next.1 + +## @backstage/plugin-scaffolder-backend-module-yeoman@0.2.29-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + - @backstage/plugin-scaffolder-node@0.2.9-next.1 + +## @backstage/plugin-scaffolder-node@0.2.9-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.8.0-next.1 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-scaffolder-common@1.4.3 + +## @backstage/plugin-scaffolder-react@1.6.2-next.1 + +### Patch Changes + +- fa66d1b5b3: Fixed bug in `ReviewState` where `enum` value was displayed in step review instead of the corresponding label when using `enumNames` +- 2aee53bbeb: Add horizontal slider if stepper overflows +- 2b725913c1: Updated dependency `@rjsf/utils` to `5.14.3`. + Updated dependency `@rjsf/core` to `5.14.3`. + Updated dependency `@rjsf/material-ui` to `5.14.3`. + Updated dependency `@rjsf/validator-ajv8` to `5.14.3`. +- a518c5a25b: Updated dependency `@react-hookz/web` to `^23.0.0`. +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-client@1.5.0-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.7 + - @backstage/plugin-scaffolder-common@1.4.3 + +## @backstage/plugin-search@1.4.4-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.4.0-next.1 + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/plugin-search-react@1.7.4-next.1 + - @backstage/core-compat-api@0.0.1-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.7 + - @backstage/plugin-search-common@1.2.8 + +## @backstage/plugin-search-backend@1.4.8-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-openapi-utils@0.1.1-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.2-next.1 + - @backstage/plugin-permission-common@0.7.10 + - @backstage/plugin-permission-node@0.7.19-next.1 + - @backstage/plugin-search-backend-node@1.2.12-next.1 + - @backstage/plugin-search-common@1.2.8 + +## @backstage/plugin-search-backend-module-catalog@0.1.12-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.5.0-next.0 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/backend-tasks@0.5.13-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-catalog-common@1.0.18 + - @backstage/plugin-catalog-node@1.5.1-next.1 + - @backstage/plugin-permission-common@0.7.10 + - @backstage/plugin-search-backend-node@1.2.12-next.1 + - @backstage/plugin-search-common@1.2.8 + +## @backstage/plugin-search-backend-module-elasticsearch@1.3.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/config@1.1.1 + - @backstage/integration-aws-node@0.1.8 + - @backstage/plugin-search-backend-node@1.2.12-next.1 + - @backstage/plugin-search-common@1.2.8 + +## @backstage/plugin-search-backend-module-explore@0.1.12-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/backend-tasks@0.5.13-next.1 + - @backstage/config@1.1.1 + - @backstage/plugin-explore-common@0.0.2 + - @backstage/plugin-search-backend-node@1.2.12-next.1 + - @backstage/plugin-search-common@1.2.8 + +## @backstage/plugin-search-backend-module-pg@0.5.17-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/config@1.1.1 + - @backstage/plugin-search-backend-node@1.2.12-next.1 + - @backstage/plugin-search-common@1.2.8 + +## @backstage/plugin-search-backend-module-stack-overflow-collator@0.1.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/backend-tasks@0.5.13-next.1 + - @backstage/config@1.1.1 + - @backstage/plugin-search-backend-node@1.2.12-next.1 + - @backstage/plugin-search-common@1.2.8 + +## @backstage/plugin-search-backend-module-techdocs@0.1.12-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.5.0-next.0 + - @backstage/plugin-techdocs-node@1.11.0-next.1 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/backend-tasks@0.5.13-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/plugin-catalog-common@1.0.18 + - @backstage/plugin-catalog-node@1.5.1-next.1 + - @backstage/plugin-permission-common@0.7.10 + - @backstage/plugin-search-backend-node@1.2.12-next.1 + - @backstage/plugin-search-common@1.2.8 + +## @backstage/plugin-search-backend-node@1.2.12-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/backend-tasks@0.5.13-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-permission-common@0.7.10 + - @backstage/plugin-search-common@1.2.8 + +## @backstage/plugin-search-react@1.7.4-next.1 + +### Patch Changes + +- a5a04739e1: Internal refactor of alpha exports due to a change in how extension factories are defined. +- Updated dependencies + - @backstage/frontend-plugin-api@0.4.0-next.1 + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/theme@0.5.0-next.0 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.7 + - @backstage/plugin-search-common@1.2.8 + +## @backstage/plugin-sentry@0.5.13-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/theme@0.5.0-next.0 + +## @backstage/plugin-shortcuts@0.3.17-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/theme@0.5.0-next.0 + - @backstage/types@1.1.1 + +## @backstage/plugin-sonarqube@0.7.10-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/plugin-sonarqube-react@0.1.11-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/theme@0.5.0-next.0 + +## @backstage/plugin-sonarqube-backend@0.2.10-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + +## @backstage/plugin-sonarqube-react@0.1.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/catalog-model@1.4.3 + +## @backstage/plugin-splunk-on-call@0.4.17-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/theme@0.5.0-next.0 + +## @backstage/plugin-stack-overflow@0.1.23-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.4.0-next.1 + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-search-react@1.7.4-next.1 + - @backstage/plugin-home-react@0.1.6-next.1 + - @backstage/config@1.1.1 + - @backstage/theme@0.5.0-next.0 + - @backstage/plugin-search-common@1.2.8 + +## @backstage/plugin-stack-overflow-backend@0.2.12-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/config@1.1.1 + - @backstage/plugin-search-backend-module-stack-overflow-collator@0.1.1-next.1 + - @backstage/plugin-search-common@1.2.8 + +## @backstage/plugin-stackstorm@0.1.9-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + +## @backstage/plugin-tech-insights@0.3.20-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + - @backstage/types@1.1.1 + - @backstage/plugin-tech-insights-common@0.2.12 + +## @backstage/plugin-tech-insights-backend@0.5.22-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.5.0-next.0 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-tasks@0.5.13-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-tech-insights-common@0.2.12 + - @backstage/plugin-tech-insights-node@0.4.14-next.1 + +## @backstage/plugin-tech-insights-backend-module-jsonfc@0.1.40-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-tech-insights-common@0.2.12 + - @backstage/plugin-tech-insights-node@0.4.14-next.1 + +## @backstage/plugin-tech-insights-node@0.4.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-tasks@0.5.13-next.1 + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + - @backstage/plugin-tech-insights-common@0.2.12 + +## @backstage/plugin-tech-radar@0.6.11-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.4.0-next.1 + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/core-compat-api@0.0.1-next.1 + - @backstage/theme@0.5.0-next.0 + +## @backstage/plugin-techdocs@1.9.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.4.0-next.1 + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/plugin-search-react@1.7.4-next.1 + - @backstage/integration@1.8.0-next.1 + - @backstage/core-compat-api@0.0.1-next.1 + - @backstage/integration-react@1.1.22-next.1 + - @backstage/plugin-techdocs-react@1.1.14-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + - @backstage/plugin-search-common@1.2.8 + +## @backstage/plugin-techdocs-addons-test-utils@1.0.25-next.2 + +### Patch Changes + +- 5d796829bb: Remove unnecessary catalog dependency +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/plugin-catalog@1.16.0-next.2 + - @backstage/core-app-api@1.11.2-next.1 + - @backstage/test-utils@1.4.6-next.1 + - @backstage/plugin-search-react@1.7.4-next.1 + - @backstage/plugin-techdocs@1.9.2-next.2 + - @backstage/integration-react@1.1.22-next.1 + - @backstage/plugin-techdocs-react@1.1.14-next.1 + - @backstage/theme@0.5.0-next.0 + +## @backstage/plugin-techdocs-backend@1.9.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.5.0-next.0 + - @backstage/integration@1.8.0-next.1 + - @backstage/plugin-techdocs-node@1.11.0-next.1 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-catalog-common@1.0.18 + - @backstage/plugin-permission-common@0.7.10 + - @backstage/plugin-search-backend-module-techdocs@0.1.12-next.1 + - @backstage/plugin-search-common@1.2.8 + +## @backstage/plugin-techdocs-module-addons-contrib@1.1.3-next.1 + +### Patch Changes + +- a518c5a25b: Updated dependency `@react-hookz/web` to `^23.0.0`. +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/integration@1.8.0-next.1 + - @backstage/integration-react@1.1.22-next.1 + - @backstage/plugin-techdocs-react@1.1.14-next.1 + - @backstage/theme@0.5.0-next.0 + +## @backstage/plugin-techdocs-node@1.11.0-next.1 + +### Patch Changes + +- 99fb54183b: Updated dependency `@azure/identity` to `^4.0.0`. +- 2666675457: Updated dependency `@google-cloud/storage` to `^7.0.0`. +- 4f773c15f6: Bumped the default TechDocs docker image version to the latest which was released several month ago +- Updated dependencies + - @backstage/integration@1.8.0-next.1 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/integration-aws-node@0.1.8 + - @backstage/plugin-search-common@1.2.8 + +## @backstage/plugin-techdocs-react@1.1.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/version-bridge@1.0.7 + +## @backstage/plugin-todo@0.2.32-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + +## @backstage/plugin-todo-backend@0.3.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.5.0-next.0 + - @backstage/integration@1.8.0-next.1 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-openapi-utils@0.1.1-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-catalog-node@1.5.1-next.1 + +## @backstage/plugin-user-settings@0.7.14-next.2 + +### Patch Changes + +- fb8f3bdbc2: Updated alpha translation message keys to use nested format and camel case. +- Updated dependencies + - @backstage/frontend-plugin-api@0.4.0-next.1 + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/core-app-api@1.11.2-next.1 + - @backstage/core-compat-api@0.0.1-next.1 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + - @backstage/types@1.1.1 + +## @backstage/plugin-user-settings-backend@0.2.7-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.2-next.1 + +## @backstage/plugin-vault@0.1.23-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + +## @backstage/plugin-vault-backend@0.4.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/backend-tasks@0.5.13-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-vault-node@0.1.1-next.1 + +## @backstage/plugin-vault-node@0.1.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.8-next.1 + +## @backstage/plugin-xcmetrics@0.2.46-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + +## example-app@0.2.90-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-app-api@0.4.0-next.1 + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/cli@0.25.0-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/plugin-catalog@1.16.0-next.2 + - @backstage/plugin-scaffolder-react@1.6.2-next.1 + - @backstage/plugin-home@0.6.0-next.1 + - @backstage/plugin-github-actions@0.6.9-next.1 + - @backstage/core-app-api@1.11.2-next.1 + - @backstage/plugin-search-react@1.7.4-next.1 + - @backstage/plugin-azure-devops@0.3.10-next.1 + - @backstage/plugin-scaffolder@1.16.2-next.1 + - @backstage/plugin-api-docs@0.10.2-next.2 + - @backstage/plugin-gcp-projects@0.3.44-next.1 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.3-next.1 + - @backstage/plugin-user-settings@0.7.14-next.2 + - @backstage/plugin-adr@0.6.11-next.1 + - @backstage/plugin-pagerduty@0.7.0-next.1 + - @backstage/plugin-catalog-import@0.10.4-next.2 + - @backstage/plugin-explore@0.4.14-next.1 + - @backstage/plugin-graphiql@0.3.1-next.2 + - @backstage/plugin-search@1.4.4-next.2 + - @backstage/plugin-stack-overflow@0.1.23-next.1 + - @backstage/plugin-tech-radar@0.6.11-next.2 + - @backstage/plugin-techdocs@1.9.2-next.2 + - @backstage/app-defaults@1.4.6-next.1 + - @backstage/integration-react@1.1.22-next.1 + - @backstage/plugin-airbrake@0.3.28-next.1 + - @backstage/plugin-apache-airflow@0.2.18-next.1 + - @backstage/plugin-azure-sites@0.1.17-next.1 + - @backstage/plugin-badges@0.2.52-next.1 + - @backstage/plugin-catalog-graph@0.3.2-next.1 + - @backstage/plugin-catalog-unprocessed-entities@0.1.6-next.1 + - @backstage/plugin-cloudbuild@0.3.28-next.1 + - @backstage/plugin-code-coverage@0.2.21-next.1 + - @backstage/plugin-cost-insights@0.12.17-next.1 + - @backstage/plugin-devtools@0.1.7-next.1 + - @backstage/plugin-dynatrace@8.0.2-next.1 + - @backstage/plugin-entity-feedback@0.2.11-next.1 + - @backstage/plugin-gcalendar@0.3.21-next.1 + - @backstage/plugin-gocd@0.1.34-next.1 + - @backstage/plugin-jenkins@0.9.3-next.1 + - @backstage/plugin-kafka@0.3.28-next.1 + - @backstage/plugin-kubernetes@0.11.3-next.1 + - @backstage/plugin-kubernetes-cluster@0.0.4-next.1 + - @backstage/plugin-lighthouse@0.4.13-next.1 + - @backstage/plugin-linguist@0.1.13-next.1 + - @backstage/plugin-microsoft-calendar@0.1.10-next.1 + - @backstage/plugin-newrelic@0.3.43-next.1 + - @backstage/plugin-newrelic-dashboard@0.3.3-next.1 + - @backstage/plugin-nomad@0.1.9-next.1 + - @backstage/plugin-octopus-deploy@0.2.10-next.1 + - @backstage/plugin-org@0.6.18-next.1 + - @backstage/plugin-playlist@0.2.2-next.1 + - @backstage/plugin-puppetdb@0.1.11-next.1 + - @backstage/plugin-rollbar@0.4.28-next.1 + - @backstage/plugin-sentry@0.5.13-next.1 + - @backstage/plugin-shortcuts@0.3.17-next.1 + - @backstage/plugin-stackstorm@0.1.9-next.1 + - @backstage/plugin-tech-insights@0.3.20-next.1 + - @backstage/plugin-techdocs-react@1.1.14-next.1 + - @backstage/plugin-todo@0.2.32-next.1 + - @backstage/plugin-permission-react@0.4.18-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/theme@0.5.0-next.0 + - @backstage/plugin-catalog-common@1.0.18 + - @backstage/plugin-linguist-common@0.1.2 + - @backstage/plugin-search-common@1.2.8 + +## example-app-next@0.0.4-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.4.0-next.1 + - @backstage/frontend-app-api@0.4.0-next.1 + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/cli@0.25.0-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/plugin-catalog@1.16.0-next.2 + - @backstage/plugin-scaffolder-react@1.6.2-next.1 + - @backstage/plugin-home@0.6.0-next.1 + - @backstage/plugin-github-actions@0.6.9-next.1 + - @backstage/core-app-api@1.11.2-next.1 + - @backstage/plugin-search-react@1.7.4-next.1 + - @backstage/plugin-azure-devops@0.3.10-next.1 + - @backstage/plugin-scaffolder@1.16.2-next.1 + - @backstage/plugin-api-docs@0.10.2-next.2 + - @backstage/plugin-gcp-projects@0.3.44-next.1 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.3-next.1 + - @backstage/plugin-user-settings@0.7.14-next.2 + - @backstage/plugin-adr@0.6.11-next.1 + - @backstage/plugin-pagerduty@0.7.0-next.1 + - app-next-example-plugin@0.0.4-next.1 + - @backstage/core-compat-api@0.0.1-next.1 + - @backstage/plugin-catalog-import@0.10.4-next.2 + - @backstage/plugin-explore@0.4.14-next.1 + - @backstage/plugin-graphiql@0.3.1-next.2 + - @backstage/plugin-search@1.4.4-next.2 + - @backstage/plugin-tech-radar@0.6.11-next.2 + - @backstage/plugin-techdocs@1.9.2-next.2 + - @backstage/app-defaults@1.4.6-next.1 + - @backstage/integration-react@1.1.22-next.1 + - @backstage/plugin-airbrake@0.3.28-next.1 + - @backstage/plugin-apache-airflow@0.2.18-next.1 + - @backstage/plugin-azure-sites@0.1.17-next.1 + - @backstage/plugin-badges@0.2.52-next.1 + - @backstage/plugin-catalog-graph@0.3.2-next.1 + - @backstage/plugin-catalog-unprocessed-entities@0.1.6-next.1 + - @backstage/plugin-cloudbuild@0.3.28-next.1 + - @backstage/plugin-code-coverage@0.2.21-next.1 + - @backstage/plugin-cost-insights@0.12.17-next.1 + - @backstage/plugin-devtools@0.1.7-next.1 + - @backstage/plugin-dynatrace@8.0.2-next.1 + - @backstage/plugin-entity-feedback@0.2.11-next.1 + - @backstage/plugin-gcalendar@0.3.21-next.1 + - @backstage/plugin-gocd@0.1.34-next.1 + - @backstage/plugin-jenkins@0.9.3-next.1 + - @backstage/plugin-kafka@0.3.28-next.1 + - @backstage/plugin-kubernetes@0.11.3-next.1 + - @backstage/plugin-lighthouse@0.4.13-next.1 + - @backstage/plugin-linguist@0.1.13-next.1 + - @backstage/plugin-microsoft-calendar@0.1.10-next.1 + - @backstage/plugin-newrelic@0.3.43-next.1 + - @backstage/plugin-newrelic-dashboard@0.3.3-next.1 + - @backstage/plugin-octopus-deploy@0.2.10-next.1 + - @backstage/plugin-org@0.6.18-next.1 + - @backstage/plugin-playlist@0.2.2-next.1 + - @backstage/plugin-puppetdb@0.1.11-next.1 + - @backstage/plugin-rollbar@0.4.28-next.1 + - @backstage/plugin-sentry@0.5.13-next.1 + - @backstage/plugin-shortcuts@0.3.17-next.1 + - @backstage/plugin-stackstorm@0.1.9-next.1 + - @backstage/plugin-tech-insights@0.3.20-next.1 + - @backstage/plugin-techdocs-react@1.1.14-next.1 + - @backstage/plugin-todo@0.2.32-next.1 + - @backstage/plugin-permission-react@0.4.18-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/theme@0.5.0-next.0 + - @backstage/plugin-catalog-common@1.0.18 + - @backstage/plugin-linguist-common@0.1.2 + - @backstage/plugin-search-common@1.2.8 + +## app-next-example-plugin@0.0.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.4.0-next.1 + - @backstage/core-components@0.13.9-next.1 + +## example-backend@0.2.90-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-backend@0.20.1-next.1 + - @backstage/plugin-catalog-backend@1.15.1-next.1 + - @backstage/catalog-client@1.5.0-next.0 + - @backstage/plugin-azure-devops-backend@0.5.0-next.1 + - @backstage/plugin-kubernetes-backend@0.14.0-next.1 + - @backstage/integration@1.8.0-next.1 + - @backstage/plugin-azure-sites-backend@0.1.18-next.1 + - @backstage/backend-common@0.20.0-next.1 + - example-app@0.2.90-next.2 + - @backstage/backend-tasks@0.5.13-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/plugin-adr-backend@0.4.5-next.1 + - @backstage/plugin-app-backend@0.3.56-next.1 + - @backstage/plugin-auth-node@0.4.2-next.1 + - @backstage/plugin-badges-backend@0.3.5-next.1 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.1.5-next.1 + - @backstage/plugin-catalog-backend-module-unprocessed@0.3.5-next.1 + - @backstage/plugin-catalog-node@1.5.1-next.1 + - @backstage/plugin-code-coverage-backend@0.2.22-next.1 + - @backstage/plugin-devtools-backend@0.2.5-next.1 + - @backstage/plugin-entity-feedback-backend@0.2.5-next.1 + - @backstage/plugin-events-backend@0.2.17-next.1 + - @backstage/plugin-events-node@0.2.17-next.1 + - @backstage/plugin-explore-backend@0.0.18-next.1 + - @backstage/plugin-jenkins-backend@0.3.2-next.1 + - @backstage/plugin-kafka-backend@0.3.6-next.1 + - @backstage/plugin-lighthouse-backend@0.3.5-next.1 + - @backstage/plugin-linguist-backend@0.5.5-next.1 + - @backstage/plugin-nomad-backend@0.1.10-next.1 + - @backstage/plugin-permission-backend@0.5.31-next.1 + - @backstage/plugin-permission-common@0.7.10 + - @backstage/plugin-permission-node@0.7.19-next.1 + - @backstage/plugin-playlist-backend@0.3.12-next.1 + - @backstage/plugin-proxy-backend@0.4.6-next.1 + - @backstage/plugin-rollbar-backend@0.1.53-next.1 + - @backstage/plugin-scaffolder-backend@1.19.2-next.1 + - @backstage/plugin-scaffolder-backend-module-confluence-to-markdown@0.2.9-next.1 + - @backstage/plugin-scaffolder-backend-module-rails@0.4.25-next.1 + - @backstage/plugin-search-backend@1.4.8-next.1 + - @backstage/plugin-search-backend-module-catalog@0.1.12-next.1 + - @backstage/plugin-search-backend-module-elasticsearch@1.3.11-next.1 + - @backstage/plugin-search-backend-module-explore@0.1.12-next.1 + - @backstage/plugin-search-backend-module-pg@0.5.17-next.1 + - @backstage/plugin-search-backend-module-techdocs@0.1.12-next.1 + - @backstage/plugin-search-backend-node@1.2.12-next.1 + - @backstage/plugin-search-common@1.2.8 + - @backstage/plugin-tech-insights-backend@0.5.22-next.1 + - @backstage/plugin-tech-insights-backend-module-jsonfc@0.1.40-next.1 + - @backstage/plugin-tech-insights-node@0.4.14-next.1 + - @backstage/plugin-techdocs-backend@1.9.1-next.1 + - @backstage/plugin-todo-backend@0.3.6-next.1 + +## example-backend-next@0.0.18-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend-module-backstage-openapi@0.1.1-next.1 + - @backstage/plugin-catalog-backend@1.15.1-next.1 + - @backstage/plugin-azure-devops-backend@0.5.0-next.1 + - @backstage/plugin-kubernetes-backend@0.14.0-next.1 + - @backstage/backend-defaults@0.2.8-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/backend-tasks@0.5.13-next.1 + - @backstage/plugin-adr-backend@0.4.5-next.1 + - @backstage/plugin-app-backend@0.3.56-next.1 + - @backstage/plugin-auth-node@0.4.2-next.1 + - @backstage/plugin-badges-backend@0.3.5-next.1 + - @backstage/plugin-catalog-backend-module-openapi@0.1.25-next.1 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.1.5-next.1 + - @backstage/plugin-catalog-backend-module-unprocessed@0.3.5-next.1 + - @backstage/plugin-devtools-backend@0.2.5-next.1 + - @backstage/plugin-entity-feedback-backend@0.2.5-next.1 + - @backstage/plugin-jenkins-backend@0.3.2-next.1 + - @backstage/plugin-lighthouse-backend@0.3.5-next.1 + - @backstage/plugin-linguist-backend@0.5.5-next.1 + - @backstage/plugin-nomad-backend@0.1.10-next.1 + - @backstage/plugin-permission-backend@0.5.31-next.1 + - @backstage/plugin-permission-backend-module-allow-all-policy@0.1.5-next.1 + - @backstage/plugin-permission-common@0.7.10 + - @backstage/plugin-permission-node@0.7.19-next.1 + - @backstage/plugin-playlist-backend@0.3.12-next.1 + - @backstage/plugin-proxy-backend@0.4.6-next.1 + - @backstage/plugin-scaffolder-backend@1.19.2-next.1 + - @backstage/plugin-search-backend@1.4.8-next.1 + - @backstage/plugin-search-backend-module-catalog@0.1.12-next.1 + - @backstage/plugin-search-backend-module-explore@0.1.12-next.1 + - @backstage/plugin-search-backend-module-techdocs@0.1.12-next.1 + - @backstage/plugin-search-backend-node@1.2.12-next.1 + - @backstage/plugin-sonarqube-backend@0.2.10-next.1 + - @backstage/plugin-techdocs-backend@1.9.1-next.1 + - @backstage/plugin-todo-backend@0.3.6-next.1 + +## @backstage/backend-plugin-manager@0.0.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.15.1-next.1 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/backend-tasks@0.5.13-next.1 + - @backstage/cli-common@0.1.13 + - @backstage/cli-node@0.2.0 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.2-next.1 + - @backstage/plugin-events-backend@0.2.17-next.1 + - @backstage/plugin-events-node@0.2.17-next.1 + - @backstage/plugin-permission-common@0.7.10 + - @backstage/plugin-permission-node@0.7.19-next.1 + - @backstage/plugin-scaffolder-node@0.2.9-next.1 + - @backstage/plugin-search-backend-node@1.2.12-next.1 + - @backstage/plugin-search-common@1.2.8 + +## e2e-test@0.2.10-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/create-app@0.5.8-next.2 + - @backstage/cli-common@0.1.13 + - @backstage/errors@1.2.3 + +## techdocs-cli-embedded-app@0.2.89-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/cli@0.25.0-next.1 + - @backstage/plugin-catalog@1.16.0-next.2 + - @backstage/core-app-api@1.11.2-next.1 + - @backstage/test-utils@1.4.6-next.1 + - @backstage/plugin-techdocs@1.9.2-next.2 + - @backstage/app-defaults@1.4.6-next.1 + - @backstage/integration-react@1.1.22-next.1 + - @backstage/plugin-techdocs-react@1.1.14-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/theme@0.5.0-next.0 + +## @internal/plugin-todo-list@1.0.20-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/theme@0.5.0-next.0 + +## @internal/plugin-todo-list-backend@1.0.20-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-auth-node@0.4.2-next.1 diff --git a/docs/tutorials/migrate-to-mui5.md b/docs/tutorials/migrate-to-mui5.md new file mode 100644 index 0000000000..96129ceb8a --- /dev/null +++ b/docs/tutorials/migrate-to-mui5.md @@ -0,0 +1,65 @@ +--- +id: migrate-to-mui5 +title: Migrating from Material UI v4 to v5 +description: Additional resources for the Material UI v5 migration guide specifically for Backstage +--- + +Backstage supports developing new plugins or components using Material UI v5. At the same time, large parts of the application as well as existing plugins will still be using Material UI v4. To support Material UI v4 and v5 at the same time, we have introduced a new concept called the `UnifiedTheme`. The goal of the `UnifiedTheme` is to allow gradual migration by running both versions in parallel, applying theme options similarly & supporting potential future versions of Material UI. + +By default, the `UnifiedThemeProvider` is already used. If you add a custom theme in your `createApp` function, you would need to replace the Material UI `ThemeProvider` with the `UnifiedThemeProvider`: + +```diff ts ++ import import { ++ UnifiedThemeProvider, ++ themes as builtinThemes, ++ } from '@backstage/theme'; + + const app = createApp({ + // ... + themes: [ + { + // ... + provider: ({ children }) => ( +- . +- {children}. +- + ), + } + ] + }); +``` + +Before making specific changes to your Backstage instance, it might be helpful to take a look at the [Migration Guide provided by Material UI](https://mui.com/material-ui/migration/migration-v4/) first. It breaks down the differences between v4 and v5, and will make it easier to understand the impact on your Backstage instance & plugins. + +It is worth noting that we are still using `@mui/styles` & `jss`. You may stumble upon documentation for migrating to `emotion` when using `makeStyles` or `withStyles`. It is not necessary to switch to `emotion`. + +Important to keep in mind is that Material UI v5 is meant to be used with React Version 17 or higher. This means if you intend to use the Material UI v5 components in your plugins, you have to enforce React Version to be at least 17 for these plugins: + +```json +... + "peerDependencies": { + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0", + "react-router-dom": "6.0.0-beta.0 || ^6.3.0" + }, +... +``` + +To comply with Material UI recommendations, we are enforcing a new linting rule that favors standard imports over named imports and also restricts 3rd-level imports as they are considered private ([Guide: Minimizing Bundle Size](https://mui.com/material-ui/guides/minimizing-bundle-size)). + +There are `core-components` as well as components exported from Backstage `*-react` plugins written in Material UI v4, which expect Material UI components as props. In these cases you will still be forced to use Material UI v4. + +For current known issues with the Material UI v5 migration, follow our [Milestone on GitHub](https://github.com/backstage/backstage/milestone/40). Please open a new issue if you run into different problems. + +### Plugins + +To migrate your plugin to Material UI v5, you can build on the resources available. + +1. Manually fix the imports from named to default imports to match the new [linting rules for minimizing bundle size](https://mui.com/material-ui/guides/minimizing-bundle-size). +2. Run the migration `codemod` for the path of the specific plugin: `npx @mui/codemod v5.0.0/preset-safe plugins/`. +3. Take a look at possible `TODO:` items the `codemod` could not fix. +4. Remove types & methods from `@backstage/theme` which are marked as `@deprecated`. +5. Ensure you are using `"react": "^17.0.0"` (or newer) as a peer dependency + +You can follow the [migration of the GraphiQL plugin](https://github.com/backstage/backstage/pull/17696) as an example of a plugin migration. diff --git a/microsite/data/plugins/circleci.yaml b/microsite/data/plugins/circleci.yaml index 5d7978624b..5980f8cf45 100644 --- a/microsite/data/plugins/circleci.yaml +++ b/microsite/data/plugins/circleci.yaml @@ -1,12 +1,12 @@ --- title: CircleCI -author: Spotify -authorUrl: https://github.com/spotify +author: CircleCI +authorUrl: https://circleci.com/ category: CI/CD description: Automate your development process with CI hosted in the cloud or on a private server. -documentation: https://github.com/backstage/backstage/tree/master/plugins/circleci +documentation: https://github.com/CircleCI-Public/backstage-plugin/tree/main/plugins/circleci iconUrl: /img/circleci.png -npmPackageName: '@backstage/plugin-circleci' +npmPackageName: '@circleci/backstage-plugin' tags: - ci - cd diff --git a/microsite/data/plugins/hcp-consul.yaml b/microsite/data/plugins/hcp-consul.yaml new file mode 100644 index 0000000000..6dabad582e --- /dev/null +++ b/microsite/data/plugins/hcp-consul.yaml @@ -0,0 +1,10 @@ +--- +title: HCP Consul +author: Hashicorp +authorUrl: https://www.hashicorp.com/ +category: Infrastructure +description: View your HCP Consul clusters, services, instances in Backstage. +documentation: https://github.com/hashicorp-forge/backstage-plugin-hcp-consul/tree/main +iconUrl: /img/hcp-consul.svg +npmPackageName: '@hashicorp/plugin-hcp-consul' +addedDate: '2023-11-24' diff --git a/microsite/data/plugins/jira-dashboard b/microsite/data/plugins/jira-dashboard new file mode 100644 index 0000000000..2e67deb4a9 --- /dev/null +++ b/microsite/data/plugins/jira-dashboard @@ -0,0 +1,10 @@ +--- +title: Jira Dashboard +author: AxisCommunications +authorUrl: https://github.com/AxisCommunications +category: Agile Planning +description: Allows you to fetch and display Jira issues for your entity. You get quickly access to issue summaries to achieve better task visibility and more efficient project management. +documentation: https://github.com/AxisCommunications/backstage-plugins/blob/main/plugins/jira-dashboard/README.md +iconUrl: https://github.com/AxisCommunications/backstage-plugins/blob/main/plugins/jira-dashboard/media/jira-logo.png +npmPackageName: '@axis-backstage/plugin-jira-dashboard' +addedDate: '2023-11-29' diff --git a/microsite/data/plugins/scaffolder-backend-slack.yaml b/microsite/data/plugins/scaffolder-backend-slack.yaml index dce7b60eb7..a726751eaf 100644 --- a/microsite/data/plugins/scaffolder-backend-slack.yaml +++ b/microsite/data/plugins/scaffolder-backend-slack.yaml @@ -5,6 +5,6 @@ authorUrl: https://github.com/arhill05 category: Scaffolder description: Interact with Slack from scaffolder templates documentation: https://github.com/arhill05/backstage-plugin-scaffolder-backend-module-slack#readme -iconUrl: img/Slack-mark-RGB.png +iconUrl: /img/Slack-mark-RGB.png npmPackageName: '@mdude2314/backstage-plugin-scaffolder-backend-module-slack' addedDate: '2023-08-04' diff --git a/microsite/data/plugins/search-backend-module-azure-devops-wiki.yaml b/microsite/data/plugins/search-backend-module-azure-devops-wiki.yaml index 02d4131cce..cc8849ee08 100644 --- a/microsite/data/plugins/search-backend-module-azure-devops-wiki.yaml +++ b/microsite/data/plugins/search-backend-module-azure-devops-wiki.yaml @@ -5,6 +5,6 @@ authorUrl: https://github.com/arhill05 category: Search description: Index wiki articles from an Azure DevOps wiki into Backstage to allow you to search them with the Backstage Search feature. documentation: https://github.com/arhill05/backstage-plugin-search-backend-module-azure-devops-wiki#readme -iconUrl: img/ado-wiki-search-icon.png +iconUrl: /img/ado-wiki-search-icon.png npmPackageName: '@mdude2314/backstage-plugin-search-backend-module-azure-devops-wiki' addedDate: '2023-06-13' diff --git a/microsite/sidebars.json b/microsite/sidebars.json index c9b1c99eba..fb513697cb 100644 --- a/microsite/sidebars.json +++ b/microsite/sidebars.json @@ -408,7 +408,8 @@ "tutorials/configuring-plugin-databases", "tutorials/switching-sqlite-postgres", "tutorials/using-backstage-proxy-within-plugin", - "tutorials/yarn-migration" + "tutorials/yarn-migration", + "tutorials/migrate-to-mui5" ], "Architecture Decision Records (ADRs)": [ "architecture-decisions/adrs-overview", diff --git a/microsite/static/img/hcp-consul.svg b/microsite/static/img/hcp-consul.svg new file mode 100644 index 0000000000..d84972585c --- /dev/null +++ b/microsite/static/img/hcp-consul.svg @@ -0,0 +1,17 @@ + +Consul Logo + + + + + + + + + + + + + + + diff --git a/microsite/yarn.lock b/microsite/yarn.lock index 0eed7b6eb3..928b81ab16 100644 --- a/microsite/yarn.lock +++ b/microsite/yarn.lock @@ -3045,9 +3045,9 @@ __metadata: linkType: hard "@types/luxon@npm:^3.0.0": - version: 3.3.5 - resolution: "@types/luxon@npm:3.3.5" - checksum: 7fd31c673600dd494dedefe8c405d2e6957c0ba101f8445f7eaf9afcf4a1fa1ca649cab01c9777523ff9de58c3c35e2589a1a004a0c629cd7953033115ac74f5 + version: 3.3.6 + resolution: "@types/luxon@npm:3.3.6" + checksum: 44fd3e617fb5d9e370a8d5529de3e703b295bfe225936e786e0b5e7fce662ef82012a6ec29cd2763afbd59bf9a9ab43186bad5c51aa552d963cd5e8c8f4e1d4f languageName: node linkType: hard diff --git a/mkdocs.yml b/mkdocs.yml index de66c97884..d4735c63fc 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -193,6 +193,7 @@ nav: - Switching Backstage from SQLite to PostgreSQL: 'tutorials/switching-sqlite-postgres.md' - Using the Backstage Proxy from Within a Plugin: 'tutorials/using-backstage-proxy-within-plugin.md' - Migration to Yarn 3: 'tutorials/yarn-migration.md' + - Migration to Material UI v5: 'tutorials/migrate-to-mui5.md' - Architecture Decision Records (ADRs): - Overview: 'architecture-decisions/index.md' - ADR001 - Architecture Decision Record (ADR) log: 'architecture-decisions/adr001-add-adr-log.md' diff --git a/package.json b/package.json index 1b23829f76..15c4a95953 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "@material-ui/pickers@^3.3.10": "patch:@material-ui/pickers@npm%3A3.3.11#./.yarn/patches/@material-ui-pickers-npm-3.3.11-1c8f68ea20.patch", "@material-ui/pickers@^3.2.10": "patch:@material-ui/pickers@npm%3A3.3.11#./.yarn/patches/@material-ui-pickers-npm-3.3.11-1c8f68ea20.patch" }, - "version": "1.21.0-next.1", + "version": "1.21.0-next.2", "dependencies": { "@backstage/errors": "workspace:^", "@manypkg/get-packages": "^1.1.3", diff --git a/packages/app-defaults/CHANGELOG.md b/packages/app-defaults/CHANGELOG.md index 62873d9809..3326a33169 100644 --- a/packages/app-defaults/CHANGELOG.md +++ b/packages/app-defaults/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/app-defaults +## 1.4.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/core-app-api@1.11.2-next.1 + - @backstage/plugin-permission-react@0.4.18-next.1 + - @backstage/theme@0.5.0-next.0 + ## 1.4.6-next.0 ### Patch Changes diff --git a/packages/app-defaults/package.json b/packages/app-defaults/package.json index 550672d7fb..4857a14492 100644 --- a/packages/app-defaults/package.json +++ b/packages/app-defaults/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/app-defaults", "description": "Provides the default wiring of a Backstage App", - "version": "1.4.6-next.0", + "version": "1.4.6-next.1", "publishConfig": { "access": "public", "main": "dist/index.esm.js", diff --git a/packages/app-next-example-plugin/CHANGELOG.md b/packages/app-next-example-plugin/CHANGELOG.md index 2eff29241d..d47b2cf2eb 100644 --- a/packages/app-next-example-plugin/CHANGELOG.md +++ b/packages/app-next-example-plugin/CHANGELOG.md @@ -1,5 +1,13 @@ # app-next-example-plugin +## 0.0.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.4.0-next.1 + - @backstage/core-components@0.13.9-next.1 + ## 0.0.4-next.0 ### Patch Changes diff --git a/packages/app-next-example-plugin/package.json b/packages/app-next-example-plugin/package.json index b3de8f9a83..4e7e84dbe2 100644 --- a/packages/app-next-example-plugin/package.json +++ b/packages/app-next-example-plugin/package.json @@ -1,7 +1,7 @@ { "name": "app-next-example-plugin", "description": "Backstage internal example plugin", - "version": "0.0.4-next.0", + "version": "0.0.4-next.1", "publishConfig": { "access": "public", "main": "dist/index.esm.js", diff --git a/packages/app-next-example-plugin/src/plugin.tsx b/packages/app-next-example-plugin/src/plugin.tsx index a3c95e900c..99c377b7e5 100644 --- a/packages/app-next-example-plugin/src/plugin.tsx +++ b/packages/app-next-example-plugin/src/plugin.tsx @@ -21,7 +21,6 @@ import { } from '@backstage/frontend-plugin-api'; export const ExamplePage = createPageExtension({ - id: 'example.page', defaultPath: '/example', loader: () => import('./Component').then(m => ), }); diff --git a/packages/app-next/CHANGELOG.md b/packages/app-next/CHANGELOG.md index 879025f1a0..ccf60e4e76 100644 --- a/packages/app-next/CHANGELOG.md +++ b/packages/app-next/CHANGELOG.md @@ -1,5 +1,81 @@ # example-app-next +## 0.0.4-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.4.0-next.1 + - @backstage/frontend-app-api@0.4.0-next.1 + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/cli@0.25.0-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/plugin-catalog@1.16.0-next.2 + - @backstage/plugin-scaffolder-react@1.6.2-next.1 + - @backstage/plugin-home@0.6.0-next.1 + - @backstage/plugin-github-actions@0.6.9-next.1 + - @backstage/core-app-api@1.11.2-next.1 + - @backstage/plugin-search-react@1.7.4-next.1 + - @backstage/plugin-azure-devops@0.3.10-next.1 + - @backstage/plugin-scaffolder@1.16.2-next.1 + - @backstage/plugin-api-docs@0.10.2-next.2 + - @backstage/plugin-gcp-projects@0.3.44-next.1 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.3-next.1 + - @backstage/plugin-user-settings@0.7.14-next.2 + - @backstage/plugin-adr@0.6.11-next.1 + - @backstage/plugin-pagerduty@0.7.0-next.1 + - app-next-example-plugin@0.0.4-next.1 + - @backstage/core-compat-api@0.0.1-next.1 + - @backstage/plugin-catalog-import@0.10.4-next.2 + - @backstage/plugin-explore@0.4.14-next.1 + - @backstage/plugin-graphiql@0.3.1-next.2 + - @backstage/plugin-search@1.4.4-next.2 + - @backstage/plugin-tech-radar@0.6.11-next.2 + - @backstage/plugin-techdocs@1.9.2-next.2 + - @backstage/app-defaults@1.4.6-next.1 + - @backstage/integration-react@1.1.22-next.1 + - @backstage/plugin-airbrake@0.3.28-next.1 + - @backstage/plugin-apache-airflow@0.2.18-next.1 + - @backstage/plugin-azure-sites@0.1.17-next.1 + - @backstage/plugin-badges@0.2.52-next.1 + - @backstage/plugin-catalog-graph@0.3.2-next.1 + - @backstage/plugin-catalog-unprocessed-entities@0.1.6-next.1 + - @backstage/plugin-cloudbuild@0.3.28-next.1 + - @backstage/plugin-code-coverage@0.2.21-next.1 + - @backstage/plugin-cost-insights@0.12.17-next.1 + - @backstage/plugin-devtools@0.1.7-next.1 + - @backstage/plugin-dynatrace@8.0.2-next.1 + - @backstage/plugin-entity-feedback@0.2.11-next.1 + - @backstage/plugin-gcalendar@0.3.21-next.1 + - @backstage/plugin-gocd@0.1.34-next.1 + - @backstage/plugin-jenkins@0.9.3-next.1 + - @backstage/plugin-kafka@0.3.28-next.1 + - @backstage/plugin-kubernetes@0.11.3-next.1 + - @backstage/plugin-lighthouse@0.4.13-next.1 + - @backstage/plugin-linguist@0.1.13-next.1 + - @backstage/plugin-microsoft-calendar@0.1.10-next.1 + - @backstage/plugin-newrelic@0.3.43-next.1 + - @backstage/plugin-newrelic-dashboard@0.3.3-next.1 + - @backstage/plugin-octopus-deploy@0.2.10-next.1 + - @backstage/plugin-org@0.6.18-next.1 + - @backstage/plugin-playlist@0.2.2-next.1 + - @backstage/plugin-puppetdb@0.1.11-next.1 + - @backstage/plugin-rollbar@0.4.28-next.1 + - @backstage/plugin-sentry@0.5.13-next.1 + - @backstage/plugin-shortcuts@0.3.17-next.1 + - @backstage/plugin-stackstorm@0.1.9-next.1 + - @backstage/plugin-tech-insights@0.3.20-next.1 + - @backstage/plugin-techdocs-react@1.1.14-next.1 + - @backstage/plugin-todo@0.2.32-next.1 + - @backstage/plugin-permission-react@0.4.18-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/theme@0.5.0-next.0 + - @backstage/plugin-catalog-common@1.0.18 + - @backstage/plugin-linguist-common@0.1.2 + - @backstage/plugin-search-common@1.2.8 + ## 0.0.4-next.1 ### Patch Changes diff --git a/packages/app-next/app-config.yaml b/packages/app-next/app-config.yaml index a46e119ac0..3eaab2fa1a 100644 --- a/packages/app-next/app-config.yaml +++ b/packages/app-next/app-config.yaml @@ -7,18 +7,17 @@ app: plugin.catalog.externalRoutes.viewTechDoc: plugin.techdocs.routes.docRoot extensions: - - apis.plugin.graphiql.browse.gitlab: true + # - apis.plugin.graphiql.browse.gitlab: true + - graphiql-endpoint:graphiql/gitlab: true - # Entity page cards - - entity.cards.about - - entity.cards.labels - - entity.cards.links: + - entity-card:catalog/about + - entity-card:catalog/labels + - entity-card:catalog/links: config: - filter: - - isKind: component + filter: kind:component has:links # Entity page content - - entity.content.techdocs + - entity-content:techdocs # scmAuthExtension: >- # createScmAuthExtension({ diff --git a/packages/app-next/package.json b/packages/app-next/package.json index 30ac920c2c..8e441b0e4a 100644 --- a/packages/app-next/package.json +++ b/packages/app-next/package.json @@ -1,6 +1,6 @@ { "name": "example-app-next", - "version": "0.0.4-next.1", + "version": "0.0.4-next.2", "private": true, "backstage": { "role": "frontend" @@ -31,7 +31,6 @@ "@backstage/plugin-catalog-import": "workspace:^", "@backstage/plugin-catalog-react": "workspace:^", "@backstage/plugin-catalog-unprocessed-entities": "workspace:^", - "@backstage/plugin-circleci": "workspace:^", "@backstage/plugin-cloudbuild": "workspace:^", "@backstage/plugin-code-coverage": "workspace:^", "@backstage/plugin-cost-insights": "workspace:^", @@ -77,6 +76,7 @@ "@backstage/plugin-todo": "workspace:^", "@backstage/plugin-user-settings": "workspace:^", "@backstage/theme": "workspace:^", + "@circleci/backstage-plugin": "^0.1.1", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.61", diff --git a/packages/app-next/src/App.tsx b/packages/app-next/src/App.tsx index 67faf8d646..ccfeb61b58 100644 --- a/packages/app-next/src/App.tsx +++ b/packages/app-next/src/App.tsx @@ -17,6 +17,7 @@ import React from 'react'; import { createApp } from '@backstage/frontend-app-api'; import { pagesPlugin } from './examples/pagesPlugin'; +import { CustomNotFoundErrorPage } from './examples/notFoundErrorPageExtension'; import graphiqlPlugin from '@backstage/plugin-graphiql/alpha'; import techRadarPlugin from '@backstage/plugin-tech-radar/alpha'; import userSettingsPlugin from '@backstage/plugin-user-settings/alpha'; @@ -32,7 +33,10 @@ import { } from '@backstage/frontend-plugin-api'; import techdocsPlugin from '@backstage/plugin-techdocs/alpha'; import { homePage } from './HomePage'; -import { collectLegacyRoutes } from '@backstage/core-compat-api'; +import { + collectLegacyComponents, + collectLegacyRoutes, +} from '@backstage/core-compat-api'; import { FlatRoutes } from '@backstage/core-app-api'; import { Route } from 'react-router'; import { CatalogImportPage } from '@backstage/plugin-catalog-import'; @@ -79,7 +83,7 @@ TODO: /* app.tsx */ const homePageExtension = createExtension({ - id: 'myhomepage', + name: 'myhomepage', attachTo: { id: 'home', input: 'props' }, output: { children: coreExtensionData.reactElement, @@ -91,7 +95,7 @@ const homePageExtension = createExtension({ }); const signInPage = createSignInPageExtension({ - id: 'signInPage', + name: 'guest', loader: async () => (props: SignInPageProps) => , }); @@ -114,6 +118,10 @@ const collectedLegacyPlugins = collectLegacyRoutes( , ); +const legacyAppComponents = collectLegacyComponents({ + NotFoundErrorPage: CustomNotFoundErrorPage, +}); + const app = createApp({ features: [ graphiqlPlugin, @@ -129,6 +137,7 @@ const app = createApp({ scmAuthExtension, scmIntegrationApi, signInPage, + ...legacyAppComponents, ], }), ], diff --git a/packages/app-next/src/examples/notFoundErrorPageExtension.tsx b/packages/app-next/src/examples/notFoundErrorPageExtension.tsx new file mode 100644 index 0000000000..82acc25fb5 --- /dev/null +++ b/packages/app-next/src/examples/notFoundErrorPageExtension.tsx @@ -0,0 +1,62 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import { + createComponentExtension, + coreComponentsRefs, +} from '@backstage/frontend-plugin-api'; +import { Box, Typography } from '@material-ui/core'; +import { Button } from '@backstage/core-components'; + +export function CustomNotFoundErrorPage() { + return ( + + 404 + + Bowie was unable to locate this page. Please contact your support team + if this page used to exist. + + Backstage bowie + + + ); +} + +export default createComponentExtension({ + ref: coreComponentsRefs.notFoundErrorPage, + component: { sync: () => CustomNotFoundErrorPage }, +}); diff --git a/packages/app-next/src/examples/pagesPlugin.tsx b/packages/app-next/src/examples/pagesPlugin.tsx index 1e9409a5e4..c5069f99c3 100644 --- a/packages/app-next/src/examples/pagesPlugin.tsx +++ b/packages/app-next/src/examples/pagesPlugin.tsx @@ -36,7 +36,7 @@ export const pageXRouteRef = createRouteRef(); // }); const IndexPage = createPageExtension({ - id: 'index', + name: 'index', defaultPath: '/', routeRef: indexRouteRef, loader: async () => { @@ -68,7 +68,7 @@ const IndexPage = createPageExtension({ }); const Page1 = createPageExtension({ - id: 'page1', + name: 'page1', defaultPath: '/page1', routeRef: page1RouteRef, loader: async () => { @@ -102,7 +102,7 @@ const Page1 = createPageExtension({ }); const ExternalPage = createPageExtension({ - id: 'pageX', + name: 'pageX', defaultPath: '/pageX', routeRef: pageXRouteRef, loader: async () => { diff --git a/packages/app/CHANGELOG.md b/packages/app/CHANGELOG.md index 089aa84942..546a7918d7 100644 --- a/packages/app/CHANGELOG.md +++ b/packages/app/CHANGELOG.md @@ -1,5 +1,81 @@ # example-app +## 0.2.90-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-app-api@0.4.0-next.1 + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/cli@0.25.0-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/plugin-catalog@1.16.0-next.2 + - @backstage/plugin-scaffolder-react@1.6.2-next.1 + - @backstage/plugin-home@0.6.0-next.1 + - @backstage/plugin-github-actions@0.6.9-next.1 + - @backstage/core-app-api@1.11.2-next.1 + - @backstage/plugin-search-react@1.7.4-next.1 + - @backstage/plugin-azure-devops@0.3.10-next.1 + - @backstage/plugin-scaffolder@1.16.2-next.1 + - @backstage/plugin-api-docs@0.10.2-next.2 + - @backstage/plugin-gcp-projects@0.3.44-next.1 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.3-next.1 + - @backstage/plugin-user-settings@0.7.14-next.2 + - @backstage/plugin-adr@0.6.11-next.1 + - @backstage/plugin-pagerduty@0.7.0-next.1 + - @backstage/plugin-catalog-import@0.10.4-next.2 + - @backstage/plugin-explore@0.4.14-next.1 + - @backstage/plugin-graphiql@0.3.1-next.2 + - @backstage/plugin-search@1.4.4-next.2 + - @backstage/plugin-stack-overflow@0.1.23-next.1 + - @backstage/plugin-tech-radar@0.6.11-next.2 + - @backstage/plugin-techdocs@1.9.2-next.2 + - @backstage/app-defaults@1.4.6-next.1 + - @backstage/integration-react@1.1.22-next.1 + - @backstage/plugin-airbrake@0.3.28-next.1 + - @backstage/plugin-apache-airflow@0.2.18-next.1 + - @backstage/plugin-azure-sites@0.1.17-next.1 + - @backstage/plugin-badges@0.2.52-next.1 + - @backstage/plugin-catalog-graph@0.3.2-next.1 + - @backstage/plugin-catalog-unprocessed-entities@0.1.6-next.1 + - @backstage/plugin-cloudbuild@0.3.28-next.1 + - @backstage/plugin-code-coverage@0.2.21-next.1 + - @backstage/plugin-cost-insights@0.12.17-next.1 + - @backstage/plugin-devtools@0.1.7-next.1 + - @backstage/plugin-dynatrace@8.0.2-next.1 + - @backstage/plugin-entity-feedback@0.2.11-next.1 + - @backstage/plugin-gcalendar@0.3.21-next.1 + - @backstage/plugin-gocd@0.1.34-next.1 + - @backstage/plugin-jenkins@0.9.3-next.1 + - @backstage/plugin-kafka@0.3.28-next.1 + - @backstage/plugin-kubernetes@0.11.3-next.1 + - @backstage/plugin-kubernetes-cluster@0.0.4-next.1 + - @backstage/plugin-lighthouse@0.4.13-next.1 + - @backstage/plugin-linguist@0.1.13-next.1 + - @backstage/plugin-microsoft-calendar@0.1.10-next.1 + - @backstage/plugin-newrelic@0.3.43-next.1 + - @backstage/plugin-newrelic-dashboard@0.3.3-next.1 + - @backstage/plugin-nomad@0.1.9-next.1 + - @backstage/plugin-octopus-deploy@0.2.10-next.1 + - @backstage/plugin-org@0.6.18-next.1 + - @backstage/plugin-playlist@0.2.2-next.1 + - @backstage/plugin-puppetdb@0.1.11-next.1 + - @backstage/plugin-rollbar@0.4.28-next.1 + - @backstage/plugin-sentry@0.5.13-next.1 + - @backstage/plugin-shortcuts@0.3.17-next.1 + - @backstage/plugin-stackstorm@0.1.9-next.1 + - @backstage/plugin-tech-insights@0.3.20-next.1 + - @backstage/plugin-techdocs-react@1.1.14-next.1 + - @backstage/plugin-todo@0.2.32-next.1 + - @backstage/plugin-permission-react@0.4.18-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/theme@0.5.0-next.0 + - @backstage/plugin-catalog-common@1.0.18 + - @backstage/plugin-linguist-common@0.1.2 + - @backstage/plugin-search-common@1.2.8 + ## 0.2.90-next.1 ### Patch Changes diff --git a/packages/app/package.json b/packages/app/package.json index 7cf7ae3290..e47388a7e1 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -1,6 +1,6 @@ { "name": "example-app", - "version": "0.2.90-next.1", + "version": "0.2.90-next.2", "private": true, "backstage": { "role": "frontend" @@ -35,7 +35,6 @@ "@backstage/plugin-catalog-import": "workspace:^", "@backstage/plugin-catalog-react": "workspace:^", "@backstage/plugin-catalog-unprocessed-entities": "workspace:^", - "@backstage/plugin-circleci": "workspace:^", "@backstage/plugin-cloudbuild": "workspace:^", "@backstage/plugin-code-coverage": "workspace:^", "@backstage/plugin-cost-insights": "workspace:^", @@ -84,6 +83,7 @@ "@backstage/plugin-todo": "workspace:^", "@backstage/plugin-user-settings": "workspace:^", "@backstage/theme": "workspace:^", + "@circleci/backstage-plugin": "^0.1.1", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.61", @@ -102,7 +102,7 @@ "react-use": "^17.2.4", "vite": "^4.4.9", "vite-plugin-html": "^3.2.0", - "vite-plugin-node-polyfills": "^0.16.0", + "vite-plugin-node-polyfills": "^0.17.0", "zen-observable": "^0.10.0" }, "devDependencies": { diff --git a/packages/app/src/components/catalog/EntityPage.tsx b/packages/app/src/components/catalog/EntityPage.tsx index ecc1f008b5..3bd8869f4f 100644 --- a/packages/app/src/components/catalog/EntityPage.tsx +++ b/packages/app/src/components/catalog/EntityPage.tsx @@ -77,7 +77,7 @@ import { import { EntityCircleCIContent, isCircleCIAvailable, -} from '@backstage/plugin-circleci'; +} from '@circleci/backstage-plugin'; import { EntityCloudbuildContent, isCloudbuildAvailable, diff --git a/packages/backend-app-api/CHANGELOG.md b/packages/backend-app-api/CHANGELOG.md index 1f19f67eea..e3a25d9514 100644 --- a/packages/backend-app-api/CHANGELOG.md +++ b/packages/backend-app-api/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/backend-app-api +## 0.5.9-next.1 + +### Patch Changes + +- 1da5f434f3: Ensure redaction of secrets that have accidental extra whitespace around them +- 9f8f266ff4: Add redacting for secrets in stack traces of logs +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/backend-tasks@0.5.13-next.1 + - @backstage/cli-common@0.1.13 + - @backstage/cli-node@0.2.0 + - @backstage/config@1.1.1 + - @backstage/config-loader@1.5.3 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.2-next.1 + - @backstage/plugin-permission-node@0.7.19-next.1 + ## 0.5.9-next.0 ### Patch Changes diff --git a/packages/backend-app-api/alpha-api-report.md b/packages/backend-app-api/api-report-alpha.md similarity index 100% rename from packages/backend-app-api/alpha-api-report.md rename to packages/backend-app-api/api-report-alpha.md diff --git a/packages/backend-app-api/package.json b/packages/backend-app-api/package.json index 51e052d8a3..bf1269d0c9 100644 --- a/packages/backend-app-api/package.json +++ b/packages/backend-app-api/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/backend-app-api", "description": "Core API used by Backstage backend apps", - "version": "0.5.9-next.0", + "version": "0.5.9-next.1", "main": "src/index.ts", "types": "src/index.ts", "publishConfig": { diff --git a/packages/backend-app-api/src/logging/WinstonLogger.test.ts b/packages/backend-app-api/src/logging/WinstonLogger.test.ts index 5d8cfc9c90..c7173b1cbe 100644 --- a/packages/backend-app-api/src/logging/WinstonLogger.test.ts +++ b/packages/backend-app-api/src/logging/WinstonLogger.test.ts @@ -14,25 +14,37 @@ * limitations under the License. */ +import { TransformableInfo } from 'logform'; import { WinstonLogger } from './WinstonLogger'; -function msg(message: string) { - return { message, level: 'info' }; +function msg(info: TransformableInfo): TransformableInfo { + return { message: info.message, level: info.level, stack: info.stack }; } describe('WinstonLogger', () => { it('redacter should redact and escape regex', () => { const redacter = WinstonLogger.redacter(); - expect(redacter.format.transform(msg('hello (world)'))).toEqual( - msg('hello (world)'), + const log = { + level: 'error', + message: 'hello (world)', + stack: 'hello (world) from this file', + }; + expect(redacter.format.transform(msg(log))).toEqual(msg(log)); + redacter.add(['hello\n']); + expect(redacter.format.transform(msg(log))).toEqual( + msg({ + ...log, + message: '[REDACTED] (world)', + stack: '[REDACTED] (world) from this file', + }), ); - redacter.add(['hello']); - expect(redacter.format.transform(msg('hello (world)'))).toEqual( - msg('[REDACTED] (world)'), - ); - redacter.add(['(world)']); - expect(redacter.format.transform(msg('hello (world)'))).toEqual( - msg('[REDACTED] [REDACTED]'), + redacter.add(['(world']); + expect(redacter.format.transform(msg(log))).toEqual( + msg({ + ...log, + message: '[REDACTED] [REDACTED])', + stack: '[REDACTED] [REDACTED]) from this file', + }), ); }); }); diff --git a/packages/backend-app-api/src/logging/WinstonLogger.ts b/packages/backend-app-api/src/logging/WinstonLogger.ts index 06a189508b..7dd0ed80b0 100644 --- a/packages/backend-app-api/src/logging/WinstonLogger.ts +++ b/packages/backend-app-api/src/logging/WinstonLogger.ts @@ -82,11 +82,18 @@ export class WinstonLogger implements RootLoggerService { if (redactionPattern && typeof info.message === 'string') { info.message = info.message.replace(redactionPattern, '[REDACTED]'); } + if (redactionPattern && typeof info.stack === 'string') { + info.stack = info.stack.replace(redactionPattern, '[REDACTED]'); + } return info; })(), add(newRedactions) { let added = 0; - for (const redaction of newRedactions) { + for (const redactionToTrim of newRedactions) { + // Trimming the string ensures that we don't accdentally get extra + // newlines or other whitespace interfering with the redaction; this + // can happen for example when using string literals in yaml + const redaction = redactionToTrim.trim(); // Exclude secrets that are empty or just one character in length. These // typically mean that you are running local dev or tests, or using the // --lax flag which sets things to just 'x'. diff --git a/packages/backend-app-api/src/wiring/BackendInitializer.test.ts b/packages/backend-app-api/src/wiring/BackendInitializer.test.ts index 791b9b1c3a..b5b53083a9 100644 --- a/packages/backend-app-api/src/wiring/BackendInitializer.test.ts +++ b/packages/backend-app-api/src/wiring/BackendInitializer.test.ts @@ -102,7 +102,7 @@ describe('BackendInitializer', () => { init.add( createBackendModule({ pluginId: 'test', - moduleId: 'modA', + moduleId: 'mod-a', register(reg) { reg.registerInit({ deps: { extension: extensionPoint }, @@ -118,7 +118,7 @@ describe('BackendInitializer', () => { init.add( createBackendModule({ pluginId: 'test', - moduleId: 'modB', + moduleId: 'mod-b', register(reg) { const values = ['b']; reg.registerExtensionPoint(extensionPoint, { values }); @@ -135,7 +135,7 @@ describe('BackendInitializer', () => { init.add( createBackendModule({ pluginId: 'test', - moduleId: 'modC', + moduleId: 'mod-c', register(reg) { reg.registerInit({ deps: { extension: extensionPoint }, @@ -265,7 +265,7 @@ describe('BackendInitializer', () => { init.add( createBackendModule({ pluginId: 'test', - moduleId: 'modA', + moduleId: 'mod-a', register(reg) { reg.registerExtensionPoint(extA, 'a'); reg.registerInit({ @@ -278,7 +278,7 @@ describe('BackendInitializer', () => { init.add( createBackendModule({ pluginId: 'test', - moduleId: 'modB', + moduleId: 'mod-b', register(reg) { reg.registerExtensionPoint(extB, 'b'); reg.registerInit({ @@ -289,7 +289,7 @@ describe('BackendInitializer', () => { })(), ); await expect(init.start()).rejects.toThrow( - "Circular dependency detected for modules of plugin 'test', 'modA' -> 'modB' -> 'modA'", + "Circular dependency detected for modules of plugin 'test', 'mod-a' -> 'mod-b' -> 'mod-a'", ); }); @@ -298,7 +298,7 @@ describe('BackendInitializer', () => { const extA = createExtensionPoint({ id: 'a' }); init.add( createBackendPlugin({ - pluginId: 'testA', + pluginId: 'test-a', register(reg) { reg.registerExtensionPoint(extA, 'a'); reg.registerInit({ @@ -310,7 +310,7 @@ describe('BackendInitializer', () => { ); init.add( createBackendModule({ - pluginId: 'testB', + pluginId: 'test-b', moduleId: 'mod', register(reg) { reg.registerInit({ @@ -321,7 +321,7 @@ describe('BackendInitializer', () => { })(), ); await expect(init.start()).rejects.toThrow( - "Extension point registered for plugin 'testA' may not be used by module for plugin 'testB'", + "Extension point registered for plugin 'test-a' may not be used by module for plugin 'test-b'", ); }); }); diff --git a/packages/backend-common/CHANGELOG.md b/packages/backend-common/CHANGELOG.md index 16a4bcafb4..dbe198bdb8 100644 --- a/packages/backend-common/CHANGELOG.md +++ b/packages/backend-common/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/backend-common +## 0.20.0-next.1 + +### Patch Changes + +- 2666675457: Updated dependency `@google-cloud/storage` to `^7.0.0`. +- Updated dependencies + - @backstage/backend-app-api@0.5.9-next.1 + - @backstage/integration@1.8.0-next.1 + - @backstage/backend-dev-utils@0.1.2 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/cli-common@0.1.13 + - @backstage/config@1.1.1 + - @backstage/config-loader@1.5.3 + - @backstage/errors@1.2.3 + - @backstage/integration-aws-node@0.1.8 + - @backstage/types@1.1.1 + ## 0.20.0-next.0 ### Minor Changes diff --git a/packages/backend-common/alpha-api-report.md b/packages/backend-common/api-report-alpha.md similarity index 100% rename from packages/backend-common/alpha-api-report.md rename to packages/backend-common/api-report-alpha.md diff --git a/packages/backend-common/testUtils-api-report.md b/packages/backend-common/api-report-testUtils.md similarity index 100% rename from packages/backend-common/testUtils-api-report.md rename to packages/backend-common/api-report-testUtils.md diff --git a/packages/backend-common/package.json b/packages/backend-common/package.json index 14436fc1f2..357c929e6c 100644 --- a/packages/backend-common/package.json +++ b/packages/backend-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/backend-common", "description": "Common functionality library for Backstage backends", - "version": "0.20.0-next.0", + "version": "0.20.0-next.1", "main": "src/index.ts", "types": "src/index.ts", "publishConfig": { @@ -75,7 +75,7 @@ "@types/express": "^4.17.6", "@types/luxon": "^3.0.0", "@types/webpack-env": "^1.15.2", - "archiver": "^5.0.2", + "archiver": "^6.0.0", "base64-stream": "^1.0.0", "compression": "^1.7.4", "concat-stream": "^2.0.0", @@ -118,7 +118,7 @@ "@aws-sdk/util-stream-node": "^3.350.0", "@backstage/backend-test-utils": "workspace:^", "@backstage/cli": "workspace:^", - "@types/archiver": "^5.1.0", + "@types/archiver": "^6.0.0", "@types/base64-stream": "^1.0.2", "@types/compression": "^1.7.0", "@types/concat-stream": "^2.0.0", diff --git a/packages/backend-defaults/CHANGELOG.md b/packages/backend-defaults/CHANGELOG.md index 7354e39b9d..5f2063890a 100644 --- a/packages/backend-defaults/CHANGELOG.md +++ b/packages/backend-defaults/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/backend-defaults +## 0.2.8-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-app-api@0.5.9-next.1 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + ## 0.2.8-next.0 ### Patch Changes diff --git a/packages/backend-defaults/package.json b/packages/backend-defaults/package.json index bb2e2b0743..d9ff1ea302 100644 --- a/packages/backend-defaults/package.json +++ b/packages/backend-defaults/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/backend-defaults", "description": "Backend defaults used by Backstage backend apps", - "version": "0.2.8-next.0", + "version": "0.2.8-next.1", "main": "src/index.ts", "types": "src/index.ts", "publishConfig": { diff --git a/packages/backend-next/CHANGELOG.md b/packages/backend-next/CHANGELOG.md index 5d035799c5..c1bfc21fba 100644 --- a/packages/backend-next/CHANGELOG.md +++ b/packages/backend-next/CHANGELOG.md @@ -1,5 +1,46 @@ # example-backend-next +## 0.0.18-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend-module-backstage-openapi@0.1.1-next.1 + - @backstage/plugin-catalog-backend@1.15.1-next.1 + - @backstage/plugin-azure-devops-backend@0.5.0-next.1 + - @backstage/plugin-kubernetes-backend@0.14.0-next.1 + - @backstage/backend-defaults@0.2.8-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/backend-tasks@0.5.13-next.1 + - @backstage/plugin-adr-backend@0.4.5-next.1 + - @backstage/plugin-app-backend@0.3.56-next.1 + - @backstage/plugin-auth-node@0.4.2-next.1 + - @backstage/plugin-badges-backend@0.3.5-next.1 + - @backstage/plugin-catalog-backend-module-openapi@0.1.25-next.1 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.1.5-next.1 + - @backstage/plugin-catalog-backend-module-unprocessed@0.3.5-next.1 + - @backstage/plugin-devtools-backend@0.2.5-next.1 + - @backstage/plugin-entity-feedback-backend@0.2.5-next.1 + - @backstage/plugin-jenkins-backend@0.3.2-next.1 + - @backstage/plugin-lighthouse-backend@0.3.5-next.1 + - @backstage/plugin-linguist-backend@0.5.5-next.1 + - @backstage/plugin-nomad-backend@0.1.10-next.1 + - @backstage/plugin-permission-backend@0.5.31-next.1 + - @backstage/plugin-permission-backend-module-allow-all-policy@0.1.5-next.1 + - @backstage/plugin-permission-common@0.7.10 + - @backstage/plugin-permission-node@0.7.19-next.1 + - @backstage/plugin-playlist-backend@0.3.12-next.1 + - @backstage/plugin-proxy-backend@0.4.6-next.1 + - @backstage/plugin-scaffolder-backend@1.19.2-next.1 + - @backstage/plugin-search-backend@1.4.8-next.1 + - @backstage/plugin-search-backend-module-catalog@0.1.12-next.1 + - @backstage/plugin-search-backend-module-explore@0.1.12-next.1 + - @backstage/plugin-search-backend-module-techdocs@0.1.12-next.1 + - @backstage/plugin-search-backend-node@1.2.12-next.1 + - @backstage/plugin-sonarqube-backend@0.2.10-next.1 + - @backstage/plugin-techdocs-backend@1.9.1-next.1 + - @backstage/plugin-todo-backend@0.3.6-next.1 + ## 0.0.18-next.0 ### Patch Changes diff --git a/packages/backend-next/package.json b/packages/backend-next/package.json index 2d53421dfc..fd7845252d 100644 --- a/packages/backend-next/package.json +++ b/packages/backend-next/package.json @@ -1,6 +1,6 @@ { "name": "example-backend-next", - "version": "0.0.18-next.0", + "version": "0.0.18-next.1", "main": "dist/index.cjs.js", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/packages/backend-openapi-utils/CHANGELOG.md b/packages/backend-openapi-utils/CHANGELOG.md index 39c12d507f..6df79cdcd8 100644 --- a/packages/backend-openapi-utils/CHANGELOG.md +++ b/packages/backend-openapi-utils/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/backend-openapi-utils +## 0.1.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + ## 0.1.1-next.0 ### Patch Changes diff --git a/packages/backend-openapi-utils/package.json b/packages/backend-openapi-utils/package.json index e3d57c8f27..d97638c05f 100644 --- a/packages/backend-openapi-utils/package.json +++ b/packages/backend-openapi-utils/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/backend-openapi-utils", "description": "OpenAPI typescript support.", - "version": "0.1.1-next.0", + "version": "0.1.1-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/packages/backend-plugin-api/CHANGELOG.md b/packages/backend-plugin-api/CHANGELOG.md index 98d025ca68..9c3f730912 100644 --- a/packages/backend-plugin-api/CHANGELOG.md +++ b/packages/backend-plugin-api/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/backend-plugin-api +## 0.6.8-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-tasks@0.5.13-next.1 + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.2-next.1 + - @backstage/plugin-permission-common@0.7.10 + ## 0.6.8-next.0 ### Patch Changes diff --git a/packages/backend-plugin-api/alpha-api-report.md b/packages/backend-plugin-api/api-report-alpha.md similarity index 100% rename from packages/backend-plugin-api/alpha-api-report.md rename to packages/backend-plugin-api/api-report-alpha.md diff --git a/packages/backend-plugin-api/package.json b/packages/backend-plugin-api/package.json index 81a4694934..bf7cee4e1b 100644 --- a/packages/backend-plugin-api/package.json +++ b/packages/backend-plugin-api/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/backend-plugin-api", "description": "Core API used by Backstage backend plugins", - "version": "0.6.8-next.0", + "version": "0.6.8-next.1", "main": "src/index.ts", "types": "src/index.ts", "publishConfig": { diff --git a/packages/backend-plugin-manager/CHANGELOG.md b/packages/backend-plugin-manager/CHANGELOG.md index f826362f2d..e4481c8bac 100644 --- a/packages/backend-plugin-manager/CHANGELOG.md +++ b/packages/backend-plugin-manager/CHANGELOG.md @@ -1,5 +1,28 @@ # @backstage/backend-plugin-manager +## 0.0.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.15.1-next.1 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/backend-tasks@0.5.13-next.1 + - @backstage/cli-common@0.1.13 + - @backstage/cli-node@0.2.0 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.2-next.1 + - @backstage/plugin-events-backend@0.2.17-next.1 + - @backstage/plugin-events-node@0.2.17-next.1 + - @backstage/plugin-permission-common@0.7.10 + - @backstage/plugin-permission-node@0.7.19-next.1 + - @backstage/plugin-scaffolder-node@0.2.9-next.1 + - @backstage/plugin-search-backend-node@1.2.12-next.1 + - @backstage/plugin-search-common@1.2.8 + ## 0.0.4-next.0 ### Patch Changes diff --git a/packages/backend-plugin-manager/package.json b/packages/backend-plugin-manager/package.json index 643a94f3cb..7b3a57cc7b 100644 --- a/packages/backend-plugin-manager/package.json +++ b/packages/backend-plugin-manager/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/backend-plugin-manager", "description": "Backstage plugin management backend", - "version": "0.0.4-next.0", + "version": "0.0.4-next.1", "private": true, "main": "src/index.ts", "types": "src/index.ts", diff --git a/packages/backend-plugin-manager/src/loader/CommonJSModuleLoader.ts b/packages/backend-plugin-manager/src/loader/CommonJSModuleLoader.ts index 06a08117cd..66af367cea 100644 --- a/packages/backend-plugin-manager/src/loader/CommonJSModuleLoader.ts +++ b/packages/backend-plugin-manager/src/loader/CommonJSModuleLoader.ts @@ -49,6 +49,6 @@ export class CommonJSModuleLoader implements ModuleLoader { } async load(packagePath: string): Promise { - return await import(/* webpackIgnore: true */ packagePath); + return await require(/* webpackIgnore: true */ packagePath); } } diff --git a/packages/backend-plugin-manager/src/manager/plugin-manager.test.ts b/packages/backend-plugin-manager/src/manager/plugin-manager.test.ts index 4aabd2c072..5b1e1caf4d 100644 --- a/packages/backend-plugin-manager/src/manager/plugin-manager.test.ts +++ b/packages/backend-plugin-manager/src/manager/plugin-manager.test.ts @@ -113,6 +113,94 @@ describe('backend-plugin-manager', () => { >([]); }, }, + { + name: 'should successfully load a new backend plugin by the default BackendFeature', + packageManifest: { + name: 'backend-dynamic-plugin-test', + version: '0.0.0', + backstage: { + role: 'backend-plugin', + }, + main: 'dist/index.cjs.js', + }, + indexFile: { + retativePath: ['dist', 'index.cjs.js'], + content: `const alpha = { $$type: '@backstage/BackendFeature' }; exports["default"] = alpha;`, + }, + expectedLogs(location) { + return { + infos: [ + { + message: `loaded dynamic backend plugin 'backend-dynamic-plugin-test' from '${location}'`, + }, + ], + }; + }, + checkLoadedPlugins(plugins) { + expect(plugins).toMatchObject([ + { + name: 'backend-dynamic-plugin-test', + version: '0.0.0', + role: 'backend-plugin', + platform: 'node', + installer: { + kind: 'new', + }, + }, + ]); + const installer: NewBackendPluginInstaller = ( + plugins[0] as BackendDynamicPlugin + ).installer as NewBackendPluginInstaller; + expect(installer.install()).toEqual< + BackendFeature | BackendFeature[] + >({ $$type: '@backstage/BackendFeature' }); + }, + }, + { + name: 'should successfully load a new backend plugin by the default BackendFeatureFactory', + packageManifest: { + name: 'backend-dynamic-plugin-test', + version: '0.0.0', + backstage: { + role: 'backend-plugin', + }, + main: 'dist/index.cjs.js', + }, + indexFile: { + retativePath: ['dist', 'index.cjs.js'], + content: `const alpha = () => { return { $$type: '@backstage/BackendFeature' } }; + alpha.$$type = '@backstage/BackendFeatureFactory'; + exports["default"] = alpha;`, + }, + expectedLogs(location) { + return { + infos: [ + { + message: `loaded dynamic backend plugin 'backend-dynamic-plugin-test' from '${location}'`, + }, + ], + }; + }, + checkLoadedPlugins(plugins) { + expect(plugins).toMatchObject([ + { + name: 'backend-dynamic-plugin-test', + version: '0.0.0', + role: 'backend-plugin', + platform: 'node', + installer: { + kind: 'new', + }, + }, + ]); + const installer: NewBackendPluginInstaller = ( + plugins[0] as BackendDynamicPlugin + ).installer as NewBackendPluginInstaller; + expect(installer.install()).toEqual< + BackendFeature | BackendFeature[] + >({ $$type: '@backstage/BackendFeature' }); + }, + }, { name: 'should successfully load a new backend plugin module', packageManifest: { @@ -221,7 +309,7 @@ describe('backend-plugin-manager', () => { return { errors: [ { - message: `dynamic backend plugin 'backend-dynamic-plugin-test' could not be loaded from '${location}': the module should export a 'const dynamicPluginInstaller: BackendDynamicPluginInstaller' field.`, + message: `dynamic backend plugin 'backend-dynamic-plugin-test' could not be loaded from '${location}': the module should either export a 'BackendFeature' or 'BackendFeatureFactory' as default export, or export a 'const dynamicPluginInstaller: BackendDynamicPluginInstaller' field as dynamic loading entrypoint.`, }, ], }; @@ -249,7 +337,7 @@ describe('backend-plugin-manager', () => { return { errors: [ { - message: `dynamic backend plugin 'backend-dynamic-plugin-test' could not be loaded from '${location}': the module should export a 'const dynamicPluginInstaller: BackendDynamicPluginInstaller' field.`, + message: `dynamic backend plugin 'backend-dynamic-plugin-test' could not be loaded from '${location}': the module should either export a 'BackendFeature' or 'BackendFeatureFactory' as default export, or export a 'const dynamicPluginInstaller: BackendDynamicPluginInstaller' field as dynamic loading entrypoint.`, }, ], }; @@ -378,7 +466,7 @@ describe('backend-plugin-manager', () => { logger, async bootstrap(_: string, __: string[]): Promise {}, load: async (packagePath: string) => - await import(/* webpackIgnore: true */ packagePath), + await require(/* webpackIgnore: true */ packagePath), }); const loadedPlugins: DynamicPlugin[] = await pluginManager.loadPlugins(); diff --git a/packages/backend-plugin-manager/src/manager/plugin-manager.ts b/packages/backend-plugin-manager/src/manager/plugin-manager.ts index ad76c7529e..5a5caa0a89 100644 --- a/packages/backend-plugin-manager/src/manager/plugin-manager.ts +++ b/packages/backend-plugin-manager/src/manager/plugin-manager.ts @@ -140,12 +140,25 @@ export class PluginManager implements BackendPluginProvider { `${plugin.location}/${plugin.manifest.main}`, ); try { - const { dynamicPluginInstaller } = await this.moduleLoader.load( - packagePath, - ); + const pluginModule = await this.moduleLoader.load(packagePath); + + let dynamicPluginInstaller; + if (isBackendFeature(pluginModule.default)) { + dynamicPluginInstaller = { + kind: 'new', + install: () => pluginModule.default, + }; + } else if (isBackendFeatureFactory(pluginModule.default)) { + dynamicPluginInstaller = { + kind: 'new', + install: pluginModule.default, + }; + } else { + dynamicPluginInstaller = pluginModule.dynamicPluginInstaller; + } if (!isBackendDynamicPluginInstaller(dynamicPluginInstaller)) { this.logger.error( - `dynamic backend plugin '${plugin.manifest.name}' could not be loaded from '${plugin.location}': the module should export a 'const dynamicPluginInstaller: BackendDynamicPluginInstaller' field.`, + `dynamic backend plugin '${plugin.manifest.name}' could not be loaded from '${plugin.location}': the module should either export a 'BackendFeature' or 'BackendFeatureFactory' as default export, or export a 'const dynamicPluginInstaller: BackendDynamicPluginInstaller' field as dynamic loading entrypoint.`, ); return undefined; } @@ -263,3 +276,21 @@ export const dynamicPluginsFeatureDiscoveryServiceFactory = return new DynamicPluginsEnabledFeatureDiscoveryService(dynamicPlugins); }, }); + +function isBackendFeature(value: unknown): value is BackendFeature { + return ( + !!value && + typeof value === 'object' && + (value as BackendFeature).$$type === '@backstage/BackendFeature' + ); +} + +function isBackendFeatureFactory( + value: unknown, +): value is () => BackendFeature { + return ( + !!value && + typeof value === 'function' && + (value as any).$$type === '@backstage/BackendFeatureFactory' + ); +} diff --git a/packages/backend-plugin-manager/src/scanner/plugin-scanner-watcher.test.ts b/packages/backend-plugin-manager/src/scanner/plugin-scanner-watcher.test.ts index e346bf749f..7b795aee4f 100644 --- a/packages/backend-plugin-manager/src/scanner/plugin-scanner-watcher.test.ts +++ b/packages/backend-plugin-manager/src/scanner/plugin-scanner-watcher.test.ts @@ -229,6 +229,7 @@ describe('plugin-scanner', () => { logger.logs = {}; const debug = jest.spyOn(logger, 'debug'); + const info = jest.spyOn(logger, 'info'); // Check that not all file changes trigger a new scan of plugins await mkdir( @@ -240,8 +241,11 @@ describe('plugin-scanner', () => { ), ); await waitForExpect(() => { - expect(debug).toHaveBeenCalledTimes(1); + expect(debug).toHaveBeenCalled(); }); + expect(info).toHaveBeenCalledTimes(0); + debug.mockClear(); + await writeFile( join( backstageRootDirectory, @@ -252,8 +256,11 @@ describe('plugin-scanner', () => { 'content', ); await waitForExpect(() => { - expect(debug).toHaveBeenCalledTimes(2); + expect(debug).toHaveBeenCalled(); }); + expect(info).toHaveBeenCalledTimes(0); + debug.mockClear(); + await rm( join( backstageRootDirectory, @@ -263,8 +270,11 @@ describe('plugin-scanner', () => { ), ); await waitForExpect(() => { - expect(debug).toHaveBeenCalledTimes(3); + expect(debug).toHaveBeenCalled(); }); + expect(info).toHaveBeenCalledTimes(0); + debug.mockClear(); + await rm( join( backstageRootDirectory, @@ -275,8 +285,10 @@ describe('plugin-scanner', () => { { recursive: true }, ); await waitForExpect(() => { - expect(debug).toHaveBeenCalledTimes(4); + expect(debug).toHaveBeenCalled(); }); + expect(info).toHaveBeenCalledTimes(0); + debug.mockClear(); const onWindows = path.sep === '\\'; // Order of events is not fixed on Windows. diff --git a/packages/backend-tasks/CHANGELOG.md b/packages/backend-tasks/CHANGELOG.md index 8635aa3e03..c25edab02d 100644 --- a/packages/backend-tasks/CHANGELOG.md +++ b/packages/backend-tasks/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/backend-tasks +## 0.5.13-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + ## 0.5.13-next.0 ### Patch Changes diff --git a/packages/backend-tasks/package.json b/packages/backend-tasks/package.json index cd71be4190..f577380a7c 100644 --- a/packages/backend-tasks/package.json +++ b/packages/backend-tasks/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/backend-tasks", "description": "Common distributed task management library for Backstage backends", - "version": "0.5.13-next.0", + "version": "0.5.13-next.1", "main": "src/index.ts", "types": "src/index.ts", "publishConfig": { diff --git a/packages/backend-test-utils/CHANGELOG.md b/packages/backend-test-utils/CHANGELOG.md index 4931fd8b2f..cd9428b8a0 100644 --- a/packages/backend-test-utils/CHANGELOG.md +++ b/packages/backend-test-utils/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/backend-test-utils +## 0.2.9-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-app-api@0.5.9-next.1 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.2-next.1 + ## 0.2.9-next.0 ### Patch Changes diff --git a/packages/backend-test-utils/package.json b/packages/backend-test-utils/package.json index f449be08ed..390bb99954 100644 --- a/packages/backend-test-utils/package.json +++ b/packages/backend-test-utils/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/backend-test-utils", "description": "Test helpers library for Backstage backends", - "version": "0.2.9-next.0", + "version": "0.2.9-next.1", "main": "src/index.ts", "types": "src/index.ts", "publishConfig": { diff --git a/packages/backend-test-utils/src/next/wiring/TestBackend.test.ts b/packages/backend-test-utils/src/next/wiring/TestBackend.test.ts index b127712927..d71c774dd9 100644 --- a/packages/backend-test-utils/src/next/wiring/TestBackend.test.ts +++ b/packages/backend-test-utils/src/next/wiring/TestBackend.test.ts @@ -33,8 +33,8 @@ beforeAll(async () => { await startTestBackend({ features: [ createBackendModule({ - moduleId: 'test.module', pluginId: 'test', + moduleId: 'test-module', register(env) { env.registerInit({ deps: { lifecycle: coreServices.lifecycle }, @@ -128,8 +128,8 @@ describe('TestBackend', () => { }); const testModule = createBackendModule({ - moduleId: 'test.module', pluginId: 'test', + moduleId: 'test-module', register(env) { env.registerInit({ deps: { @@ -153,8 +153,8 @@ describe('TestBackend', () => { const shutdownSpy = jest.fn(); const testModule = createBackendModule({ - moduleId: 'test.module', pluginId: 'test', + moduleId: 'test-module', register(env) { env.registerInit({ deps: { diff --git a/packages/backend-test-utils/src/next/wiring/TestBackend.ts b/packages/backend-test-utils/src/next/wiring/TestBackend.ts index 21ae6159c9..14e8920390 100644 --- a/packages/backend-test-utils/src/next/wiring/TestBackend.ts +++ b/packages/backend-test-utils/src/next/wiring/TestBackend.ts @@ -156,7 +156,7 @@ function createExtensionPointTestModules( modules.push( createBackendModule({ pluginId, - moduleId: 'testExtensionPointRegistration', + moduleId: 'test-extension-point-registration', register(reg) { for (const id of pluginExtensionPointIds) { const tuple = extensionPointMap.get(id)!; diff --git a/packages/backend/CHANGELOG.md b/packages/backend/CHANGELOG.md index 11ddf672a8..f3a446b211 100644 --- a/packages/backend/CHANGELOG.md +++ b/packages/backend/CHANGELOG.md @@ -1,5 +1,63 @@ # example-backend +## 0.2.90-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-backend@0.20.1-next.1 + - @backstage/plugin-catalog-backend@1.15.1-next.1 + - @backstage/catalog-client@1.5.0-next.0 + - @backstage/plugin-azure-devops-backend@0.5.0-next.1 + - @backstage/plugin-kubernetes-backend@0.14.0-next.1 + - @backstage/integration@1.8.0-next.1 + - @backstage/plugin-azure-sites-backend@0.1.18-next.1 + - @backstage/backend-common@0.20.0-next.1 + - example-app@0.2.90-next.2 + - @backstage/backend-tasks@0.5.13-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/plugin-adr-backend@0.4.5-next.1 + - @backstage/plugin-app-backend@0.3.56-next.1 + - @backstage/plugin-auth-node@0.4.2-next.1 + - @backstage/plugin-badges-backend@0.3.5-next.1 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.1.5-next.1 + - @backstage/plugin-catalog-backend-module-unprocessed@0.3.5-next.1 + - @backstage/plugin-catalog-node@1.5.1-next.1 + - @backstage/plugin-code-coverage-backend@0.2.22-next.1 + - @backstage/plugin-devtools-backend@0.2.5-next.1 + - @backstage/plugin-entity-feedback-backend@0.2.5-next.1 + - @backstage/plugin-events-backend@0.2.17-next.1 + - @backstage/plugin-events-node@0.2.17-next.1 + - @backstage/plugin-explore-backend@0.0.18-next.1 + - @backstage/plugin-jenkins-backend@0.3.2-next.1 + - @backstage/plugin-kafka-backend@0.3.6-next.1 + - @backstage/plugin-lighthouse-backend@0.3.5-next.1 + - @backstage/plugin-linguist-backend@0.5.5-next.1 + - @backstage/plugin-nomad-backend@0.1.10-next.1 + - @backstage/plugin-permission-backend@0.5.31-next.1 + - @backstage/plugin-permission-common@0.7.10 + - @backstage/plugin-permission-node@0.7.19-next.1 + - @backstage/plugin-playlist-backend@0.3.12-next.1 + - @backstage/plugin-proxy-backend@0.4.6-next.1 + - @backstage/plugin-rollbar-backend@0.1.53-next.1 + - @backstage/plugin-scaffolder-backend@1.19.2-next.1 + - @backstage/plugin-scaffolder-backend-module-confluence-to-markdown@0.2.9-next.1 + - @backstage/plugin-scaffolder-backend-module-rails@0.4.25-next.1 + - @backstage/plugin-search-backend@1.4.8-next.1 + - @backstage/plugin-search-backend-module-catalog@0.1.12-next.1 + - @backstage/plugin-search-backend-module-elasticsearch@1.3.11-next.1 + - @backstage/plugin-search-backend-module-explore@0.1.12-next.1 + - @backstage/plugin-search-backend-module-pg@0.5.17-next.1 + - @backstage/plugin-search-backend-module-techdocs@0.1.12-next.1 + - @backstage/plugin-search-backend-node@1.2.12-next.1 + - @backstage/plugin-search-common@1.2.8 + - @backstage/plugin-tech-insights-backend@0.5.22-next.1 + - @backstage/plugin-tech-insights-backend-module-jsonfc@0.1.40-next.1 + - @backstage/plugin-tech-insights-node@0.4.14-next.1 + - @backstage/plugin-techdocs-backend@1.9.1-next.1 + - @backstage/plugin-todo-backend@0.3.6-next.1 + ## 0.2.90-next.0 ### Patch Changes diff --git a/packages/backend/package.json b/packages/backend/package.json index fc964dfdbc..f57897f163 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -1,6 +1,6 @@ { "name": "example-backend", - "version": "0.2.90-next.0", + "version": "0.2.90-next.1", "main": "dist/index.cjs.js", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/packages/catalog-client/CHANGELOG.md b/packages/catalog-client/CHANGELOG.md index 7a2618f0e0..b7517e5734 100644 --- a/packages/catalog-client/CHANGELOG.md +++ b/packages/catalog-client/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/catalog-client +## 1.5.0-next.0 + +### Minor Changes + +- 38340678c3: The internals of `CatalogClient` are now auto-generated using the `backstage-repo-tools schema openapi generate-client` command. + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + ## 1.4.6 ### Patch Changes diff --git a/packages/catalog-client/package.json b/packages/catalog-client/package.json index 02c30ee19b..2be5fbea43 100644 --- a/packages/catalog-client/package.json +++ b/packages/catalog-client/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/catalog-client", "description": "An isomorphic client for the catalog backend", - "version": "1.4.6", + "version": "1.5.0-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -35,7 +35,8 @@ "dependencies": { "@backstage/catalog-model": "workspace:^", "@backstage/errors": "workspace:^", - "cross-fetch": "^4.0.0" + "cross-fetch": "^4.0.0", + "uri-template": "^2.0.0" }, "devDependencies": { "@backstage/cli": "workspace:^", diff --git a/packages/catalog-client/src/CatalogClient.test.ts b/packages/catalog-client/src/CatalogClient.test.ts index f5c221569d..4892017032 100644 --- a/packages/catalog-client/src/CatalogClient.test.ts +++ b/packages/catalog-client/src/CatalogClient.test.ts @@ -86,7 +86,7 @@ describe('CatalogClient', () => { server.use( rest.get(`${mockBaseUrl}/entities`, (req, res, ctx) => { - expect(req.url.search).toBe( + expect(decodeURIComponent(req.url.search)).toBe( '?filter=a=1,b=2,b=3,%C3%B6=%3D&filter=a=2&filter=c', ); return res(ctx.json([])); @@ -120,7 +120,9 @@ describe('CatalogClient', () => { server.use( rest.get(`${mockBaseUrl}/entities`, (req, res, ctx) => { - expect(req.url.search).toBe('?filter=a=1,b=2,b=3,%C3%B6=%3D,c'); + expect(decodeURIComponent(req.url.search)).toBe( + '?filter=a=1,b=2,b=3,%C3%B6=%3D,c', + ); return res(ctx.json([])); }), ); @@ -145,7 +147,7 @@ describe('CatalogClient', () => { server.use( rest.get(`${mockBaseUrl}/entities`, (req, res, ctx) => { - expect(req.url.search).toBe('?fields=a.b,%C3%B6'); + expect(decodeURIComponent(req.url.search)).toBe('?fields=a.b,%C3%B6'); return res(ctx.json([])); }), ); @@ -185,7 +187,7 @@ describe('CatalogClient', () => { server.use( rest.get(`${mockBaseUrl}/entities`, (req, res, ctx) => { - expect(req.url.search).toBe('?offset=1&limit=2&after=%3D'); + expect(req.url.search).toBe('?limit=2&offset=1&after=%3D'); return res(ctx.json([])); }), ); @@ -203,7 +205,7 @@ describe('CatalogClient', () => { server.use( rest.get(`${mockBaseUrl}/entities`, (req, res, ctx) => { - expect(req.url.search).toBe( + expect(decodeURIComponent(req.url.search)).toBe( '?order=asc:kind&order=desc:metadata.name', ); return res(ctx.json([])); @@ -326,9 +328,9 @@ describe('CatalogClient', () => { expect(response).toEqual({ items: [], totalItems: 0 }); expect(mockedEndpoint).toHaveBeenCalledTimes(1); - expect(mockedEndpoint.mock.calls[0][0].url.search).toBe( - '?filter=a=1,b=2,b=3,%C3%B6=%3D&filter=a=2&filter=c', - ); + expect( + decodeURIComponent(mockedEndpoint.mock.calls[0][0].url.search), + ).toBe('?filter=a=1,b=2,b=3,%C3%B6=%3D&filter=a=2&filter=c'); }); it('should send query params correctly on initial request', async () => { @@ -351,8 +353,10 @@ describe('CatalogClient', () => { { field: 'metadata.uid', order: 'desc' }, ], }); - expect(mockedEndpoint.mock.calls[0][0].url.search).toBe( - '?limit=100&orderField=metadata.name,asc&orderField=metadata.uid,desc&fields=a,b&fullTextFilterTerm=query', + expect( + decodeURIComponent(mockedEndpoint.mock.calls[0][0].url.search), + ).toBe( + '?fields=a,b&limit=100&orderField=metadata.name%2Casc&orderField=metadata.uid%2Cdesc&fullTextFilterTerm=query', ); }); @@ -375,7 +379,7 @@ describe('CatalogClient', () => { cursor: 'cursor', }); expect(mockedEndpoint.mock.calls[0][0].url.search).toBe( - '?cursor=cursor&limit=100&fields=a,b', + '?fields=a,b&limit=100&cursor=cursor', ); }); diff --git a/packages/catalog-client/src/CatalogClient.ts b/packages/catalog-client/src/CatalogClient.ts index c388519e05..511bf45c51 100644 --- a/packages/catalog-client/src/CatalogClient.ts +++ b/packages/catalog-client/src/CatalogClient.ts @@ -22,7 +22,6 @@ import { stringifyLocationRef, } from '@backstage/catalog-model'; import { ResponseError } from '@backstage/errors'; -import crossFetch from 'cross-fetch'; import { CATALOG_FILTER_EXISTS, AddLocationRequest, @@ -40,12 +39,11 @@ import { GetEntitiesByRefsRequest, GetEntitiesByRefsResponse, QueryEntitiesRequest, - QueryEntitiesResponse, EntityFilterQuery, + QueryEntitiesResponse, } from './types/api'; -import { DiscoveryApi } from './types/discovery'; -import { FetchApi } from './types/fetch'; import { isQueryEntitiesInitialRequest } from './utils'; +import { DefaultApiClient } from './generated'; /** * A frontend and backend compatible client for communicating with the Backstage @@ -54,15 +52,13 @@ import { isQueryEntitiesInitialRequest } from './utils'; * @public */ export class CatalogClient implements CatalogApi { - private readonly discoveryApi: DiscoveryApi; - private readonly fetchApi: FetchApi; + private readonly apiClient: DefaultApiClient; constructor(options: { discoveryApi: { getBaseUrl(pluginId: string): Promise }; fetchApi?: { fetch: typeof fetch }; }) { - this.discoveryApi = options.discoveryApi; - this.fetchApi = options.fetchApi || { fetch: crossFetch }; + this.apiClient = new DefaultApiClient(options); } /** @@ -72,13 +68,11 @@ export class CatalogClient implements CatalogApi { request: GetEntityAncestorsRequest, options?: CatalogRequestOptions, ): Promise { - const { kind, namespace, name } = parseEntityRef(request.entityRef); return await this.requestRequired( - 'GET', - `/entities/by-name/${encodeURIComponent(kind)}/${encodeURIComponent( - namespace, - )}/${encodeURIComponent(name)}/ancestry`, - options, + await this.apiClient.getEntityAncestryByName( + { path: parseEntityRef(request.entityRef) }, + options, + ), ); } @@ -90,9 +84,7 @@ export class CatalogClient implements CatalogApi { options?: CatalogRequestOptions, ): Promise { return await this.requestOptional( - 'GET', - `/locations/${encodeURIComponent(id)}`, - options, + await this.apiClient.getLocation({ path: { id } }, options), ); } @@ -111,17 +103,12 @@ export class CatalogClient implements CatalogApi { limit, after, } = request ?? {}; - const params = this.getParams(filter); - - if (fields.length) { - params.push(`fields=${fields.map(encodeURIComponent).join(',')}`); - } - + const encodedOrder = []; if (order) { for (const directive of [order].flat()) { if (directive) { - params.push( - `order=${encodeURIComponent(directive.order)}:${encodeURIComponent( + encodedOrder.push( + `${encodeURIComponent(directive.order)}:${encodeURIComponent( directive.field, )}`, ); @@ -129,21 +116,20 @@ export class CatalogClient implements CatalogApi { } } - if (offset !== undefined) { - params.push(`offset=${offset}`); - } - if (limit !== undefined) { - params.push(`limit=${limit}`); - } - if (after !== undefined) { - params.push(`after=${encodeURIComponent(after)}`); - } - - const query = params.length ? `?${params.join('&')}` : ''; const entities: Entity[] = await this.requestRequired( - 'GET', - `/entities${query}`, - options, + await this.apiClient.getEntities( + { + query: { + fields: fields.map(encodeURIComponent), + limit, + filter: this.getFilterValue(filter), + offset, + after, + order: order ? encodedOrder : undefined, + }, + }, + options, + ), ); const refCompare = (a: Entity, b: Entity) => { @@ -178,22 +164,12 @@ export class CatalogClient implements CatalogApi { request: GetEntitiesByRefsRequest, options?: CatalogRequestOptions, ): Promise { - const body: any = { entityRefs: request.entityRefs }; - if (request.fields?.length) { - body.fields = request.fields; - } - - const baseUrl = await this.discoveryApi.getBaseUrl('catalog'); - const url = `${baseUrl}/entities/by-refs`; - - const response = await this.fetchApi.fetch(url, { - headers: { - 'Content-Type': 'application/json', - ...(options?.token && { Authorization: `Bearer ${options?.token}` }), + const response = await this.apiClient.getEntitiesByRefs( + { + body: request, }, - method: 'POST', - body: JSON.stringify(body), - }); + options, + ); if (!response.ok) { throw await ResponseError.fromResponse(response); @@ -212,8 +188,10 @@ export class CatalogClient implements CatalogApi { async queryEntities( request: QueryEntitiesRequest = {}, options?: CatalogRequestOptions, - ) { - const params: string[] = []; + ): Promise { + const params: Partial< + Parameters[0]['query'] + > = {}; if (isQueryEntitiesInitialRequest(request)) { const { @@ -223,45 +201,42 @@ export class CatalogClient implements CatalogApi { orderFields, fullTextFilter, } = request; - params.push(...this.getParams(filter)); + params.filter = this.getFilterValue(filter); if (limit !== undefined) { - params.push(`limit=${limit}`); + params.limit = limit; } if (orderFields !== undefined) { - (Array.isArray(orderFields) ? orderFields : [orderFields]).forEach( - ({ field, order }) => params.push(`orderField=${field},${order}`), - ); + params.orderField = ( + Array.isArray(orderFields) ? orderFields : [orderFields] + ).map(({ field, order }) => encodeURIComponent(`${field},${order}`)); } if (fields.length) { - params.push(`fields=${fields.map(encodeURIComponent).join(',')}`); + params.fields = fields.map(encodeURIComponent); } const normalizedFullTextFilterTerm = fullTextFilter?.term?.trim(); if (normalizedFullTextFilterTerm) { - params.push(`fullTextFilterTerm=${normalizedFullTextFilterTerm}`); + params.fullTextFilterTerm = normalizedFullTextFilterTerm; } if (fullTextFilter?.fields?.length) { - params.push(`fullTextFilterFields=${fullTextFilter.fields.join(',')}`); + params.fullTextFilterFields = fullTextFilter.fields; } } else { const { fields = [], limit, cursor } = request; - params.push(`cursor=${cursor}`); + params.cursor = cursor; if (limit !== undefined) { - params.push(`limit=${limit}`); + params.limit = limit; } if (fields.length) { - params.push(`fields=${fields.map(encodeURIComponent).join(',')}`); + params.fields = fields.map(encodeURIComponent); } } - const query = params.length ? `?${params.join('&')}` : ''; - return this.requestRequired( - 'GET', - `/entities/by-query${query}`, - options, - ); + return this.apiClient + .getEntitiesByQuery({ query: params }, options) + .then(r => r.json()); } /** @@ -271,13 +246,13 @@ export class CatalogClient implements CatalogApi { entityRef: string | CompoundEntityRef, options?: CatalogRequestOptions, ): Promise { - const { kind, namespace, name } = parseEntityRef(entityRef); return this.requestOptional( - 'GET', - `/entities/by-name/${encodeURIComponent(kind)}/${encodeURIComponent( - namespace, - )}/${encodeURIComponent(name)}`, - options, + await this.apiClient.getEntityByName( + { + path: parseEntityRef(entityRef), + }, + options, + ), ); } @@ -294,11 +269,10 @@ export class CatalogClient implements CatalogApi { ): Promise { const { kind, namespace = 'default', name } = compoundName; return this.requestOptional( - 'GET', - `/entities/by-name/${encodeURIComponent(kind)}/${encodeURIComponent( - namespace, - )}/${encodeURIComponent(name)}`, - options, + await this.apiClient.getEntityByName( + { path: { kind, namespace, name } }, + options, + ), ); } @@ -306,16 +280,9 @@ export class CatalogClient implements CatalogApi { * {@inheritdoc CatalogApi.refreshEntity} */ async refreshEntity(entityRef: string, options?: CatalogRequestOptions) { - const response = await this.fetchApi.fetch( - `${await this.discoveryApi.getBaseUrl('catalog')}/refresh`, - { - headers: { - 'Content-Type': 'application/json', - ...(options?.token && { Authorization: `Bearer ${options?.token}` }), - }, - method: 'POST', - body: JSON.stringify({ entityRef }), - }, + const response = await this.apiClient.refreshEntity( + { body: { entityRef } }, + options, ); if (response.status !== 200) { @@ -331,14 +298,14 @@ export class CatalogClient implements CatalogApi { options?: CatalogRequestOptions, ): Promise { const { filter = [], facets } = request; - const params = this.getParams(filter); - - for (const facet of facets) { - params.push(`facet=${encodeURIComponent(facet)}`); - } - - const query = params.length ? `?${params.join('&')}` : ''; - return await this.requestOptional('GET', `/entity-facets${query}`, options); + return await this.requestOptional( + await this.apiClient.getEntityFacets( + { + query: { facet: facets, filter: this.getFilterValue(filter) }, + }, + options, + ), + ); } /** @@ -350,18 +317,12 @@ export class CatalogClient implements CatalogApi { ): Promise { const { type = 'url', target, dryRun } = request; - const response = await this.fetchApi.fetch( - `${await this.discoveryApi.getBaseUrl('catalog')}/locations${ - dryRun ? '?dryRun=true' : '' - }`, + const response = await this.apiClient.createLocation( { - headers: { - 'Content-Type': 'application/json', - ...(options?.token && { Authorization: `Bearer ${options?.token}` }), - }, - method: 'POST', - body: JSON.stringify({ type, target }), + body: { type, target }, + query: { dryRun: dryRun ? 'true' : undefined }, }, + options, ); if (response.status !== 201) { @@ -389,9 +350,7 @@ export class CatalogClient implements CatalogApi { options?: CatalogRequestOptions, ): Promise { const all: { data: Location }[] = await this.requestRequired( - 'GET', - '/locations', - options, + await this.apiClient.getLocation({ path: { id: locationRef } }, options), ); return all .map(r => r.data) @@ -406,9 +365,7 @@ export class CatalogClient implements CatalogApi { options?: CatalogRequestOptions, ): Promise { await this.requestIgnored( - 'DELETE', - `/locations/${encodeURIComponent(id)}`, - options, + await this.apiClient.deleteLocation({ path: { id } }, options), ); } @@ -420,9 +377,7 @@ export class CatalogClient implements CatalogApi { options?: CatalogRequestOptions, ): Promise { await this.requestIgnored( - 'DELETE', - `/entities/by-uid/${encodeURIComponent(uid)}`, - options, + await this.apiClient.deleteEntityByUid({ path: { uid } }, options), ); } @@ -434,16 +389,9 @@ export class CatalogClient implements CatalogApi { locationRef: string, options?: CatalogRequestOptions, ): Promise { - const response = await this.fetchApi.fetch( - `${await this.discoveryApi.getBaseUrl('catalog')}/validate-entity`, - { - headers: { - 'Content-Type': 'application/json', - ...(options?.token && { Authorization: `Bearer ${options?.token}` }), - }, - method: 'POST', - body: JSON.stringify({ entity, location: locationRef }), - }, + const response = await this.apiClient.validateEntity( + { body: { entity, location: locationRef } }, + options, ); if (response.ok) { @@ -456,7 +404,7 @@ export class CatalogClient implements CatalogApi { throw await ResponseError.fromResponse(response); } - const { errors = [] } = await response.json(); + const { errors = [] } = (await response.json()) as any; return { valid: false, @@ -468,33 +416,13 @@ export class CatalogClient implements CatalogApi { // Private methods // - private async requestIgnored( - method: string, - path: string, - options?: CatalogRequestOptions, - ): Promise { - const url = `${await this.discoveryApi.getBaseUrl('catalog')}${path}`; - const headers: Record = options?.token - ? { Authorization: `Bearer ${options.token}` } - : {}; - const response = await this.fetchApi.fetch(url, { method, headers }); - + private async requestIgnored(response: Response): Promise { if (!response.ok) { throw await ResponseError.fromResponse(response); } } - private async requestRequired( - method: string, - path: string, - options?: CatalogRequestOptions, - ): Promise { - const url = `${await this.discoveryApi.getBaseUrl('catalog')}${path}`; - const headers: Record = options?.token - ? { Authorization: `Bearer ${options.token}` } - : {}; - const response = await this.fetchApi.fetch(url, { method, headers }); - + private async requestRequired(response: Response): Promise { if (!response.ok) { throw await ResponseError.fromResponse(response); } @@ -502,17 +430,7 @@ export class CatalogClient implements CatalogApi { return response.json(); } - private async requestOptional( - method: string, - path: string, - options?: CatalogRequestOptions, - ): Promise { - const url = `${await this.discoveryApi.getBaseUrl('catalog')}${path}`; - const headers: Record = options?.token - ? { Authorization: `Bearer ${options.token}` } - : {}; - const response = await this.fetchApi.fetch(url, { method, headers }); - + private async requestOptional(response: Response): Promise { if (!response.ok) { if (response.status === 404) { return undefined; @@ -523,8 +441,8 @@ export class CatalogClient implements CatalogApi { return await response.json(); } - private getParams(filter: EntityFilterQuery = []) { - const params: string[] = []; + private getFilterValue(filter: EntityFilterQuery = []) { + const filters: string[] = []; // filter param can occur multiple times, for example // /api/catalog/entities?filter=metadata.name=wayback-search,kind=component&filter=metadata.name=www-artist,kind=component' // the "outer array" defined by `filter` occurrences corresponds to "anyOf" filters @@ -544,9 +462,9 @@ export class CatalogClient implements CatalogApi { } if (filterParts.length) { - params.push(`filter=${filterParts.join(',')}`); + filters.push(filterParts.join(',')); } } - return params; + return filters; } } diff --git a/packages/catalog-client/src/generated/apis/DefaultApi.client.ts b/packages/catalog-client/src/generated/apis/DefaultApi.client.ts new file mode 100644 index 0000000000..260f7c9fc8 --- /dev/null +++ b/packages/catalog-client/src/generated/apis/DefaultApi.client.ts @@ -0,0 +1,535 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { DiscoveryApi } from '../types/discovery'; +import { FetchApi } from '../types/fetch'; +import crossFetch from 'cross-fetch'; +import { pluginId } from '../pluginId'; +import * as parser from 'uri-template'; + +import { AnalyzeLocationRequest } from '../models/AnalyzeLocationRequest.model'; +import { AnalyzeLocationResponse } from '../models/AnalyzeLocationResponse.model'; +import { CreateLocation201Response } from '../models/CreateLocation201Response.model'; +import { CreateLocationRequest } from '../models/CreateLocationRequest.model'; +import { EntitiesBatchResponse } from '../models/EntitiesBatchResponse.model'; +import { EntitiesQueryResponse } from '../models/EntitiesQueryResponse.model'; +import { Entity } from '../models/Entity.model'; +import { EntityAncestryResponse } from '../models/EntityAncestryResponse.model'; +import { EntityFacetsResponse } from '../models/EntityFacetsResponse.model'; +import { GetEntitiesByRefsRequest } from '../models/GetEntitiesByRefsRequest.model'; +import { GetLocations200ResponseInner } from '../models/GetLocations200ResponseInner.model'; +import { Location } from '../models/Location.model'; +import { RefreshEntityRequest } from '../models/RefreshEntityRequest.model'; +import { ValidateEntityRequest } from '../models/ValidateEntityRequest.model'; + +type TypedResponse = Omit & { + json: () => Promise; +}; + +/** + * Options you can pass into a request for additional information. + * + * @public + */ +export interface RequestOptions { + token?: string; +} + +/** + * no description + */ +export class DefaultApiClient { + private readonly discoveryApi: DiscoveryApi; + private readonly fetchApi: FetchApi; + + constructor(options: { + discoveryApi: { getBaseUrl(pluginId: string): Promise }; + fetchApi?: { fetch: typeof fetch }; + }) { + this.discoveryApi = options.discoveryApi; + this.fetchApi = options.fetchApi || { fetch: crossFetch }; + } + + /** + * Validate a given location. + * @param analyzeLocationRequest + */ + public async analyzeLocation( + // @ts-ignore + request: { + body: AnalyzeLocationRequest; + }, + options?: RequestOptions, + ): Promise> { + const baseUrl = await this.discoveryApi.getBaseUrl(pluginId); + + const uriTemplate = `/analyze-location`; + + const uri = parser.parse(uriTemplate).expand({}); + + return await this.fetchApi.fetch(`${baseUrl}${uri}`, { + headers: { + 'Content-Type': 'application/json', + ...(options?.token && { Authorization: `Bearer ${options?.token}` }), + }, + method: 'POST', + body: JSON.stringify(request.body), + }); + } + + /** + * Create a location for a given target. + * @param createLocationRequest + * @param dryRun + */ + public async createLocation( + // @ts-ignore + request: { + body: CreateLocationRequest; + query: { + dryRun?: string; + }; + }, + options?: RequestOptions, + ): Promise> { + const baseUrl = await this.discoveryApi.getBaseUrl(pluginId); + + const uriTemplate = `/locations{?dryRun}`; + + const uri = parser.parse(uriTemplate).expand({ + ...request.query, + }); + + return await this.fetchApi.fetch(`${baseUrl}${uri}`, { + headers: { + 'Content-Type': 'application/json', + ...(options?.token && { Authorization: `Bearer ${options?.token}` }), + }, + method: 'POST', + body: JSON.stringify(request.body), + }); + } + + /** + * Delete a single entity by UID. + * @param uid + */ + public async deleteEntityByUid( + // @ts-ignore + request: { + path: { + uid: string; + }; + }, + options?: RequestOptions, + ): Promise> { + const baseUrl = await this.discoveryApi.getBaseUrl(pluginId); + + const uriTemplate = `/entities/by-uid/{uid}`; + + const uri = parser.parse(uriTemplate).expand({ + uid: request.path.uid, + }); + + return await this.fetchApi.fetch(`${baseUrl}${uri}`, { + headers: { + 'Content-Type': 'application/json', + ...(options?.token && { Authorization: `Bearer ${options?.token}` }), + }, + method: 'DELETE', + }); + } + + /** + * Delete a location by id. + * @param id + */ + public async deleteLocation( + // @ts-ignore + request: { + path: { + id: string; + }; + }, + options?: RequestOptions, + ): Promise> { + const baseUrl = await this.discoveryApi.getBaseUrl(pluginId); + + const uriTemplate = `/locations/{id}`; + + const uri = parser.parse(uriTemplate).expand({ + id: request.path.id, + }); + + return await this.fetchApi.fetch(`${baseUrl}${uri}`, { + headers: { + 'Content-Type': 'application/json', + ...(options?.token && { Authorization: `Bearer ${options?.token}` }), + }, + method: 'DELETE', + }); + } + + /** + * Get all entities matching a given filter. + * @param fields Restrict to just these fields in the response. + * @param limit Number of records to return in the response. + * @param filter Filter for just the entities defined by this filter. + * @param offset Number of records to skip in the query page. + * @param after Pointer to the previous page of results. + * @param order + */ + public async getEntities( + // @ts-ignore + request: { + query: { + fields?: Array; + limit?: number; + filter?: Array; + offset?: number; + after?: string; + order?: Array; + }; + }, + options?: RequestOptions, + ): Promise>> { + const baseUrl = await this.discoveryApi.getBaseUrl(pluginId); + + const uriTemplate = `/entities{?fields,limit,filter*,offset,after,order*}`; + + const uri = parser.parse(uriTemplate).expand({ + ...request.query, + }); + + return await this.fetchApi.fetch(`${baseUrl}${uri}`, { + headers: { + 'Content-Type': 'application/json', + ...(options?.token && { Authorization: `Bearer ${options?.token}` }), + }, + method: 'GET', + }); + } + + /** + * Search for entities by a given query. + * @param fields Restrict to just these fields in the response. + * @param limit Number of records to return in the response. + * @param orderField The fields to sort returned results by. + * @param cursor Cursor to a set page of results. + * @param filter Filter for just the entities defined by this filter. + * @param fullTextFilterTerm Text search term. + * @param fullTextFilterFields A comma separated list of fields to sort returned results by. + */ + public async getEntitiesByQuery( + // @ts-ignore + request: { + query: { + fields?: Array; + limit?: number; + orderField?: Array; + cursor?: string; + filter?: Array; + fullTextFilterTerm?: string; + fullTextFilterFields?: Array; + }; + }, + options?: RequestOptions, + ): Promise> { + const baseUrl = await this.discoveryApi.getBaseUrl(pluginId); + + const uriTemplate = `/entities/by-query{?fields,limit,orderField*,cursor,filter*,fullTextFilterTerm,fullTextFilterFields}`; + + const uri = parser.parse(uriTemplate).expand({ + ...request.query, + }); + + return await this.fetchApi.fetch(`${baseUrl}${uri}`, { + headers: { + 'Content-Type': 'application/json', + ...(options?.token && { Authorization: `Bearer ${options?.token}` }), + }, + method: 'GET', + }); + } + + /** + * Get a batch set of entities given an array of entityRefs. + * @param getEntitiesByRefsRequest + */ + public async getEntitiesByRefs( + // @ts-ignore + request: { + body: GetEntitiesByRefsRequest; + }, + options?: RequestOptions, + ): Promise> { + const baseUrl = await this.discoveryApi.getBaseUrl(pluginId); + + const uriTemplate = `/entities/by-refs`; + + const uri = parser.parse(uriTemplate).expand({}); + + return await this.fetchApi.fetch(`${baseUrl}${uri}`, { + headers: { + 'Content-Type': 'application/json', + ...(options?.token && { Authorization: `Bearer ${options?.token}` }), + }, + method: 'POST', + body: JSON.stringify(request.body), + }); + } + + /** + * Get an entity's ancestry by entity ref. + * @param kind + * @param namespace + * @param name + */ + public async getEntityAncestryByName( + // @ts-ignore + request: { + path: { + kind: string; + namespace: string; + name: string; + }; + }, + options?: RequestOptions, + ): Promise> { + const baseUrl = await this.discoveryApi.getBaseUrl(pluginId); + + const uriTemplate = `/entities/by-name/{kind}/{namespace}/{name}/ancestry`; + + const uri = parser.parse(uriTemplate).expand({ + kind: request.path.kind, + namespace: request.path.namespace, + name: request.path.name, + }); + + return await this.fetchApi.fetch(`${baseUrl}${uri}`, { + headers: { + 'Content-Type': 'application/json', + ...(options?.token && { Authorization: `Bearer ${options?.token}` }), + }, + method: 'GET', + }); + } + + /** + * Get an entity by an entity ref. + * @param kind + * @param namespace + * @param name + */ + public async getEntityByName( + // @ts-ignore + request: { + path: { + kind: string; + namespace: string; + name: string; + }; + }, + options?: RequestOptions, + ): Promise> { + const baseUrl = await this.discoveryApi.getBaseUrl(pluginId); + + const uriTemplate = `/entities/by-name/{kind}/{namespace}/{name}`; + + const uri = parser.parse(uriTemplate).expand({ + kind: request.path.kind, + namespace: request.path.namespace, + name: request.path.name, + }); + + return await this.fetchApi.fetch(`${baseUrl}${uri}`, { + headers: { + 'Content-Type': 'application/json', + ...(options?.token && { Authorization: `Bearer ${options?.token}` }), + }, + method: 'GET', + }); + } + + /** + * Get a single entity by the UID. + * @param uid + */ + public async getEntityByUid( + // @ts-ignore + request: { + path: { + uid: string; + }; + }, + options?: RequestOptions, + ): Promise> { + const baseUrl = await this.discoveryApi.getBaseUrl(pluginId); + + const uriTemplate = `/entities/by-uid/{uid}`; + + const uri = parser.parse(uriTemplate).expand({ + uid: request.path.uid, + }); + + return await this.fetchApi.fetch(`${baseUrl}${uri}`, { + headers: { + 'Content-Type': 'application/json', + ...(options?.token && { Authorization: `Bearer ${options?.token}` }), + }, + method: 'GET', + }); + } + + /** + * Get all entity facets that match the given filters. + * @param facet + * @param filter Filter for just the entities defined by this filter. + */ + public async getEntityFacets( + // @ts-ignore + request: { + query: { + facet: Array; + filter?: Array; + }; + }, + options?: RequestOptions, + ): Promise> { + const baseUrl = await this.discoveryApi.getBaseUrl(pluginId); + + const uriTemplate = `/entity-facets{?facet*,filter*}`; + + const uri = parser.parse(uriTemplate).expand({ + ...request.query, + }); + + return await this.fetchApi.fetch(`${baseUrl}${uri}`, { + headers: { + 'Content-Type': 'application/json', + ...(options?.token && { Authorization: `Bearer ${options?.token}` }), + }, + method: 'GET', + }); + } + + /** + * Get a location by id. + * @param id + */ + public async getLocation( + // @ts-ignore + request: { + path: { + id: string; + }; + }, + options?: RequestOptions, + ): Promise> { + const baseUrl = await this.discoveryApi.getBaseUrl(pluginId); + + const uriTemplate = `/locations/{id}`; + + const uri = parser.parse(uriTemplate).expand({ + id: request.path.id, + }); + + return await this.fetchApi.fetch(`${baseUrl}${uri}`, { + headers: { + 'Content-Type': 'application/json', + ...(options?.token && { Authorization: `Bearer ${options?.token}` }), + }, + method: 'GET', + }); + } + + /** + * Get all locations + */ + public async getLocations( + // @ts-ignore + request: {}, + options?: RequestOptions, + ): Promise>> { + const baseUrl = await this.discoveryApi.getBaseUrl(pluginId); + + const uriTemplate = `/locations`; + + const uri = parser.parse(uriTemplate).expand({}); + + return await this.fetchApi.fetch(`${baseUrl}${uri}`, { + headers: { + 'Content-Type': 'application/json', + ...(options?.token && { Authorization: `Bearer ${options?.token}` }), + }, + method: 'GET', + }); + } + + /** + * Refresh the entity related to entityRef. + * @param refreshEntityRequest + */ + public async refreshEntity( + // @ts-ignore + request: { + body: RefreshEntityRequest; + }, + options?: RequestOptions, + ): Promise> { + const baseUrl = await this.discoveryApi.getBaseUrl(pluginId); + + const uriTemplate = `/refresh`; + + const uri = parser.parse(uriTemplate).expand({}); + + return await this.fetchApi.fetch(`${baseUrl}${uri}`, { + headers: { + 'Content-Type': 'application/json', + ...(options?.token && { Authorization: `Bearer ${options?.token}` }), + }, + method: 'POST', + body: JSON.stringify(request.body), + }); + } + + /** + * Validate that a passed in entity has no errors in schema. + * @param validateEntityRequest + */ + public async validateEntity( + // @ts-ignore + request: { + body: ValidateEntityRequest; + }, + options?: RequestOptions, + ): Promise> { + const baseUrl = await this.discoveryApi.getBaseUrl(pluginId); + + const uriTemplate = `/validate-entity`; + + const uri = parser.parse(uriTemplate).expand({}); + + return await this.fetchApi.fetch(`${baseUrl}${uri}`, { + headers: { + 'Content-Type': 'application/json', + ...(options?.token && { Authorization: `Bearer ${options?.token}` }), + }, + method: 'POST', + body: JSON.stringify(request.body), + }); + } +} diff --git a/plugins/kubernetes-react/src/utils/clusterLinks/index.ts b/packages/catalog-client/src/generated/apis/index.ts similarity index 75% rename from plugins/kubernetes-react/src/utils/clusterLinks/index.ts rename to packages/catalog-client/src/generated/apis/index.ts index 90a30e8277..ad2f72c5b2 100644 --- a/plugins/kubernetes-react/src/utils/clusterLinks/index.ts +++ b/packages/catalog-client/src/generated/apis/index.ts @@ -1,5 +1,5 @@ /* - * Copyright 2020 The Backstage Authors + * Copyright 2023 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,8 +14,4 @@ * limitations under the License. */ -export { - formatClusterLink, - type FormatClusterLinkOptions, -} from './formatClusterLink'; -export { clusterLinksFormatters } from './formatters'; +export * from './DefaultApi.client'; diff --git a/plugins/kubernetes-react/src/utils/index.ts b/packages/catalog-client/src/generated/index.ts similarity index 88% rename from plugins/kubernetes-react/src/utils/index.ts rename to packages/catalog-client/src/generated/index.ts index d47afaf668..7df0cfd292 100644 --- a/plugins/kubernetes-react/src/utils/index.ts +++ b/packages/catalog-client/src/generated/index.ts @@ -1,5 +1,5 @@ /* - * Copyright 2020 The Backstage Authors + * Copyright 2023 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,4 +13,5 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -export * from './clusterLinks'; + +export * from './apis'; diff --git a/packages/catalog-client/src/generated/models/AnalyzeLocationEntityField.model.ts b/packages/catalog-client/src/generated/models/AnalyzeLocationEntityField.model.ts new file mode 100644 index 0000000000..a1c1e1f669 --- /dev/null +++ b/packages/catalog-client/src/generated/models/AnalyzeLocationEntityField.model.ts @@ -0,0 +1,36 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export interface AnalyzeLocationEntityField { + /** + * A text to show to the user to inform about the choices made. Like, it could say \"Found a CODEOWNERS file that covers this target, so we suggest leaving this field empty; which would currently make it owned by X\" where X is taken from the codeowners file. + */ + description: string; + value: string | null; + /** + * The outcome of the analysis for this particular field + */ + state: AnalyzeLocationEntityFieldStateEnum; + /** + * e.g. \"spec.owner\"? The frontend needs to know how to \"inject\" the field into the entity again if the user wants to change it + */ + field: string; +} + +export type AnalyzeLocationEntityFieldStateEnum = + | 'analysisSuggestedValue' + | 'analysisSuggestedNoValue' + | 'needsUserInput'; diff --git a/packages/catalog-client/src/generated/models/AnalyzeLocationExistingEntity.model.ts b/packages/catalog-client/src/generated/models/AnalyzeLocationExistingEntity.model.ts new file mode 100644 index 0000000000..5ede0fb60b --- /dev/null +++ b/packages/catalog-client/src/generated/models/AnalyzeLocationExistingEntity.model.ts @@ -0,0 +1,27 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Entity } from '../models/Entity.model'; +import { LocationSpec } from '../models/LocationSpec.model'; + +/** + * If the folder pointed to already contained catalog info yaml files, they are read and emitted like this so that the frontend can inform the user that it located them and can make sure to register them as well if they weren't already + */ +export interface AnalyzeLocationExistingEntity { + entity: Entity; + isRegistered: boolean; + location: LocationSpec; +} diff --git a/packages/catalog-client/src/generated/models/AnalyzeLocationGenerateEntity.model.ts b/packages/catalog-client/src/generated/models/AnalyzeLocationGenerateEntity.model.ts new file mode 100644 index 0000000000..886aac05d9 --- /dev/null +++ b/packages/catalog-client/src/generated/models/AnalyzeLocationGenerateEntity.model.ts @@ -0,0 +1,26 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { AnalyzeLocationEntityField } from '../models/AnalyzeLocationEntityField.model'; +import { RecursivePartialEntity } from '../models/RecursivePartialEntity.model'; + +/** + * This is some form of representation of what the analyzer could deduce. We should probably have a chat about how this can best be conveyed to the frontend. It'll probably contain a (possibly incomplete) entity, plus enough info for the frontend to know what form data to show to the user for overriding/completing the info. + */ +export interface AnalyzeLocationGenerateEntity { + fields: Array; + entity: RecursivePartialEntity; +} diff --git a/plugins/kubernetes-react/src/utils/clusterLinks/formatters/eks.ts b/packages/catalog-client/src/generated/models/AnalyzeLocationRequest.model.ts similarity index 68% rename from plugins/kubernetes-react/src/utils/clusterLinks/formatters/eks.ts rename to packages/catalog-client/src/generated/models/AnalyzeLocationRequest.model.ts index 975c39797c..e8e9542f5a 100644 --- a/plugins/kubernetes-react/src/utils/clusterLinks/formatters/eks.ts +++ b/packages/catalog-client/src/generated/models/AnalyzeLocationRequest.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2020 The Backstage Authors + * Copyright 2023 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,8 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { ClusterLinksFormatterOptions } from '../../../types/types'; -export function eksFormatter(_options: ClusterLinksFormatterOptions): URL { - throw new Error('EKS formatter is not yet implemented. Please, contribute!'); +import { LocationInput } from '../models/LocationInput.model'; + +export interface AnalyzeLocationRequest { + catalogFileName?: string; + location: LocationInput; } diff --git a/packages/catalog-client/src/generated/models/AnalyzeLocationResponse.model.ts b/packages/catalog-client/src/generated/models/AnalyzeLocationResponse.model.ts new file mode 100644 index 0000000000..bf60aa8018 --- /dev/null +++ b/packages/catalog-client/src/generated/models/AnalyzeLocationResponse.model.ts @@ -0,0 +1,23 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { AnalyzeLocationExistingEntity } from '../models/AnalyzeLocationExistingEntity.model'; +import { AnalyzeLocationGenerateEntity } from '../models/AnalyzeLocationGenerateEntity.model'; + +export interface AnalyzeLocationResponse { + generateEntities: Array; + existingEntityFiles: Array; +} diff --git a/packages/catalog-client/src/generated/models/CreateLocation201Response.model.ts b/packages/catalog-client/src/generated/models/CreateLocation201Response.model.ts new file mode 100644 index 0000000000..d090d80bf2 --- /dev/null +++ b/packages/catalog-client/src/generated/models/CreateLocation201Response.model.ts @@ -0,0 +1,24 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Entity } from '../models/Entity.model'; +import { Location } from '../models/Location.model'; + +export interface CreateLocation201Response { + exists?: boolean; + entities: Array; + location: Location; +} diff --git a/packages/catalog-client/src/generated/models/CreateLocationRequest.model.ts b/packages/catalog-client/src/generated/models/CreateLocationRequest.model.ts new file mode 100644 index 0000000000..6ef950072e --- /dev/null +++ b/packages/catalog-client/src/generated/models/CreateLocationRequest.model.ts @@ -0,0 +1,20 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export interface CreateLocationRequest { + target: string; + type: string; +} diff --git a/packages/catalog-client/src/generated/models/EntitiesBatchResponse.model.ts b/packages/catalog-client/src/generated/models/EntitiesBatchResponse.model.ts new file mode 100644 index 0000000000..d09b2cc086 --- /dev/null +++ b/packages/catalog-client/src/generated/models/EntitiesBatchResponse.model.ts @@ -0,0 +1,24 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { NullableEntity } from '../models/NullableEntity.model'; + +export interface EntitiesBatchResponse { + /** + * The list of entities, in the same order as the refs in the request. Entries that are null signify that no entity existed with that ref. + */ + items: Array; +} diff --git a/packages/catalog-client/src/generated/models/EntitiesQueryResponse.model.ts b/packages/catalog-client/src/generated/models/EntitiesQueryResponse.model.ts new file mode 100644 index 0000000000..10d4747caf --- /dev/null +++ b/packages/catalog-client/src/generated/models/EntitiesQueryResponse.model.ts @@ -0,0 +1,27 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { EntitiesQueryResponsePageInfo } from '../models/EntitiesQueryResponsePageInfo.model'; +import { Entity } from '../models/Entity.model'; + +export interface EntitiesQueryResponse { + /** + * The list of entities paginated by a specific filter. + */ + items: Array; + totalItems: number; + pageInfo: EntitiesQueryResponsePageInfo; +} diff --git a/packages/catalog-client/src/generated/models/EntitiesQueryResponsePageInfo.model.ts b/packages/catalog-client/src/generated/models/EntitiesQueryResponsePageInfo.model.ts new file mode 100644 index 0000000000..8a7d5b39bb --- /dev/null +++ b/packages/catalog-client/src/generated/models/EntitiesQueryResponsePageInfo.model.ts @@ -0,0 +1,26 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export interface EntitiesQueryResponsePageInfo { + /** + * The cursor for the next batch of entities. + */ + nextCursor?: string; + /** + * The cursor for the previous batch of entities. + */ + prevCursor?: string; +} diff --git a/packages/catalog-client/src/generated/models/Entity.model.ts b/packages/catalog-client/src/generated/models/Entity.model.ts new file mode 100644 index 0000000000..59ef9bc741 --- /dev/null +++ b/packages/catalog-client/src/generated/models/Entity.model.ts @@ -0,0 +1,41 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { EntityMeta } from '../models/EntityMeta.model'; +import { EntityRelation } from '../models/EntityRelation.model'; + +/** + * The parts of the format that's common to all versions/kinds of entity. + */ +export interface Entity { + /** + * The relations that this entity has with other entities. + */ + relations?: Array; + /** + * A type representing all allowed JSON object values. + */ + spec?: { [key: string]: any }; + metadata: EntityMeta; + /** + * The high level entity type being described. + */ + kind: string; + /** + * The version of specification format for this particular entity that this is written against. + */ + apiVersion: string; +} diff --git a/packages/catalog-client/src/generated/models/EntityAncestryResponse.model.ts b/packages/catalog-client/src/generated/models/EntityAncestryResponse.model.ts new file mode 100644 index 0000000000..08076fcf36 --- /dev/null +++ b/packages/catalog-client/src/generated/models/EntityAncestryResponse.model.ts @@ -0,0 +1,22 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { EntityAncestryResponseItemsInner } from '../models/EntityAncestryResponseItemsInner.model'; + +export interface EntityAncestryResponse { + items: Array; + rootEntityRef: string; +} diff --git a/packages/catalog-client/src/generated/models/EntityAncestryResponseItemsInner.model.ts b/packages/catalog-client/src/generated/models/EntityAncestryResponseItemsInner.model.ts new file mode 100644 index 0000000000..7713c21295 --- /dev/null +++ b/packages/catalog-client/src/generated/models/EntityAncestryResponseItemsInner.model.ts @@ -0,0 +1,22 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Entity } from '../models/Entity.model'; + +export interface EntityAncestryResponseItemsInner { + parentEntityRefs: Array; + entity: Entity; +} diff --git a/packages/catalog-client/src/generated/models/EntityFacet.model.ts b/packages/catalog-client/src/generated/models/EntityFacet.model.ts new file mode 100644 index 0000000000..3872091d77 --- /dev/null +++ b/packages/catalog-client/src/generated/models/EntityFacet.model.ts @@ -0,0 +1,20 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export interface EntityFacet { + value: string; + count: number; +} diff --git a/packages/catalog-client/src/generated/models/EntityFacetsResponse.model.ts b/packages/catalog-client/src/generated/models/EntityFacetsResponse.model.ts new file mode 100644 index 0000000000..0ff37e4f2f --- /dev/null +++ b/packages/catalog-client/src/generated/models/EntityFacetsResponse.model.ts @@ -0,0 +1,21 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { EntityFacet } from '../models/EntityFacet.model'; + +export interface EntityFacetsResponse { + facets: { [key: string]: Array }; +} diff --git a/packages/catalog-client/src/generated/models/EntityLink.model.ts b/packages/catalog-client/src/generated/models/EntityLink.model.ts new file mode 100644 index 0000000000..f52f9f2df0 --- /dev/null +++ b/packages/catalog-client/src/generated/models/EntityLink.model.ts @@ -0,0 +1,37 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * A link to external information that is related to the entity. + */ +export interface EntityLink { + /** + * An optional value to categorize links into specific groups + */ + type?: string; + /** + * An optional semantic key that represents a visual icon. + */ + icon?: string; + /** + * An optional descriptive title for the link. + */ + title?: string; + /** + * The url to the external site, document, etc. + */ + url: string; +} diff --git a/packages/catalog-client/src/generated/models/EntityMeta.model.ts b/packages/catalog-client/src/generated/models/EntityMeta.model.ts new file mode 100644 index 0000000000..cb0fd28fdc --- /dev/null +++ b/packages/catalog-client/src/generated/models/EntityMeta.model.ts @@ -0,0 +1,64 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { EntityLink } from '../models/EntityLink.model'; + +/** + * Metadata fields common to all versions/kinds of entity. + */ +export interface EntityMeta { + [key: string]: any; + /** + * A list of external hyperlinks related to the entity. + */ + links?: Array; + /** + * A list of single-valued strings, to for example classify catalog entities in various ways. + */ + tags?: Array; + /** + * Construct a type with a set of properties K of type T + */ + annotations?: { [key: string]: string }; + /** + * Construct a type with a set of properties K of type T + */ + labels?: { [key: string]: string }; + /** + * A short (typically relatively few words, on one line) description of the entity. + */ + description?: string; + /** + * A display name of the entity, to be presented in user interfaces instead of the `name` property above, when available. This field is sometimes useful when the `name` is cumbersome or ends up being perceived as overly technical. The title generally does not have as stringent format requirements on it, so it may contain special characters and be more explanatory. Do keep it very short though, and avoid situations where a title can be confused with the name of another entity, or where two entities share a title. Note that this is only for display purposes, and may be ignored by some parts of the code. Entity references still always make use of the `name` property, not the title. + */ + title?: string; + /** + * The namespace that the entity belongs to. + */ + namespace?: string; + /** + * The name of the entity. Must be unique within the catalog at any given point in time, for any given namespace + kind pair. This value is part of the technical identifier of the entity, and as such it will appear in URLs, database tables, entity references, and similar. It is subject to restrictions regarding what characters are allowed. If you want to use a different, more human readable string with fewer restrictions on it in user interfaces, see the `title` field below. + */ + name: string; + /** + * An opaque string that changes for each update operation to any part of the entity, including metadata. This field can not be set by the user at creation time, and the server will reject an attempt to do so. The field will be populated in read operations. The field can (optionally) be specified when performing update or delete operations, and the server will then reject the operation if it does not match the current stored value. + */ + etag?: string; + /** + * A globally unique ID for the entity. This field can not be set by the user at creation time, and the server will reject an attempt to do so. The field will be populated in read operations. The field can (optionally) be specified when performing update or delete operations, but the server is free to reject requests that do so in such a way that it breaks semantics. + */ + uid?: string; +} diff --git a/packages/catalog-client/src/generated/models/EntityRelation.model.ts b/packages/catalog-client/src/generated/models/EntityRelation.model.ts new file mode 100644 index 0000000000..1107f7563c --- /dev/null +++ b/packages/catalog-client/src/generated/models/EntityRelation.model.ts @@ -0,0 +1,29 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * A relation of a specific type to another entity in the catalog. + */ +export interface EntityRelation { + /** + * The entity ref of the target of this relation. + */ + targetRef: string; + /** + * The type of the relation. + */ + type: string; +} diff --git a/packages/catalog-client/src/generated/models/ErrorError.model.ts b/packages/catalog-client/src/generated/models/ErrorError.model.ts new file mode 100644 index 0000000000..9dbfa22813 --- /dev/null +++ b/packages/catalog-client/src/generated/models/ErrorError.model.ts @@ -0,0 +1,22 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export interface ErrorError { + name: string; + message: string; + stack?: string; + code?: string; +} diff --git a/packages/catalog-client/src/generated/models/ErrorRequest.model.ts b/packages/catalog-client/src/generated/models/ErrorRequest.model.ts new file mode 100644 index 0000000000..e6306b87f5 --- /dev/null +++ b/packages/catalog-client/src/generated/models/ErrorRequest.model.ts @@ -0,0 +1,20 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export interface ErrorRequest { + method: string; + url: string; +} diff --git a/packages/catalog-client/src/generated/models/ErrorResponse.model.ts b/packages/catalog-client/src/generated/models/ErrorResponse.model.ts new file mode 100644 index 0000000000..1f55c34b4a --- /dev/null +++ b/packages/catalog-client/src/generated/models/ErrorResponse.model.ts @@ -0,0 +1,19 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export interface ErrorResponse { + statusCode: number; +} diff --git a/packages/catalog-client/src/generated/models/GetEntitiesByRefsRequest.model.ts b/packages/catalog-client/src/generated/models/GetEntitiesByRefsRequest.model.ts new file mode 100644 index 0000000000..bdda7c2fa6 --- /dev/null +++ b/packages/catalog-client/src/generated/models/GetEntitiesByRefsRequest.model.ts @@ -0,0 +1,20 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export interface GetEntitiesByRefsRequest { + entityRefs: Array; + fields?: Array; +} diff --git a/packages/catalog-client/src/generated/models/GetLocations200ResponseInner.model.ts b/packages/catalog-client/src/generated/models/GetLocations200ResponseInner.model.ts new file mode 100644 index 0000000000..134306cce7 --- /dev/null +++ b/packages/catalog-client/src/generated/models/GetLocations200ResponseInner.model.ts @@ -0,0 +1,21 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Location } from '../models/Location.model'; + +export interface GetLocations200ResponseInner { + data: Location; +} diff --git a/packages/catalog-client/src/generated/models/Location.model.ts b/packages/catalog-client/src/generated/models/Location.model.ts new file mode 100644 index 0000000000..27e47a5a25 --- /dev/null +++ b/packages/catalog-client/src/generated/models/Location.model.ts @@ -0,0 +1,24 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Entity location for a specific entity. + */ +export interface Location { + target: string; + type: string; + id: string; +} diff --git a/packages/catalog-client/src/generated/models/LocationInput.model.ts b/packages/catalog-client/src/generated/models/LocationInput.model.ts new file mode 100644 index 0000000000..8de7e3e6c9 --- /dev/null +++ b/packages/catalog-client/src/generated/models/LocationInput.model.ts @@ -0,0 +1,20 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export interface LocationInput { + type: string; + target: string; +} diff --git a/packages/catalog-client/src/generated/models/LocationSpec.model.ts b/packages/catalog-client/src/generated/models/LocationSpec.model.ts new file mode 100644 index 0000000000..80db2cadf2 --- /dev/null +++ b/packages/catalog-client/src/generated/models/LocationSpec.model.ts @@ -0,0 +1,23 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Holds the entity location information. + */ +export interface LocationSpec { + target: string; + type: string; +} diff --git a/packages/catalog-client/src/generated/models/ModelError.model.ts b/packages/catalog-client/src/generated/models/ModelError.model.ts new file mode 100644 index 0000000000..989e755c9a --- /dev/null +++ b/packages/catalog-client/src/generated/models/ModelError.model.ts @@ -0,0 +1,26 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ErrorError } from '../models/ErrorError.model'; +import { ErrorRequest } from '../models/ErrorRequest.model'; +import { ErrorResponse } from '../models/ErrorResponse.model'; + +export interface ModelError { + [key: string]: any; + error: ErrorError; + request?: ErrorRequest; + response: ErrorResponse; +} diff --git a/packages/catalog-client/src/generated/models/NullableEntity.model.ts b/packages/catalog-client/src/generated/models/NullableEntity.model.ts new file mode 100644 index 0000000000..36aff86ba8 --- /dev/null +++ b/packages/catalog-client/src/generated/models/NullableEntity.model.ts @@ -0,0 +1,41 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { EntityMeta } from '../models/EntityMeta.model'; +import { EntityRelation } from '../models/EntityRelation.model'; + +/** + * The parts of the format that's common to all versions/kinds of entity. + */ +export interface NullableEntity { + /** + * The relations that this entity has with other entities. + */ + relations?: Array; + /** + * A type representing all allowed JSON object values. + */ + spec?: { [key: string]: any }; + metadata: EntityMeta; + /** + * The high level entity type being described. + */ + kind: string; + /** + * The version of specification format for this particular entity that this is written against. + */ + apiVersion: string; +} diff --git a/packages/catalog-client/src/generated/models/RecursivePartialEntity.model.ts b/packages/catalog-client/src/generated/models/RecursivePartialEntity.model.ts new file mode 100644 index 0000000000..8de7824550 --- /dev/null +++ b/packages/catalog-client/src/generated/models/RecursivePartialEntity.model.ts @@ -0,0 +1,41 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { RecursivePartialEntityMeta } from '../models/RecursivePartialEntityMeta.model'; +import { RecursivePartialEntityRelation } from '../models/RecursivePartialEntityRelation.model'; + +/** + * Makes all keys of an entire hierarchy optional. + */ +export interface RecursivePartialEntity { + /** + * The version of specification format for this particular entity that this is written against. + */ + apiVersion?: string; + /** + * The high level entity type being described. + */ + kind?: string; + metadata?: RecursivePartialEntityMeta; + /** + * A type representing all allowed JSON object values. + */ + spec?: { [key: string]: any }; + /** + * The relations that this entity has with other entities. + */ + relations?: Array; +} diff --git a/packages/catalog-client/src/generated/models/RecursivePartialEntityMeta.model.ts b/packages/catalog-client/src/generated/models/RecursivePartialEntityMeta.model.ts new file mode 100644 index 0000000000..b21cc1bb21 --- /dev/null +++ b/packages/catalog-client/src/generated/models/RecursivePartialEntityMeta.model.ts @@ -0,0 +1,60 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { EntityLink } from '../models/EntityLink.model'; + +export interface RecursivePartialEntityMeta { + /** + * A list of external hyperlinks related to the entity. + */ + links?: Array; + /** + * A list of single-valued strings, to for example classify catalog entities in various ways. + */ + tags?: Array; + /** + * Construct a type with a set of properties K of type T + */ + annotations?: { [key: string]: string }; + /** + * Construct a type with a set of properties K of type T + */ + labels?: { [key: string]: string }; + /** + * A short (typically relatively few words, on one line) description of the entity. + */ + description?: string; + /** + * A display name of the entity, to be presented in user interfaces instead of the `name` property above, when available. This field is sometimes useful when the `name` is cumbersome or ends up being perceived as overly technical. The title generally does not have as stringent format requirements on it, so it may contain special characters and be more explanatory. Do keep it very short though, and avoid situations where a title can be confused with the name of another entity, or where two entities share a title. Note that this is only for display purposes, and may be ignored by some parts of the code. Entity references still always make use of the `name` property, not the title. + */ + title?: string; + /** + * The namespace that the entity belongs to. + */ + namespace?: string; + /** + * The name of the entity. Must be unique within the catalog at any given point in time, for any given namespace + kind pair. This value is part of the technical identifier of the entity, and as such it will appear in URLs, database tables, entity references, and similar. It is subject to restrictions regarding what characters are allowed. If you want to use a different, more human readable string with fewer restrictions on it in user interfaces, see the `title` field below. + */ + name?: string; + /** + * An opaque string that changes for each update operation to any part of the entity, including metadata. This field can not be set by the user at creation time, and the server will reject an attempt to do so. The field will be populated in read operations. The field can (optionally) be specified when performing update or delete operations, and the server will then reject the operation if it does not match the current stored value. + */ + etag?: string; + /** + * A globally unique ID for the entity. This field can not be set by the user at creation time, and the server will reject an attempt to do so. The field will be populated in read operations. The field can (optionally) be specified when performing update or delete operations, but the server is free to reject requests that do so in such a way that it breaks semantics. + */ + uid?: string; +} diff --git a/packages/catalog-client/src/generated/models/RecursivePartialEntityMetaAllOf.model.ts b/packages/catalog-client/src/generated/models/RecursivePartialEntityMetaAllOf.model.ts new file mode 100644 index 0000000000..5111cd7f9f --- /dev/null +++ b/packages/catalog-client/src/generated/models/RecursivePartialEntityMetaAllOf.model.ts @@ -0,0 +1,63 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { EntityLink } from '../models/EntityLink.model'; + +/** + * Metadata fields common to all versions/kinds of entity. + */ +export interface RecursivePartialEntityMetaAllOf { + /** + * A list of external hyperlinks related to the entity. + */ + links?: Array; + /** + * A list of single-valued strings, to for example classify catalog entities in various ways. + */ + tags?: Array; + /** + * Construct a type with a set of properties K of type T + */ + annotations?: { [key: string]: string }; + /** + * Construct a type with a set of properties K of type T + */ + labels?: { [key: string]: string }; + /** + * A short (typically relatively few words, on one line) description of the entity. + */ + description?: string; + /** + * A display name of the entity, to be presented in user interfaces instead of the `name` property above, when available. This field is sometimes useful when the `name` is cumbersome or ends up being perceived as overly technical. The title generally does not have as stringent format requirements on it, so it may contain special characters and be more explanatory. Do keep it very short though, and avoid situations where a title can be confused with the name of another entity, or where two entities share a title. Note that this is only for display purposes, and may be ignored by some parts of the code. Entity references still always make use of the `name` property, not the title. + */ + title?: string; + /** + * The namespace that the entity belongs to. + */ + namespace?: string; + /** + * The name of the entity. Must be unique within the catalog at any given point in time, for any given namespace + kind pair. This value is part of the technical identifier of the entity, and as such it will appear in URLs, database tables, entity references, and similar. It is subject to restrictions regarding what characters are allowed. If you want to use a different, more human readable string with fewer restrictions on it in user interfaces, see the `title` field below. + */ + name?: string; + /** + * An opaque string that changes for each update operation to any part of the entity, including metadata. This field can not be set by the user at creation time, and the server will reject an attempt to do so. The field will be populated in read operations. The field can (optionally) be specified when performing update or delete operations, and the server will then reject the operation if it does not match the current stored value. + */ + etag?: string; + /** + * A globally unique ID for the entity. This field can not be set by the user at creation time, and the server will reject an attempt to do so. The field will be populated in read operations. The field can (optionally) be specified when performing update or delete operations, but the server is free to reject requests that do so in such a way that it breaks semantics. + */ + uid?: string; +} diff --git a/packages/catalog-client/src/generated/models/RecursivePartialEntityRelation.model.ts b/packages/catalog-client/src/generated/models/RecursivePartialEntityRelation.model.ts new file mode 100644 index 0000000000..50635b69f4 --- /dev/null +++ b/packages/catalog-client/src/generated/models/RecursivePartialEntityRelation.model.ts @@ -0,0 +1,29 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * A relation of a specific type to another entity in the catalog. + */ +export interface RecursivePartialEntityRelation { + /** + * The entity ref of the target of this relation. + */ + targetRef?: string; + /** + * The type of the relation. + */ + type?: string; +} diff --git a/packages/catalog-client/src/generated/models/RefreshEntityRequest.model.ts b/packages/catalog-client/src/generated/models/RefreshEntityRequest.model.ts new file mode 100644 index 0000000000..573523385b --- /dev/null +++ b/packages/catalog-client/src/generated/models/RefreshEntityRequest.model.ts @@ -0,0 +1,26 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Options for requesting a refresh of entities in the catalog. + */ +export interface RefreshEntityRequest { + authorizationToken?: string; + /** + * The reference to a single entity that should be refreshed + */ + entityRef: string; +} diff --git a/packages/catalog-client/src/generated/models/ValidateEntity400Response.model.ts b/packages/catalog-client/src/generated/models/ValidateEntity400Response.model.ts new file mode 100644 index 0000000000..0031b2edc2 --- /dev/null +++ b/packages/catalog-client/src/generated/models/ValidateEntity400Response.model.ts @@ -0,0 +1,21 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ValidateEntity400ResponseErrorsInner } from '../models/ValidateEntity400ResponseErrorsInner.model'; + +export interface ValidateEntity400Response { + errors: Array; +} diff --git a/packages/catalog-client/src/generated/models/ValidateEntity400ResponseErrorsInner.model.ts b/packages/catalog-client/src/generated/models/ValidateEntity400ResponseErrorsInner.model.ts new file mode 100644 index 0000000000..6223df27af --- /dev/null +++ b/packages/catalog-client/src/generated/models/ValidateEntity400ResponseErrorsInner.model.ts @@ -0,0 +1,21 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export interface ValidateEntity400ResponseErrorsInner { + [key: string]: any; + name: string; + message: string; +} diff --git a/packages/catalog-client/src/generated/models/ValidateEntityRequest.model.ts b/packages/catalog-client/src/generated/models/ValidateEntityRequest.model.ts new file mode 100644 index 0000000000..d5ce1be5c9 --- /dev/null +++ b/packages/catalog-client/src/generated/models/ValidateEntityRequest.model.ts @@ -0,0 +1,20 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export interface ValidateEntityRequest { + location: string; + entity: { [key: string]: any }; +} diff --git a/packages/catalog-client/src/generated/pluginId.ts b/packages/catalog-client/src/generated/pluginId.ts new file mode 100644 index 0000000000..c8c8ce1ca3 --- /dev/null +++ b/packages/catalog-client/src/generated/pluginId.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export const pluginId = 'catalog'; diff --git a/packages/catalog-client/src/generated/types/discovery.ts b/packages/catalog-client/src/generated/types/discovery.ts new file mode 100644 index 0000000000..a7f87d3780 --- /dev/null +++ b/packages/catalog-client/src/generated/types/discovery.ts @@ -0,0 +1,22 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * This is a copy of the DiscoveryApi, to avoid importing core-plugin-api. + */ +export type DiscoveryApi = { + getBaseUrl(pluginId: string): Promise; +}; diff --git a/packages/catalog-client/src/generated/types/fetch.ts b/packages/catalog-client/src/generated/types/fetch.ts new file mode 100644 index 0000000000..3de56c028e --- /dev/null +++ b/packages/catalog-client/src/generated/types/fetch.ts @@ -0,0 +1,22 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * This is a copy of FetchApi, to avoid importing core-plugin-api. + */ +export type FetchApi = { + fetch: typeof fetch; +}; diff --git a/packages/catalog-model/alpha-api-report.md b/packages/catalog-model/api-report-alpha.md similarity index 100% rename from packages/catalog-model/alpha-api-report.md rename to packages/catalog-model/api-report-alpha.md diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index ec80b61dc4..acee7fd45c 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,30 @@ # @backstage/cli +## 0.25.0-next.1 + +### Minor Changes + +- 38340678c3: Updates the ESLint config to ignore issues created by generated files in `**/src/generated/**`. + +### Patch Changes + +- 0ffee55010: Toned down the warning message when git is not found +- c6f3743172: Added a warning when starting a standalone backend plugin that hasn't been updated to the new backend system. +- 3e358b0dff: Added deprecation warning for React Router v6 beta, please make sure you have migrated your apps to use React Router v6 stable as support for the beta version will be removed. See the [migration tutorial](https://backstage.io/docs/tutorials/react-router-stable-migration) for more information. +- 8056425e09: Updated dependency `@typescript-eslint/eslint-plugin` to `6.12.0`. +- 33e96e59e7: Switched the `@typescript-eslint/eslint-plugin` dependency back to using a `^` version range. +- Updated dependencies + - @backstage/eslint-plugin@0.1.4-next.0 + - @backstage/integration@1.8.0-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/cli-common@0.1.13 + - @backstage/cli-node@0.2.0 + - @backstage/config@1.1.1 + - @backstage/config-loader@1.5.3 + - @backstage/errors@1.2.3 + - @backstage/release-manifests@0.0.11 + - @backstage/types@1.1.1 + ## 0.24.1-next.0 ### Patch Changes diff --git a/packages/cli/config/eslint-factory.js b/packages/cli/config/eslint-factory.js index 58a9362051..3717943427 100644 --- a/packages/cli/config/eslint-factory.js +++ b/packages/cli/config/eslint-factory.js @@ -66,7 +66,7 @@ function createConfig(dir, extraConfig = {}) { ...(extraExtends ?? []), ], parser: '@typescript-eslint/parser', - plugins: ['import', ...(plugins ?? [])], + plugins: ['import', 'unused-imports', ...(plugins ?? [])], env: { jest: true, ...env, @@ -165,6 +165,23 @@ function createConfig(dir, extraConfig = {}) { ], }, }, + { + files: ['**/src/generated/**/*.ts'], + rules: { + ...tsRules, + 'no-unused-vars': 'off', + 'unused-imports/no-unused-imports': 'error', + 'unused-imports/no-unused-vars': [ + 'warn', + { + vars: 'all', + varsIgnorePattern: '^_', + args: 'none', + argsIgnorePattern: '^_', + }, + ], + }, + }, ...(overrides ?? []), ], }; diff --git a/packages/cli/package.json b/packages/cli/package.json index 490325296c..7281d81f0b 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/cli", "description": "CLI for developing Backstage plugins and apps", - "version": "0.24.1-next.0", + "version": "0.25.0-next.1", "publishConfig": { "access": "public" }, @@ -46,9 +46,9 @@ "@octokit/oauth-app": "^4.2.0", "@octokit/request": "^6.0.0", "@pmmmwh/react-refresh-webpack-plugin": "^0.5.7", - "@rollup/plugin-commonjs": "^23.0.0", - "@rollup/plugin-json": "^5.0.0", - "@rollup/plugin-node-resolve": "^13.0.6", + "@rollup/plugin-commonjs": "^25.0.0", + "@rollup/plugin-json": "^6.0.0", + "@rollup/plugin-node-resolve": "^15.0.0", "@rollup/plugin-yaml": "^4.0.0", "@spotify/eslint-config-base": "^14.0.0", "@spotify/eslint-config-react": "^14.0.0", @@ -64,7 +64,7 @@ "@swc/jest": "^0.2.22", "@types/jest": "^29.0.0", "@types/webpack-env": "^1.15.2", - "@typescript-eslint/eslint-plugin": "6.11.0", + "@typescript-eslint/eslint-plugin": "^6.12.0", "@typescript-eslint/parser": "^6.7.2", "@yarnpkg/lockfile": "^1.1.0", "@yarnpkg/parsers": "^3.0.0-rc.4", @@ -89,6 +89,7 @@ "eslint-plugin-jsx-a11y": "^6.5.1", "eslint-plugin-react": "^7.28.0", "eslint-plugin-react-hooks": "^4.3.0", + "eslint-plugin-unused-imports": "^3.0.0", "eslint-webpack-plugin": "^3.1.1", "express": "^4.17.1", "fork-ts-checker-webpack-plugin": "^7.0.0-alpha.8", @@ -176,7 +177,7 @@ "@vitejs/plugin-react": "^4.0.4", "vite": "^4.4.9", "vite-plugin-html": "^3.2.0", - "vite-plugin-node-polyfills": "^0.16.0" + "vite-plugin-node-polyfills": "^0.17.0" }, "peerDependenciesMeta": { "@vitejs/plugin-react": { diff --git a/packages/cli/src/commands/start/command.ts b/packages/cli/src/commands/start/command.ts index 48122d5ac0..427f8b6efb 100644 --- a/packages/cli/src/commands/start/command.ts +++ b/packages/cli/src/commands/start/command.ts @@ -16,7 +16,7 @@ import { OptionValues } from 'commander'; import { findRoleFromCommand } from '../../lib/role'; -import { startBackend } from './startBackend'; +import { startBackend, startBackendPlugin } from './startBackend'; import { startFrontend } from './startFrontend'; export async function command(opts: OptionValues): Promise { @@ -31,10 +31,11 @@ export async function command(opts: OptionValues): Promise { switch (role) { case 'backend': + return startBackend(options); case 'backend-plugin': case 'backend-plugin-module': case 'node-library': - return startBackend(options); + return startBackendPlugin(options); case 'frontend': return startFrontend({ ...options, diff --git a/packages/cli/src/commands/start/startBackend.ts b/packages/cli/src/commands/start/startBackend.ts index 967a1108a0..25758a0939 100644 --- a/packages/cli/src/commands/start/startBackend.ts +++ b/packages/cli/src/commands/start/startBackend.ts @@ -26,17 +26,7 @@ interface StartBackendOptions { } export async function startBackend(options: StartBackendOptions) { - const hasDev = await fs.pathExists(paths.resolveTarget('dev')); - if (hasDev) { - const waitForExit = await startBackendExperimental({ - entry: 'dev/index', - checksEnabled: false, // not supported - inspectEnabled: options.inspectEnabled, - inspectBrkEnabled: options.inspectBrkEnabled, - }); - - await waitForExit(); - } else if (!process.env.LEGACY_BACKEND_START) { + if (!process.env.LEGACY_BACKEND_START) { const waitForExit = await startBackendExperimental({ entry: 'src/index', checksEnabled: false, // not supported @@ -46,12 +36,7 @@ export async function startBackend(options: StartBackendOptions) { await waitForExit(); } else { - // Cleaning dist/ before we start the dev process helps work around an issue - // where we end up with the entrypoint executing multiple times, causing - // a port bind conflict among other things. - await fs.remove(paths.resolveTarget('dist')); - - const waitForExit = await serveBackend({ + const waitForExit = await cleanDistAndServeBackend({ entry: 'src/index', checksEnabled: options.checksEnabled, inspectEnabled: options.inspectEnabled, @@ -61,3 +46,63 @@ export async function startBackend(options: StartBackendOptions) { await waitForExit(); } } + +export async function startBackendPlugin(options: StartBackendOptions) { + if (!process.env.LEGACY_BACKEND_START) { + const hasDevIndexEntry = await fs.pathExists( + paths.resolveTarget('dev', 'index.ts'), + ); + const hasSrcIndexEntry = await fs.pathExists( + paths.resolveTarget('src', 'run.ts'), + ); + + if (!hasDevIndexEntry && !hasSrcIndexEntry) { + console.warn( + hasSrcIndexEntry + ? `The 'dev' directory is missing. The plugin might not be updated for the new backend system. To run, use "LEGACY_BACKEND_START=1 yarn start".` + : `The 'dev' directory is missing. Please create a proper dev/index.ts in order to start the plugin.`, + ); + return; + } + + const waitForExit = await startBackendExperimental({ + entry: 'dev/index', + checksEnabled: false, // not supported + inspectEnabled: options.inspectEnabled, + inspectBrkEnabled: options.inspectBrkEnabled, + }); + + await waitForExit(); + } else { + const hasEntry = await fs.pathExists(paths.resolveTarget('src', 'run.ts')); + if (!hasEntry) { + console.warn( + `src/run.ts is missing. Please create the file or run the command without LEGACY_BACKEND_START`, + ); + return; + } + + const waitForExit = await cleanDistAndServeBackend({ + entry: 'src/run', + checksEnabled: options.checksEnabled, + inspectEnabled: options.inspectEnabled, + inspectBrkEnabled: options.inspectBrkEnabled, + }); + + await waitForExit(); + } +} + +async function cleanDistAndServeBackend(options: { + entry: string; + checksEnabled: boolean; + inspectEnabled: boolean; + inspectBrkEnabled: boolean; +}) { + // Cleaning dist/ before we start the dev process helps work around an issue + // where we end up with the entrypoint executing multiple times, causing + // a port bind conflict among other things. + await fs.remove(paths.resolveTarget('dist')); + + return serveBackend(options); +} diff --git a/packages/cli/src/lib/bundler/config.ts b/packages/cli/src/lib/bundler/config.ts index 542cc83ce9..a9d5c2aa67 100644 --- a/packages/cli/src/lib/bundler/config.ts +++ b/packages/cli/src/lib/bundler/config.ts @@ -55,18 +55,24 @@ export function resolveBaseUrl(config: Config): URL { async function readBuildInfo() { const timestamp = Date.now(); - let commit = 'unknown'; + let commit: string | undefined; try { commit = await runPlain('git', 'rev-parse', 'HEAD'); } catch (error) { - console.warn(`WARNING: Failed to read git commit, ${error}`); + // ignore, see below } - let gitVersion = 'unknown'; + let gitVersion: string | undefined; try { gitVersion = await runPlain('git', 'describe', '--always'); } catch (error) { - console.warn(`WARNING: Failed to describe git version, ${error}`); + // ignore, see below + } + + if (commit === undefined || gitVersion === undefined) { + console.info( + 'NOTE: Did not compute git version or commit hash, could not execute the git command line utility', + ); } const { version: packageVersion } = await fs.readJson( @@ -75,10 +81,10 @@ async function readBuildInfo() { return { cliVersion: version, - gitVersion, + gitVersion: gitVersion ?? 'unknown', packageVersion, timestamp, - commit, + commit: commit ?? 'unknown', }; } diff --git a/packages/core-app-api/CHANGELOG.md b/packages/core-app-api/CHANGELOG.md index f77c607e3f..2a0e26c1e7 100644 --- a/packages/core-app-api/CHANGELOG.md +++ b/packages/core-app-api/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/core-app-api +## 1.11.2-next.1 + +### Patch Changes + +- 3e358b0dff: Added deprecation warning for React Router v6 beta, please make sure you have migrated your apps to use React Router v6 stable as support for the beta version will be removed. See the [migration tutorial](https://backstage.io/docs/tutorials/react-router-stable-migration) for more information. +- Updated dependencies + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.7 + ## 1.11.2-next.0 ### Patch Changes diff --git a/packages/core-app-api/package.json b/packages/core-app-api/package.json index 0efa8b3bb8..b18f31eb4f 100644 --- a/packages/core-app-api/package.json +++ b/packages/core-app-api/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/core-app-api", "description": "Core app API used by Backstage apps", - "version": "1.11.2-next.0", + "version": "1.11.2-next.1", "publishConfig": { "access": "public" }, diff --git a/packages/core-compat-api/CHANGELOG.md b/packages/core-compat-api/CHANGELOG.md index 017e3ec785..1be69b6986 100644 --- a/packages/core-compat-api/CHANGELOG.md +++ b/packages/core-compat-api/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/core-compat-api +## 0.0.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.4.0-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/core-app-api@1.11.2-next.1 + ## 0.0.1-next.0 ### Patch Changes diff --git a/packages/core-compat-api/api-report.md b/packages/core-compat-api/api-report.md index 877aefa24f..93a2fa8cf1 100644 --- a/packages/core-compat-api/api-report.md +++ b/packages/core-compat-api/api-report.md @@ -6,7 +6,9 @@ /// import { AnyRouteRefParams } from '@backstage/core-plugin-api'; +import { AppComponents } from '@backstage/core-plugin-api'; import { BackstagePlugin } from '@backstage/frontend-plugin-api'; +import { ExtensionDefinition } from '@backstage/frontend-plugin-api'; import { ExtensionOverrides } from '@backstage/frontend-plugin-api'; import { ExternalRouteRef } from '@backstage/core-plugin-api'; import { ExternalRouteRef as ExternalRouteRef_2 } from '@backstage/frontend-plugin-api'; @@ -16,6 +18,11 @@ import { RouteRef as RouteRef_2 } from '@backstage/frontend-plugin-api'; import { SubRouteRef } from '@backstage/core-plugin-api'; import { SubRouteRef as SubRouteRef_2 } from '@backstage/frontend-plugin-api'; +// @public (undocumented) +export function collectLegacyComponents( + components: Partial, +): ExtensionDefinition[]; + // @public (undocumented) export function collectLegacyRoutes( flatRoutesElement: JSX.Element, diff --git a/packages/core-compat-api/package.json b/packages/core-compat-api/package.json index f887193616..531b09722e 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.0.1-next.0", + "version": "0.0.1-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/packages/core-compat-api/src/collectLegacyComponents.test.tsx b/packages/core-compat-api/src/collectLegacyComponents.test.tsx new file mode 100644 index 0000000000..7ca11a777e --- /dev/null +++ b/packages/core-compat-api/src/collectLegacyComponents.test.tsx @@ -0,0 +1,66 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; + +import { collectLegacyComponents } from './collectLegacyComponents'; + +describe('collectLegacyComponents', () => { + const components = { + Progress: () =>
Progress
, + BootErrorPage: () =>
BootErrorPage
, + NotFoundErrorPage: () =>
NotFoundErrorPage
, + ErrorBoundaryFallback: () =>
ErrorBoundaryFallback
, + }; + + it('should collect legacy routes', () => { + const collected = collectLegacyComponents(components); + + expect( + collected.map(p => ({ + kind: p.kind, + namespace: p.namespace, + attachTo: p.attachTo, + disabled: p.disabled, + })), + ).toEqual([ + { + kind: 'component', + namespace: 'core.components.progress', + attachTo: { id: 'core', input: 'components' }, + disabled: false, + }, + { + kind: 'component', + namespace: 'core.components.bootErrorPage', + attachTo: { id: 'core', input: 'components' }, + disabled: false, + }, + { + kind: 'component', + namespace: 'core.components.notFoundErrorPage', + attachTo: { id: 'core', input: 'components' }, + disabled: false, + }, + { + kind: 'component', + namespace: 'core.components.errorBoundaryFallback', + attachTo: { id: 'core', input: 'components' }, + disabled: false, + }, + ]); + }); +}); diff --git a/packages/core-compat-api/src/collectLegacyComponents.tsx b/packages/core-compat-api/src/collectLegacyComponents.tsx new file mode 100644 index 0000000000..6ae6df7cff --- /dev/null +++ b/packages/core-compat-api/src/collectLegacyComponents.tsx @@ -0,0 +1,50 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + ComponentRef, + createComponentExtension, + coreComponentsRefs, + ExtensionDefinition, +} from '@backstage/frontend-plugin-api'; +import { AppComponents } from '@backstage/core-plugin-api'; + +type ComponentTypes = T[keyof T]; + +const refs: Record> = { + Progress: coreComponentsRefs.progress, + BootErrorPage: coreComponentsRefs.bootErrorPage, + NotFoundErrorPage: coreComponentsRefs.notFoundErrorPage, + ErrorBoundaryFallback: coreComponentsRefs.errorBoundaryFallback, +}; + +/** @public */ +export function collectLegacyComponents(components: Partial) { + return Object.entries(components).reduce[]>( + (extensions, [name, component]) => { + const ref = refs[name]; + return ref + ? extensions.concat( + createComponentExtension({ + ref, + component: { sync: () => component }, + }), + ) + : extensions; + }, + [], + ); +} diff --git a/packages/core-compat-api/src/collectLegacyRoutes.test.tsx b/packages/core-compat-api/src/collectLegacyRoutes.test.tsx index 85fed83e8c..3dd9f330cc 100644 --- a/packages/core-compat-api/src/collectLegacyRoutes.test.tsx +++ b/packages/core-compat-api/src/collectLegacyRoutes.test.tsx @@ -22,6 +22,8 @@ import React from 'react'; import { Route } from 'react-router-dom'; import { collectLegacyRoutes } from './collectLegacyRoutes'; +// eslint-disable-next-line @backstage/no-relative-monorepo-imports +import { toInternalBackstagePlugin } from '../../frontend-plugin-api/src/wiring/createPlugin'; describe('collectLegacyRoutes', () => { it('should collect legacy routes', () => { @@ -37,7 +39,7 @@ describe('collectLegacyRoutes', () => { expect( collected.map(p => ({ id: p.id, - extensions: p.extensions.map(e => ({ + extensions: toInternalBackstagePlugin(p).extensions.map(e => ({ id: e.id, attachTo: e.attachTo, disabled: e.disabled, @@ -49,13 +51,13 @@ describe('collectLegacyRoutes', () => { id: 'score-card', extensions: [ { - id: 'plugin.score-card.page', - attachTo: { id: 'core.routes', input: 'routes' }, + id: 'page:score-card', + attachTo: { id: 'core/routes', input: 'routes' }, disabled: false, defaultConfig: { path: 'score-board' }, }, { - id: 'apis.plugin.scoringdata.service', + id: 'api:plugin.scoringdata.service', attachTo: { id: 'core', input: 'apis' }, disabled: false, }, @@ -65,13 +67,13 @@ describe('collectLegacyRoutes', () => { id: 'stackstorm', extensions: [ { - id: 'plugin.stackstorm.page', - attachTo: { id: 'core.routes', input: 'routes' }, + id: 'page:stackstorm', + attachTo: { id: 'core/routes', input: 'routes' }, disabled: false, defaultConfig: { path: 'stackstorm' }, }, { - id: 'apis.plugin.stackstorm.service', + id: 'api:plugin.stackstorm.service', attachTo: { id: 'core', input: 'apis' }, disabled: false, }, @@ -81,19 +83,19 @@ describe('collectLegacyRoutes', () => { id: 'puppetDb', extensions: [ { - id: 'plugin.puppetDb.page', - attachTo: { id: 'core.routes', input: 'routes' }, + id: 'page:puppetDb', + attachTo: { id: 'core/routes', input: 'routes' }, disabled: false, defaultConfig: { path: 'puppetdb' }, }, { - id: 'plugin.puppetDb.page2', - attachTo: { id: 'core.routes', input: 'routes' }, + id: 'page:puppetDb/2', + attachTo: { id: 'core/routes', input: 'routes' }, disabled: false, defaultConfig: { path: 'puppetdb' }, }, { - id: 'apis.plugin.puppetdb.service', + id: 'api:plugin.puppetdb.service', attachTo: { id: 'core', input: 'apis' }, disabled: false, }, diff --git a/packages/core-compat-api/src/collectLegacyRoutes.tsx b/packages/core-compat-api/src/collectLegacyRoutes.tsx index 037107bfda..92bced29a2 100644 --- a/packages/core-compat-api/src/collectLegacyRoutes.tsx +++ b/packages/core-compat-api/src/collectLegacyRoutes.tsx @@ -16,11 +16,11 @@ import React, { ReactNode } from 'react'; import { - Extension, createApiExtension, createPageExtension, createPlugin, BackstagePlugin, + ExtensionDefinition, } from '@backstage/frontend-plugin-api'; import { Route, Routes } from 'react-router-dom'; import { @@ -64,7 +64,7 @@ export function collectLegacyRoutes( ): BackstagePlugin[] { const createdPluginIds = new Map< LegacyBackstagePlugin, - Extension[] + ExtensionDefinition[] >(); React.Children.forEach( @@ -95,19 +95,18 @@ export function collectLegacyRoutes( 'core.mountPoint', ); - const pluginId = plugin.getId(); - const detectedExtensions = - createdPluginIds.get(plugin) ?? new Array>(); + createdPluginIds.get(plugin) ?? + new Array>(); createdPluginIds.set(plugin, detectedExtensions); const path: string = route.props.path; detectedExtensions.push( createPageExtension({ - id: `plugin.${pluginId}.page${ - detectedExtensions.length ? detectedExtensions.length + 1 : '' - }`, + name: detectedExtensions.length + ? String(detectedExtensions.length + 1) + : undefined, defaultPath: path[0] === '/' ? path.slice(1) : path, routeRef: routeRef ? convertLegacyRouteRef(routeRef) : undefined, @@ -132,9 +131,7 @@ export function collectLegacyRoutes( extensions: [ ...extensions, ...Array.from(plugin.getApis()).map(factory => - createApiExtension({ - factory, - }), + createApiExtension({ factory }), ), ], }), diff --git a/packages/core-compat-api/src/convertLegacyApp.test.tsx b/packages/core-compat-api/src/convertLegacyApp.test.tsx index 0f1f6145c9..4a5be57bc8 100644 --- a/packages/core-compat-api/src/convertLegacyApp.test.tsx +++ b/packages/core-compat-api/src/convertLegacyApp.test.tsx @@ -59,13 +59,13 @@ describe('convertLegacyApp', () => { id: 'score-card', extensions: [ { - id: 'plugin.score-card.page', - attachTo: { id: 'core.routes', input: 'routes' }, + id: 'page:score-card', + attachTo: { id: 'core/routes', input: 'routes' }, disabled: false, defaultConfig: { path: 'score-board' }, }, { - id: 'apis.plugin.scoringdata.service', + id: 'api:plugin.scoringdata.service', attachTo: { id: 'core', input: 'apis' }, disabled: false, }, @@ -75,13 +75,13 @@ describe('convertLegacyApp', () => { id: 'stackstorm', extensions: [ { - id: 'plugin.stackstorm.page', - attachTo: { id: 'core.routes', input: 'routes' }, + id: 'page:stackstorm', + attachTo: { id: 'core/routes', input: 'routes' }, disabled: false, defaultConfig: { path: 'stackstorm' }, }, { - id: 'apis.plugin.stackstorm.service', + id: 'api:plugin.stackstorm.service', attachTo: { id: 'core', input: 'apis' }, disabled: false, }, @@ -91,19 +91,19 @@ describe('convertLegacyApp', () => { id: 'puppetDb', extensions: [ { - id: 'plugin.puppetDb.page', - attachTo: { id: 'core.routes', input: 'routes' }, + id: 'page:puppetDb', + attachTo: { id: 'core/routes', input: 'routes' }, disabled: false, defaultConfig: { path: 'puppetdb' }, }, { - id: 'plugin.puppetDb.page2', - attachTo: { id: 'core.routes', input: 'routes' }, + id: 'page:puppetDb/2', + attachTo: { id: 'core/routes', input: 'routes' }, disabled: false, defaultConfig: { path: 'puppetdb' }, }, { - id: 'apis.plugin.puppetdb.service', + id: 'api:plugin.puppetdb.service', attachTo: { id: 'core', input: 'apis' }, disabled: false, }, @@ -113,13 +113,13 @@ describe('convertLegacyApp', () => { id: undefined, extensions: [ { - id: 'core.layout', + id: 'core/layout', attachTo: { id: 'core', input: 'root' }, disabled: false, }, { - id: 'core.nav', - attachTo: { id: 'core.layout', input: 'nav' }, + id: 'core/nav', + attachTo: { id: 'core/layout', input: 'nav' }, disabled: true, }, ], diff --git a/packages/core-compat-api/src/convertLegacyApp.ts b/packages/core-compat-api/src/convertLegacyApp.ts index 93521b3955..9fe07cc783 100644 --- a/packages/core-compat-api/src/convertLegacyApp.ts +++ b/packages/core-compat-api/src/convertLegacyApp.ts @@ -100,7 +100,8 @@ export function convertLegacyApp( const [routesEl] = routesEls; const CoreLayoutOverride = createExtension({ - id: 'core.layout', + namespace: 'core', + name: 'layout', attachTo: { id: 'core', input: 'root' }, inputs: { content: createExtensionInput( @@ -116,13 +117,18 @@ export function convertLegacyApp( factory({ inputs }) { // Clone the root element, this replaces the FlatRoutes declared in the app with out content input return { - element: React.cloneElement(rootEl, undefined, inputs.content.element), + element: React.cloneElement( + rootEl, + undefined, + inputs.content.output.element, + ), }; }, }); const CoreNavOverride = createExtension({ - id: 'core.nav', - attachTo: { id: 'core.layout', input: 'nav' }, + namespace: 'core', + name: 'nav', + attachTo: { id: 'core/layout', input: 'nav' }, output: {}, factory: () => ({}), disabled: true, diff --git a/packages/core-compat-api/src/index.ts b/packages/core-compat-api/src/index.ts index 421ced7054..d31513e559 100644 --- a/packages/core-compat-api/src/index.ts +++ b/packages/core-compat-api/src/index.ts @@ -14,5 +14,6 @@ * limitations under the License. */ export { collectLegacyRoutes } from './collectLegacyRoutes'; +export { collectLegacyComponents } from './collectLegacyComponents'; export { convertLegacyApp } from './convertLegacyApp'; export { convertLegacyRouteRef } from './convertLegacyRouteRef'; diff --git a/packages/core-components/CHANGELOG.md b/packages/core-components/CHANGELOG.md index f726aa1b19..6a319b1add 100644 --- a/packages/core-components/CHANGELOG.md +++ b/packages/core-components/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/core-components +## 0.13.9-next.1 + +### Patch Changes + +- e8f2acef80: Added a new `/testUtils` sub-path that initially exports a `mockBreakpoint` helper. +- 07dfdf3702: Updated dependency `linkifyjs` to `4.1.3`. +- a518c5a25b: Updated dependency `@react-hookz/web` to `^23.0.0`. +- f291757e70: Update `linkify-react` to version `4.1.3` +- Updated dependencies + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + - @backstage/version-bridge@1.0.7 + ## 0.13.9-next.0 ### Patch Changes diff --git a/packages/core-components/api-report-testUtils.md b/packages/core-components/api-report-testUtils.md new file mode 100644 index 0000000000..93a6bb2a07 --- /dev/null +++ b/packages/core-components/api-report-testUtils.md @@ -0,0 +1,10 @@ +## API Report File for "@backstage/core-components" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +// @public +export function mockBreakpoint(options: { matches: boolean }): void; + +// (No @packageDocumentation comment for this package) +``` diff --git a/packages/core-components/package.json b/packages/core-components/package.json index 1c1201a320..19bcb306cc 100644 --- a/packages/core-components/package.json +++ b/packages/core-components/package.json @@ -1,11 +1,9 @@ { "name": "@backstage/core-components", "description": "Core components used by Backstage plugins and apps", - "version": "0.13.9-next.0", + "version": "0.13.9-next.1", "publishConfig": { - "access": "public", - "main": "dist/index.esm.js", - "types": "dist/index.d.ts" + "access": "public" }, "backstage": { "role": "web-library" @@ -22,6 +20,21 @@ "license": "Apache-2.0", "main": "src/index.ts", "types": "src/index.ts", + "exports": { + ".": "./src/index.ts", + "./testUtils": "./src/testUtils.ts", + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "testUtils": [ + "src/testUtils.ts" + ], + "package.json": [ + "package.json" + ] + } + }, "sideEffects": false, "scripts": { "build": "backstage-cli package build", @@ -43,7 +56,7 @@ "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.61", - "@react-hookz/web": "^20.0.0", + "@react-hookz/web": "^23.0.0", "@types/react": "^16.13.1 || ^17.0.0", "@types/react-sparklines": "^1.7.0", "@types/react-text-truncate": "^0.14.0", @@ -56,7 +69,7 @@ "history": "^5.0.0", "immer": "^9.0.1", "linkify-react": "4.1.3", - "linkifyjs": "4.1.2", + "linkifyjs": "4.1.3", "lodash": "^4.17.21", "pluralize": "^8.0.0", "qs": "^6.9.4", diff --git a/packages/core-components/src/layout/Sidebar/MobileSidebar.test.tsx b/packages/core-components/src/layout/Sidebar/MobileSidebar.test.tsx index 7012afabf1..c29276a6cf 100644 --- a/packages/core-components/src/layout/Sidebar/MobileSidebar.test.tsx +++ b/packages/core-components/src/layout/Sidebar/MobileSidebar.test.tsx @@ -14,7 +14,8 @@ * limitations under the License. */ -import { mockBreakpoint, renderInTestApp } from '@backstage/test-utils'; +import { renderInTestApp } from '@backstage/test-utils'; +import { mockBreakpoint } from '@backstage/core-components/testUtils'; import CreateComponentIcon from '@material-ui/icons/AddCircleOutline'; import HomeIcon from '@material-ui/icons/Home'; import LayersIcon from '@material-ui/icons/Layers'; diff --git a/packages/core-components/src/layout/Sidebar/SidebarGroup.test.tsx b/packages/core-components/src/layout/Sidebar/SidebarGroup.test.tsx index 687140277a..b59395efab 100644 --- a/packages/core-components/src/layout/Sidebar/SidebarGroup.test.tsx +++ b/packages/core-components/src/layout/Sidebar/SidebarGroup.test.tsx @@ -14,7 +14,8 @@ * limitations under the License. */ -import { mockBreakpoint, renderInTestApp } from '@backstage/test-utils'; +import { mockBreakpoint } from '@backstage/core-components/testUtils'; +import { renderInTestApp } from '@backstage/test-utils'; import HomeIcon from '@material-ui/icons/Home'; import LayersIcon from '@material-ui/icons/Layers'; import LibraryBooks from '@material-ui/icons/LibraryBooks'; diff --git a/packages/core-components/src/testUtils.ts b/packages/core-components/src/testUtils.ts new file mode 100644 index 0000000000..17bb0db63b --- /dev/null +++ b/packages/core-components/src/testUtils.ts @@ -0,0 +1,43 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * This is a mocking method suggested in the Jest docs, as it is not implemented in JSDOM yet. + * It can be used to mock values for the Material UI `useMediaQuery` hook if it is used in a tested component. + * + * For issues checkout the documentation: + * https://jestjs.io/docs/manual-mocks#mocking-methods-which-are-not-implemented-in-jsdom + * + * If there are any updates from Material UI React on testing `useMediaQuery` this mock should be replaced + * https://mui.com/material-ui/react-use-media-query/#testing + * + * @public + */ +export function mockBreakpoint(options: { matches: boolean }) { + Object.defineProperty(window, 'matchMedia', { + writable: true, + value: jest.fn().mockImplementation(query => ({ + matches: options.matches ?? false, + media: query, + onchange: null, + addListener: jest.fn(), // deprecated + removeListener: jest.fn(), // deprecated + addEventListener: jest.fn(), + removeEventListener: jest.fn(), + dispatchEvent: jest.fn(), + })), + }); +} diff --git a/packages/core-plugin-api/CHANGELOG.md b/packages/core-plugin-api/CHANGELOG.md index d80e3e9f89..eab6b0519b 100644 --- a/packages/core-plugin-api/CHANGELOG.md +++ b/packages/core-plugin-api/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/core-plugin-api +## 1.8.1-next.1 + +### Patch Changes + +- 0c93dc37b2: The `createTranslationRef` function from the `/alpha` subpath can now also accept a nested object structure of default translation messages, which will be flatted using `.` separators. +- Updated dependencies + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.7 + ## 1.8.1-next.0 ### Patch Changes diff --git a/packages/core-plugin-api/alpha-api-report.md b/packages/core-plugin-api/api-report-alpha.md similarity index 92% rename from packages/core-plugin-api/alpha-api-report.md rename to packages/core-plugin-api/api-report-alpha.md index ba3583142f..c0f0d02535 100644 --- a/packages/core-plugin-api/alpha-api-report.md +++ b/packages/core-plugin-api/api-report-alpha.md @@ -39,19 +39,17 @@ export function createTranslationMessages< // @alpha (undocumented) export function createTranslationRef< TId extends string, - const TMessages extends { - [key in string]: string; - }, + const TNestedMessages extends AnyNestedMessages, TTranslations extends { [language in string]: () => Promise<{ default: { - [key in keyof TMessages]: string | null; + [key in keyof FlattenedMessages]: string | null; }; }>; }, >( - config: TranslationRefOptions, -): TranslationRef; + config: TranslationRefOptions, +): TranslationRef>; // @alpha (undocumented) export function createTranslationResource< @@ -169,13 +167,11 @@ export interface TranslationRef< // @alpha (undocumented) export interface TranslationRefOptions< TId extends string, - TMessages extends { - [key in string]: string; - }, + TNestedMessages extends AnyNestedMessages, TTranslations extends { [language in string]: () => Promise<{ default: { - [key in keyof TMessages]: string | null; + [key in keyof FlattenedMessages]: string | null; }; }>; }, @@ -183,7 +179,7 @@ export interface TranslationRefOptions< // (undocumented) id: TId; // (undocumented) - messages: TMessages; + messages: TNestedMessages; // (undocumented) translations?: TTranslations; } diff --git a/packages/core-plugin-api/package.json b/packages/core-plugin-api/package.json index 98286779f8..3b0bc1f34b 100644 --- a/packages/core-plugin-api/package.json +++ b/packages/core-plugin-api/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/core-plugin-api", "description": "Core API used by Backstage plugins", - "version": "1.8.1-next.0", + "version": "1.8.1-next.1", "publishConfig": { "access": "public" }, diff --git a/packages/core-plugin-api/src/translation/TranslationRef.test.ts b/packages/core-plugin-api/src/translation/TranslationRef.test.ts index b73b521774..d1fde0810f 100644 --- a/packages/core-plugin-api/src/translation/TranslationRef.test.ts +++ b/packages/core-plugin-api/src/translation/TranslationRef.test.ts @@ -34,6 +34,40 @@ describe('TranslationRefImpl', () => { expect(internalRef.getDefaultMessages()).toEqual({ key: 'value' }); }); + it('should create a TranslationRef instance with nested messages', () => { + const ref = createTranslationRef({ + id: 'test', + messages: { + key: 'value', + 'nested.conflict1': 'outer conflict1', + nested: { + key: 'nested value', + key2: 'nested value2', + conflict1: 'inner conflict1', + conflict2: 'inner conflict2', + inner: { + key: 'inner value', + }, + }, + 'nested.conflict2': 'outer conflict2', + }, + }); + + const internalRef = toInternalTranslationRef(ref); + + expect(internalRef.$$type).toBe('@backstage/TranslationRef'); + expect(internalRef.version).toBe('v1'); + expect(internalRef.id).toBe('test'); + expect(internalRef.getDefaultMessages()).toEqual({ + key: 'value', + 'nested.key': 'nested value', + 'nested.key2': 'nested value2', + 'nested.conflict1': 'inner conflict1', + 'nested.inner.key': 'inner value', + 'nested.conflict2': 'outer conflict2', + }); + }); + it('should be created with lazy translations', async () => { const ref = createTranslationRef({ id: 'test', diff --git a/packages/core-plugin-api/src/translation/TranslationRef.ts b/packages/core-plugin-api/src/translation/TranslationRef.ts index 345a9c3f65..c2ac7a5ce3 100644 --- a/packages/core-plugin-api/src/translation/TranslationRef.ts +++ b/packages/core-plugin-api/src/translation/TranslationRef.ts @@ -34,6 +34,43 @@ export interface TranslationRef< /** @internal */ type AnyMessages = { [key in string]: string }; +/** @ignore */ +type AnyNestedMessages = { [key in string]: AnyNestedMessages | string }; + +/** + * Flattens a nested message declaration into a flat object with dot-separated keys. + * + * @ignore + */ +type FlattenedMessages = + // Flatten out object keys into a union structure of objects, e.g. { a: 'a', b: 'b' } -> { a: 'a' } | { b: 'b' } + // Any nested object will be flattened into the individual unions, e.g. { a: 'a', b: { x: 'x', y: 'y' } } -> { a: 'a' } | { 'b.x': 'x', 'b.y': 'y' } + // We create this structure by first nesting the desired union types into the original object, and + // then extract them by indexing with `keyof TMessages` to form the union. + // Throughout this the objects are wrapped up in a function parameter, which allows us to have the + // final step of flipping this unions around to an intersection by inferring the function parameter. + { + [TKey in keyof TMessages]: ( + _: TMessages[TKey] extends infer TValue // "local variable" for the value + ? TValue extends AnyNestedMessages + ? FlattenedMessages extends infer TNested // Recurse into nested messages, "local variable" for the result + ? { + [TNestedKey in keyof TNested as `${TKey & string}.${TNestedKey & + string}`]: TNested[TNestedKey]; + } + : never + : { [_ in TKey]: TValue } // Primitive object values are passed through with the same key + : never, + ) => void; + // The `[keyof TMessages]` extracts the object values union from our flattened structure, still wrapped up in function parameters. + // The `extends (_: infer TIntersection) => void` flips the union to an intersection, at which point we have the correct type. + }[keyof TMessages] extends (_: infer TIntersection) => void + ? // This object mapping just expands similar to the Expand<> utility type, providing nicer type hints + { + readonly [TExpandKey in keyof TIntersection]: TIntersection[TExpandKey]; + } + : never; + /** @internal */ export interface InternalTranslationRef< TId extends string = string, @@ -49,31 +86,53 @@ export interface InternalTranslationRef< /** @alpha */ export interface TranslationRefOptions< TId extends string, - TMessages extends { [key in string]: string }, + TNestedMessages extends AnyNestedMessages, TTranslations extends { [language in string]: () => Promise<{ - default: { [key in keyof TMessages]: string | null }; + default: { + [key in keyof FlattenedMessages]: string | null; + }; }>; }, > { id: TId; - messages: TMessages; + messages: TNestedMessages; translations?: TTranslations; } +function flattenMessages(nested: AnyNestedMessages): AnyMessages { + const entries = new Array<[string, string]>(); + + function visit(obj: AnyNestedMessages, prefix: string): void { + for (const [key, value] of Object.entries(obj)) { + if (typeof value === 'string') { + entries.push([prefix + key, value]); + } else { + visit(value, `${prefix}${key}.`); + } + } + } + + visit(nested, ''); + + return Object.fromEntries(entries); +} + /** @internal */ class TranslationRefImpl< TId extends string, - TMessages extends { [key in string]: string }, -> implements InternalTranslationRef + TNestedMessages extends AnyNestedMessages, +> implements InternalTranslationRef> { #id: TId; - #messages: TMessages; + #messages: FlattenedMessages; #resources: TranslationResource | undefined; - constructor(options: TranslationRefOptions) { + constructor(options: TranslationRefOptions) { this.#id = options.id; - this.#messages = options.messages; + this.#messages = flattenMessages( + options.messages, + ) as FlattenedMessages; } $$type = '@backstage/TranslationRef' as const; @@ -108,15 +167,17 @@ class TranslationRefImpl< /** @alpha */ export function createTranslationRef< TId extends string, - const TMessages extends { [key in string]: string }, + const TNestedMessages extends AnyNestedMessages, TTranslations extends { [language in string]: () => Promise<{ - default: { [key in keyof TMessages]: string | null }; + default: { + [key in keyof FlattenedMessages]: string | null; + }; }>; }, >( - config: TranslationRefOptions, -): TranslationRef { + config: TranslationRefOptions, +): TranslationRef> { const ref = new TranslationRefImpl(config); if (config.translations) { ref.setDefaultResource( @@ -132,7 +193,7 @@ export function createTranslationRef< /** @internal */ export function toInternalTranslationRef< TId extends string, - TMessages extends { [key in string]: string }, + TMessages extends AnyMessages, >(ref: TranslationRef): InternalTranslationRef { const r = ref as InternalTranslationRef; if (r.$$type !== '@backstage/TranslationRef') { diff --git a/packages/create-app/CHANGELOG.md b/packages/create-app/CHANGELOG.md index 74c2c3b7ce..9d7ccf5dce 100644 --- a/packages/create-app/CHANGELOG.md +++ b/packages/create-app/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/create-app +## 0.5.8-next.2 + +### Patch Changes + +- 375b6f7d68: CircelCI plugin moved permanently +- Updated dependencies + - @backstage/cli-common@0.1.13 + ## 0.5.8-next.1 ### Patch Changes diff --git a/packages/create-app/package.json b/packages/create-app/package.json index 52f075adc5..06fb467244 100644 --- a/packages/create-app/package.json +++ b/packages/create-app/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/create-app", "description": "A CLI that helps you create your own Backstage app", - "version": "0.5.8-next.1", + "version": "0.5.8-next.2", "publishConfig": { "access": "public" }, diff --git a/packages/create-app/src/lib/versions.ts b/packages/create-app/src/lib/versions.ts index 81bae3c0b8..03ef32d921 100644 --- a/packages/create-app/src/lib/versions.ts +++ b/packages/create-app/src/lib/versions.ts @@ -58,7 +58,6 @@ import { version as pluginCatalogBackend } from '../../../../plugins/catalog-bac import { version as pluginCatalogBackendModuleScaffolderEntityModel } from '../../../../plugins/catalog-backend-module-scaffolder-entity-model/package.json'; import { version as pluginCatalogGraph } from '../../../../plugins/catalog-graph/package.json'; import { version as pluginCatalogImport } from '../../../../plugins/catalog-import/package.json'; -import { version as pluginCircleci } from '../../../../plugins/circleci/package.json'; import { version as pluginExplore } from '../../../../plugins/explore/package.json'; import { version as pluginGithubActions } from '../../../../plugins/github-actions/package.json'; import { version as pluginLighthouse } from '../../../../plugins/lighthouse/package.json'; @@ -111,7 +110,6 @@ export const packageVersions = { pluginCatalogBackendModuleScaffolderEntityModel, '@backstage/plugin-catalog-graph': pluginCatalogGraph, '@backstage/plugin-catalog-import': pluginCatalogImport, - '@backstage/plugin-circleci': pluginCircleci, '@backstage/plugin-explore': pluginExplore, '@backstage/plugin-github-actions': pluginGithubActions, '@backstage/plugin-lighthouse': pluginLighthouse, diff --git a/packages/dev-utils/CHANGELOG.md b/packages/dev-utils/CHANGELOG.md index 485dc687ab..49c5a7785d 100644 --- a/packages/dev-utils/CHANGELOG.md +++ b/packages/dev-utils/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/dev-utils +## 1.0.25-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/core-app-api@1.11.2-next.1 + - @backstage/app-defaults@1.4.6-next.1 + - @backstage/integration-react@1.1.22-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/theme@0.5.0-next.0 + ## 1.0.25-next.0 ### Patch Changes diff --git a/packages/dev-utils/package.json b/packages/dev-utils/package.json index ddcb29bdc3..d7ca26d64f 100644 --- a/packages/dev-utils/package.json +++ b/packages/dev-utils/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/dev-utils", "description": "Utilities for developing Backstage plugins.", - "version": "1.0.25-next.0", + "version": "1.0.25-next.1", "publishConfig": { "access": "public", "main": "dist/index.esm.js", diff --git a/packages/e2e-test-utils/playwright-api-report.md b/packages/e2e-test-utils/api-report-playwright.md similarity index 100% rename from packages/e2e-test-utils/playwright-api-report.md rename to packages/e2e-test-utils/api-report-playwright.md diff --git a/packages/e2e-test/CHANGELOG.md b/packages/e2e-test/CHANGELOG.md index 053825aae0..6b39139bac 100644 --- a/packages/e2e-test/CHANGELOG.md +++ b/packages/e2e-test/CHANGELOG.md @@ -1,5 +1,14 @@ # e2e-test +## 0.2.10-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/create-app@0.5.8-next.2 + - @backstage/cli-common@0.1.13 + - @backstage/errors@1.2.3 + ## 0.2.10-next.1 ### Patch Changes diff --git a/packages/e2e-test/package.json b/packages/e2e-test/package.json index 35eec664f9..9c78471d5c 100644 --- a/packages/e2e-test/package.json +++ b/packages/e2e-test/package.json @@ -1,7 +1,7 @@ { "name": "e2e-test", "description": "E2E test for verifying Backstage packages", - "version": "0.2.10-next.1", + "version": "0.2.10-next.2", "private": true, "backstage": { "role": "cli" diff --git a/packages/eslint-plugin/CHANGELOG.md b/packages/eslint-plugin/CHANGELOG.md index 56ed54388a..6335f22f3c 100644 --- a/packages/eslint-plugin/CHANGELOG.md +++ b/packages/eslint-plugin/CHANGELOG.md @@ -1,5 +1,11 @@ # @backstage/eslint-plugin +## 0.1.4-next.0 + +### Patch Changes + +- 107dc46ab1: The `no-undeclared-imports` rule will now prefer using version queries that already exist en the repo for the same dependency type when installing new packages. + ## 0.1.3 ### Patch Changes diff --git a/packages/eslint-plugin/package.json b/packages/eslint-plugin/package.json index 34a8205868..656ec78580 100644 --- a/packages/eslint-plugin/package.json +++ b/packages/eslint-plugin/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/eslint-plugin", "description": "Backstage ESLint plugin", - "version": "0.1.3", + "version": "0.1.4-next.0", "publishConfig": { "access": "public" }, diff --git a/packages/frontend-app-api/CHANGELOG.md b/packages/frontend-app-api/CHANGELOG.md index a933f6a649..216360658c 100644 --- a/packages/frontend-app-api/CHANGELOG.md +++ b/packages/frontend-app-api/CHANGELOG.md @@ -1,5 +1,28 @@ # @backstage/frontend-app-api +## 0.4.0-next.1 + +### Minor Changes + +- e539735435: Updated core extension structure to make space for the sign-in page by adding `core.router`. + +### Patch Changes + +- 5eb6b8a7bc: Added the nav logo extension for customization of sidebar logo +- 1f12fb762c: Create a core components extension that allows adopters to override core app components such as `Progress`, `BootErrorPage`, `NotFoundErrorPage` and `ErrorBoundaryFallback`. +- 59709286b3: Collect and register feature flags from plugins and extension overrides. +- f27ee7d937: Migrate analytics route tracker component. +- a5a04739e1: Updates to provide `node` to extension factories instead of `id` and `source`. +- Updated dependencies + - @backstage/frontend-plugin-api@0.4.0-next.1 + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/core-app-api@1.11.2-next.1 + - @backstage/config@1.1.1 + - @backstage/theme@0.5.0-next.0 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.7 + ## 0.3.1-next.0 ### Patch Changes diff --git a/packages/frontend-app-api/package.json b/packages/frontend-app-api/package.json index 7a32004273..5a179ef9f0 100644 --- a/packages/frontend-app-api/package.json +++ b/packages/frontend-app-api/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/frontend-app-api", - "version": "0.3.1-next.0", + "version": "0.4.0-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/packages/frontend-app-api/src/apis/implementations/ComponentsApi/ComponentsApi.ts b/packages/frontend-app-api/src/apis/implementations/ComponentsApi/ComponentsApi.ts new file mode 100644 index 0000000000..50d125e4ab --- /dev/null +++ b/packages/frontend-app-api/src/apis/implementations/ComponentsApi/ComponentsApi.ts @@ -0,0 +1,38 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ComponentRef, ComponentsApi } from '@backstage/frontend-plugin-api'; + +/** + * Implementation for the {@linkComponentApi} + * + * @internal + */ +export class DefaultComponentsApi implements ComponentsApi { + #components: Map, any>; + + constructor(components: Map, any>) { + this.#components = components; + } + + getComponent(ref: ComponentRef): T { + const impl = this.#components.get(ref); + if (!impl) { + throw new Error(`No implementation found for component ref ${ref}`); + } + return impl; + } +} diff --git a/packages/frontend-app-api/src/apis/implementations/ComponentsApi/index.ts b/packages/frontend-app-api/src/apis/implementations/ComponentsApi/index.ts new file mode 100644 index 0000000000..f04059c54f --- /dev/null +++ b/packages/frontend-app-api/src/apis/implementations/ComponentsApi/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { DefaultComponentsApi } from './ComponentsApi'; diff --git a/packages/frontend-app-api/src/extensions/Core.tsx b/packages/frontend-app-api/src/extensions/Core.tsx index 60772d435c..d4b3b65468 100644 --- a/packages/frontend-app-api/src/extensions/Core.tsx +++ b/packages/frontend-app-api/src/extensions/Core.tsx @@ -21,7 +21,7 @@ import { } from '@backstage/frontend-plugin-api'; export const Core = createExtension({ - id: 'core', + namespace: 'core', attachTo: { id: 'root', input: 'default' }, // ignored inputs: { apis: createExtensionInput({ @@ -30,6 +30,9 @@ export const Core = createExtension({ themes: createExtensionInput({ theme: coreExtensionData.theme, }), + components: createExtensionInput({ + component: coreExtensionData.component, + }), root: createExtensionInput( { element: coreExtensionData.reactElement, @@ -42,7 +45,7 @@ export const Core = createExtension({ }, factory({ inputs }) { return { - root: inputs.root.element, + root: inputs.root.output.element, }; }, }); diff --git a/packages/frontend-app-api/src/extensions/CoreLayout.tsx b/packages/frontend-app-api/src/extensions/CoreLayout.tsx index 0099b67751..e4f86256f2 100644 --- a/packages/frontend-app-api/src/extensions/CoreLayout.tsx +++ b/packages/frontend-app-api/src/extensions/CoreLayout.tsx @@ -23,8 +23,9 @@ import { import { SidebarPage } from '@backstage/core-components'; export const CoreLayout = createExtension({ - id: 'core.layout', - attachTo: { id: 'core.router', input: 'children' }, + namespace: 'core', + name: 'layout', + attachTo: { id: 'core/router', input: 'children' }, inputs: { nav: createExtensionInput( { @@ -46,8 +47,8 @@ export const CoreLayout = createExtension({ return { element: ( - {inputs.nav.element} - {inputs.content.element} + {inputs.nav.output.element} + {inputs.content.output.element} ), }; diff --git a/packages/frontend-app-api/src/extensions/CoreNav.tsx b/packages/frontend-app-api/src/extensions/CoreNav.tsx index 8d0d4ea5de..ab9f29a15d 100644 --- a/packages/frontend-app-api/src/extensions/CoreNav.tsx +++ b/packages/frontend-app-api/src/extensions/CoreNav.tsx @@ -75,8 +75,9 @@ const SidebarNavItem = (props: NavTarget) => { }; export const CoreNav = createExtension({ - id: 'core.nav', - attachTo: { id: 'core.layout', input: 'nav' }, + namespace: 'core', + name: 'nav', + attachTo: { id: 'core/layout', input: 'nav' }, inputs: { items: createExtensionInput({ target: coreExtensionData.navTarget, @@ -98,10 +99,10 @@ export const CoreNav = createExtension({ return { element: ( - + {inputs.items.map((item, index) => ( - + ))} ), diff --git a/packages/frontend-app-api/src/extensions/CoreRouter.tsx b/packages/frontend-app-api/src/extensions/CoreRouter.tsx index f128a246e6..4dc73c91af 100644 --- a/packages/frontend-app-api/src/extensions/CoreRouter.tsx +++ b/packages/frontend-app-api/src/extensions/CoreRouter.tsx @@ -33,9 +33,11 @@ import { AppIdentityProxy } from '../../../core-app-api/src/apis/implementations import { BrowserRouter } from 'react-router-dom'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports import { signInPageComponentDataRef } from '../../../frontend-plugin-api/src/extensions/createSignInPageExtension'; +import { RouteTracker } from '../routing/RouteTracker'; export const CoreRouter = createExtension({ - id: 'core.router', + namespace: 'core', + name: 'router', attachTo: { id: 'core', input: 'root' }, inputs: { signInPage: createExtensionInput( @@ -57,8 +59,8 @@ export const CoreRouter = createExtension({ factory({ inputs }) { return { element: ( - - {inputs.children.element} + + {inputs.children.output.element} ), }; @@ -132,7 +134,7 @@ export function AppRouter(props: AppRouterProps) { if (!internalAppContext) { throw new Error('AppRouter must be rendered within the AppProvider'); } - const { appIdentityProxy } = internalAppContext; + const { routeObjects, appIdentityProxy } = internalAppContext; // If the app hasn't configured a sign-in page, we just continue as guest. if (!SignInPageComponent) { @@ -159,11 +161,17 @@ export function AppRouter(props: AppRouterProps) { { signOutTargetUrl: basePath || '/' }, ); - return {children}; + return ( + + + {children} + + ); } return ( + { - const element = useRoutes( - inputs.routes.map(route => ({ - path: `${route.path}/*`, - element: route.element, - })), + const componentsApi = useApi(componentsApiRef); + const NotFoundErrorPage = componentsApi.getComponent( + coreComponentsRefs.notFoundErrorPage, ); + const element = useRoutes([ + ...inputs.routes.map(route => ({ + path: `${route.output.path}/*`, + element: route.output.element, + })), + { + path: '*', + element: , + }, + ]); + return element; }; return { diff --git a/packages/frontend-app-api/src/extensions/components.tsx b/packages/frontend-app-api/src/extensions/components.tsx new file mode 100644 index 0000000000..fa9187a831 --- /dev/null +++ b/packages/frontend-app-api/src/extensions/components.tsx @@ -0,0 +1,43 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + createComponentExtension, + coreComponentsRefs, +} from '@backstage/frontend-plugin-api'; +// eslint-disable-next-line @backstage/no-relative-monorepo-imports +import { components as defaultComponents } from '../../../app-defaults/src/defaults'; +// eslint-disable-next-line @backstage/no-relative-monorepo-imports + +export const DefaultProgressComponent = createComponentExtension({ + ref: coreComponentsRefs.progress, + component: { sync: () => defaultComponents.Progress }, +}); + +export const DefaultBootErrorPageComponent = createComponentExtension({ + ref: coreComponentsRefs.bootErrorPage, + component: { sync: () => defaultComponents.BootErrorPage }, +}); + +export const DefaultNotFoundErrorPageComponent = createComponentExtension({ + ref: coreComponentsRefs.notFoundErrorPage, + component: { sync: () => defaultComponents.NotFoundErrorPage }, +}); + +export const DefaultErrorBoundaryComponent = createComponentExtension({ + ref: coreComponentsRefs.errorBoundaryFallback, + component: { sync: () => defaultComponents.ErrorBoundaryFallback }, +}); diff --git a/packages/frontend-app-api/src/routing/RouteTracker.test.tsx b/packages/frontend-app-api/src/routing/RouteTracker.test.tsx new file mode 100644 index 0000000000..0e66d0e50d --- /dev/null +++ b/packages/frontend-app-api/src/routing/RouteTracker.test.tsx @@ -0,0 +1,254 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { TestApiProvider } from '@backstage/test-utils'; +import React, { useEffect } from 'react'; +import { BackstageRouteObject } from './types'; +import { fireEvent, render } from '@testing-library/react'; +import { RouteTracker } from './RouteTracker'; +import { Link, MemoryRouter, Route, Routes } from 'react-router-dom'; +import { createPlugin } from '@backstage/core-plugin-api'; +import { + createRouteRef, + AnalyticsApi, + analyticsApiRef, + AppNode, + useAnalytics, +} from '@backstage/frontend-plugin-api'; +import { MATCH_ALL_ROUTE } from './extractRouteInfoFromAppNode'; + +describe('RouteTracker', () => { + const routeRef0 = createRouteRef(); + const routeRef1 = createRouteRef(); + const routeRef2 = createRouteRef(); + const plugin0 = createPlugin({ id: 'home' }); + const plugin1 = createPlugin({ id: 'plugin1' }); + const plugin2 = createPlugin({ id: 'plugin2' }); + + const routeObjects: BackstageRouteObject[] = [ + { + path: '', + element:
home page
, + routeRefs: new Set([routeRef0]), + plugins: new Set([plugin0]), + caseSensitive: false, + children: [MATCH_ALL_ROUTE], + appNode: { + spec: { extension: { id: 'home.page.index' }, source: { id: 'home' } }, + } as AppNode, + }, + { + path: '/path/:p1/:p2', + element: go, + routeRefs: new Set([routeRef1]), + plugins: new Set([plugin1]), + caseSensitive: false, + children: [MATCH_ALL_ROUTE], + appNode: { + spec: { + extension: { id: 'plugin1.page.index' }, + source: { id: 'plugin1' }, + }, + } as AppNode, + }, + { + path: '/path2/:param', + element:
hi there
, + routeRefs: new Set([routeRef2]), + plugins: new Set([plugin2]), + caseSensitive: false, + children: [MATCH_ALL_ROUTE], + appNode: { + spec: { + extension: { id: 'plugin2.page.index' }, + source: { id: 'plugin2' }, + }, + } as AppNode, + }, + ]; + + const mockedAnalytics: jest.Mocked = { + captureEvent: jest.fn(), + }; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should capture the navigate event on load', async () => { + render( + + + + + , + ); + + expect(mockedAnalytics.captureEvent).toHaveBeenCalledWith({ + action: 'navigate', + attributes: { + p1: 'foo', + p2: 'bar', + }, + context: { + extensionId: 'plugin1.page.index', + pluginId: 'plugin1', + }, + subject: '/path/foo/bar', + value: undefined, + }); + }); + + it('should capture the navigate event on route change', async () => { + const { getByText } = render( + + + + + + {routeObjects.map(({ path, element }) => ( + + ))} + + + , + ); + + fireEvent.click(getByText('go')); + + expect(mockedAnalytics.captureEvent).toHaveBeenCalledWith({ + action: 'navigate', + attributes: { + param: 'hello', + }, + context: { + extensionId: 'plugin2.page.index', + pluginId: 'plugin2', + }, + subject: '/path2/hello', + value: undefined, + }); + }); + + it('should capture path query and hash', async () => { + render( + + + + + , + ); + + expect(mockedAnalytics.captureEvent).toHaveBeenCalledWith({ + action: 'navigate', + attributes: { + p1: 'foo', + p2: 'bar', + }, + context: { + extensionId: 'plugin1.page.index', + pluginId: 'plugin1', + }, + subject: '/path/foo/bar?q=1#header-1', + value: undefined, + }); + }); + + it('should match the root path and send relevant context', async () => { + render( + + + + + , + ); + + expect(mockedAnalytics.captureEvent).toHaveBeenCalledWith({ + action: 'navigate', + attributes: {}, + context: { + extensionId: 'home.page.index', + pluginId: 'home', + }, + subject: '/', + value: undefined, + }); + }); + + it('should return default context when it would have otherwise matched on the root path', async () => { + const Dummy = () => { + const analytics = useAnalytics(); + useEffect(() => { + analytics.captureEvent('click', 'test', {}); + }, [analytics]); + return
dummy
; + }; + + render( + + + + + } /> + + + , + ); + + expect(mockedAnalytics.captureEvent).toHaveBeenNthCalledWith(1, { + action: 'navigate', + attributes: {}, + context: { + extensionId: 'App', + pluginId: 'root', + }, + subject: '/not-routable-extension', + value: undefined, + }); + expect(mockedAnalytics.captureEvent).toHaveBeenNthCalledWith(2, { + action: 'click', + attributes: undefined, + context: { + extensionId: 'App', + pluginId: 'root', + }, + subject: 'test', + value: undefined, + }); + }); + + it('should return parent route context on navigating to a sub-route', async () => { + render( + + + + + , + ); + + expect(mockedAnalytics.captureEvent).toHaveBeenCalledWith({ + action: 'navigate', + attributes: { + param: 'param-value', + }, + context: { + extensionId: 'plugin2.page.index', + pluginId: 'plugin2', + }, + subject: '/path2/param-value/sub-route', + value: undefined, + }); + }); +}); diff --git a/packages/frontend-app-api/src/routing/RouteTracker.tsx b/packages/frontend-app-api/src/routing/RouteTracker.tsx new file mode 100644 index 0000000000..8cddca5c68 --- /dev/null +++ b/packages/frontend-app-api/src/routing/RouteTracker.tsx @@ -0,0 +1,129 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React, { useEffect } from 'react'; +import { matchRoutes, useLocation } from 'react-router-dom'; +import { + useAnalytics, + AnalyticsContext, + AnalyticsEventAttributes, +} from '@backstage/frontend-plugin-api'; +import { BackstageRouteObject } from './types'; + +/** + * Returns an extension context given the current pathname and a list of + * Backstage route objects. + */ +const getExtensionContext = ( + pathname: string, + routes: BackstageRouteObject[], +) => { + try { + // Find matching routes for the given path name. + const matches = matchRoutes(routes, { pathname }); + + // Of the matching routes, get the last (e.g. most specific) instance of + // the BackstageRouteObject that contains a routeRef. Filtering by routeRef + // ensures subRouteRefs are aligned to their parent routes' context. + const routeMatch = matches + ?.filter(match => match?.route.routeRefs?.size > 0) + .pop(); + const routeObject = routeMatch?.route; + + // If there is no route object, then allow inheritance of default context. + if (!routeObject) { + return undefined; + } + + // If the matched route is the root route (no path), and the pathname is + // not the path of the homepage, then inherit from the default context. + if (routeObject.path === '' && pathname !== '/') { + return undefined; + } + + const params = Object.entries( + routeMatch?.params || {}, + ).reduce((acc, [key, value]) => { + if (value !== undefined && key !== '*') { + acc[key] = value; + } + return acc; + }, {}); + + const plugin = routeObject.appNode?.spec.source; + const extension = routeObject.appNode?.spec.extension; + + return { + params, + pluginId: plugin?.id || 'root', + extensionId: extension?.id || 'App', + }; + } catch { + return undefined; + } +}; + +/** + * Performs the actual event capture on render. + */ +const TrackNavigation = ({ + pathname, + search, + hash, + attributes, +}: { + pathname: string; + search: string; + hash: string; + attributes?: AnalyticsEventAttributes; +}) => { + const analytics = useAnalytics(); + useEffect(() => { + analytics.captureEvent('navigate', `${pathname}${search}${hash}`, { + attributes, + }); + }, [analytics, pathname, search, hash, attributes]); + + return null; +}; + +/** + * Logs a "navigate" event with appropriate plugin-level analytics context + * attributes each time the user navigates to a page. + */ +export const RouteTracker = ({ + routeObjects, +}: { + routeObjects: BackstageRouteObject[]; +}) => { + const { pathname, search, hash } = useLocation(); + + const { params, ...attributes } = getExtensionContext( + pathname, + routeObjects, + ) || { params: {} }; + + return ( + + + + ); +}; diff --git a/packages/frontend-app-api/src/routing/extractRouteInfoFromAppNode.test.ts b/packages/frontend-app-api/src/routing/extractRouteInfoFromAppNode.test.ts index 112e1435ae..f0ce6ac590 100644 --- a/packages/frontend-app-api/src/routing/extractRouteInfoFromAppNode.test.ts +++ b/packages/frontend-app-api/src/routing/extractRouteInfoFromAppNode.test.ts @@ -19,7 +19,8 @@ import { BackstagePlugin } from '@backstage/core-plugin-api'; import { extractRouteInfoFromAppNode } from './extractRouteInfoFromAppNode'; import { AnyRouteRefParams, - Extension, + AppNode, + ExtensionDefinition, RouteRef, coreExtensionData, createExtension, @@ -29,11 +30,7 @@ import { } from '@backstage/frontend-plugin-api'; import { MockConfigApi } from '@backstage/test-utils'; import { createAppTree } from '../tree'; -import { Core } from '../extensions/Core'; -import { CoreRoutes } from '../extensions/CoreRoutes'; -import { CoreNav } from '../extensions/CoreNav'; -import { CoreLayout } from '../extensions/CoreLayout'; -import { CoreRouter } from '../extensions/CoreRouter'; +import { builtinExtensions } from '../wiring/createApp'; const ref1 = createRouteRef(); const ref2 = createRouteRef(); @@ -43,16 +40,16 @@ const ref5 = createRouteRef(); const refOrder: RouteRef[] = [ref1, ref2, ref3, ref4, ref5]; function createTestExtension(options: { - id: string; + name: string; parent?: string; path?: string; routeRef?: RouteRef; }) { return createExtension({ - id: options.id, + name: options.name, attachTo: options.parent - ? { id: options.parent, input: 'children' } - : { id: 'core.routes', input: 'routes' }, + ? { id: `test/${options.parent}`, input: 'children' } + : { id: 'core/routes', input: 'routes' }, output: { element: coreExtensionData.reactElement, path: coreExtensionData.routePath.optional(), @@ -73,14 +70,14 @@ function createTestExtension(options: { }); } -function routeInfoFromExtensions(extensions: Extension[]) { +function routeInfoFromExtensions(extensions: ExtensionDefinition[]) { const plugin = createPlugin({ id: 'test', extensions, }); const tree = createAppTree({ config: new MockConfigApi({}), - builtinExtensions: [Core, CoreRoutes, CoreNav, CoreLayout, CoreRouter], + builtinExtensions, features: [plugin], }); @@ -99,8 +96,10 @@ function routeObj( children: any[] = [], type: 'mounted' | 'gathered' = 'mounted', backstagePlugin?: BackstagePlugin, + appNode?: AppNode, ) { return { + appNode, path: path, caseSensitive: false, element: type, @@ -123,33 +122,33 @@ describe('discovery', () => { it('should collect routes', () => { const info = routeInfoFromExtensions([ createTestExtension({ - id: 'nothing', + name: 'nothing', path: 'nothing', }), createTestExtension({ - id: 'page1', + name: 'page1', path: 'foo', routeRef: ref1, }), createTestExtension({ - id: 'page2', + name: 'page2', parent: 'page1', path: 'bar/:id', routeRef: ref2, }), createTestExtension({ - id: 'page3', + name: 'page3', parent: 'page2', path: 'baz', routeRef: ref3, }), createTestExtension({ - id: 'page4', + name: 'page4', path: 'divsoup', routeRef: ref4, }), createTestExtension({ - id: 'page5', + name: 'page5', parent: 'page1', path: 'blop', routeRef: ref5, @@ -171,7 +170,14 @@ describe('discovery', () => { [ref5, ref1], ]); expect(info.routeObjects).toEqual([ - routeObj('nothing', []), + routeObj( + 'nothing', + [], + undefined, + undefined, + undefined, + expect.any(Object), + ), routeObj( 'foo', [ref1], @@ -179,45 +185,70 @@ describe('discovery', () => { routeObj( 'bar/:id', [ref2], - [routeObj('baz', [ref3], undefined, undefined, expect.any(Object))], + [ + routeObj( + 'baz', + [ref3], + undefined, + undefined, + expect.any(Object), + expect.any(Object), + ), + ], undefined, expect.any(Object), + expect.any(Object), + ), + routeObj( + 'blop', + [ref5], + undefined, + undefined, + expect.any(Object), + expect.any(Object), ), - routeObj('blop', [ref5], undefined, undefined, expect.any(Object)), ], undefined, expect.any(Object), + expect.any(Object), + ), + routeObj( + 'divsoup', + [ref4], + undefined, + undefined, + expect.any(Object), + expect.any(Object), ), - routeObj('divsoup', [ref4], undefined, undefined, expect.any(Object)), ]); }); it('should handle all react router Route patterns', () => { const info = routeInfoFromExtensions([ createTestExtension({ - id: 'page1', + name: 'page1', path: 'foo', routeRef: ref1, }), createTestExtension({ - id: 'page2', + name: 'page2', parent: 'page1', path: 'bar/:id', routeRef: ref2, }), createTestExtension({ - id: 'page3', + name: 'page3', path: 'baz', routeRef: ref3, }), createTestExtension({ - id: 'page4', + name: 'page4', parent: 'page3', path: 'divsoup', routeRef: ref4, }), createTestExtension({ - id: 'page5', + name: 'page5', parent: 'page3', path: 'blop', routeRef: ref5, @@ -243,29 +274,29 @@ describe('discovery', () => { it('should strip leading slashes in route paths', () => { const info = routeInfoFromExtensions([ createTestExtension({ - id: 'page1', + name: 'page1', path: '/foo', routeRef: ref1, }), createTestExtension({ - id: 'page2', + name: 'page2', parent: 'page1', path: '/bar/:id', routeRef: ref2, }), createTestExtension({ - id: 'page3', + name: 'page3', path: '/baz', routeRef: ref3, }), createTestExtension({ - id: 'page4', + name: 'page4', parent: 'page3', path: '/divsoup', routeRef: ref4, }), createTestExtension({ - id: 'page5', + name: 'page5', parent: 'page3', path: '/blop', routeRef: ref5, @@ -291,44 +322,44 @@ describe('discovery', () => { it('should use the route aggregator key to bind child routes to the same path', () => { const info = routeInfoFromExtensions([ createTestExtension({ - id: 'foo', + name: 'foo', path: 'foo', }), createTestExtension({ - id: 'page1', + name: 'page1', parent: 'foo', routeRef: ref1, }), createTestExtension({ - id: 'fooChild', + name: 'fooChild', parent: 'foo', }), createTestExtension({ - id: 'page2', + name: 'page2', parent: 'fooChild', routeRef: ref2, }), createTestExtension({ - id: 'fooEmpty', + name: 'fooEmpty', parent: 'foo', }), createTestExtension({ - id: 'page3', + name: 'page3', path: 'bar', routeRef: ref3, }), createTestExtension({ - id: 'page3Child', + name: 'page3Child', parent: 'page3', path: '', }), createTestExtension({ - id: 'page4', + name: 'page4', parent: 'page3Child', routeRef: ref4, }), createTestExtension({ - id: 'page5', + name: 'page5', parent: 'page4', routeRef: ref5, }), @@ -349,13 +380,30 @@ describe('discovery', () => { [ref5, ref3], ]); expect(info.routeObjects).toEqual([ - routeObj('foo', [ref1, ref2], [], 'mounted', expect.any(Object)), + routeObj( + 'foo', + [ref1, ref2], + [], + 'mounted', + expect.any(Object), + expect.any(Object), + ), routeObj( 'bar', [ref3], - [routeObj('', [ref4, ref5], [], 'mounted', expect.any(Object))], + [ + routeObj( + '', + [ref4, ref5], + [], + 'mounted', + expect.any(Object), + expect.any(Object), + ), + ], 'mounted', expect.any(Object), + expect.any(Object), ), ]); }); @@ -363,34 +411,34 @@ describe('discovery', () => { it('should use the route aggregator but stop when encountering explicit path', () => { const info = routeInfoFromExtensions([ createTestExtension({ - id: 'page1', + name: 'page1', path: 'foo', routeRef: ref1, }), createTestExtension({ - id: 'page1Child', + name: 'page1Child', parent: 'page1', path: 'bar', }), createTestExtension({ - id: 'page2', + name: 'page2', parent: 'page1Child', routeRef: ref2, }), createTestExtension({ - id: 'page3', + name: 'page3', parent: 'page2', path: 'baz', routeRef: ref3, }), createTestExtension({ - id: 'page4', + name: 'page4', parent: 'page3', path: '/blop', routeRef: ref4, }), createTestExtension({ - id: 'page5', + name: 'page5', parent: 'page2', routeRef: ref5, }), @@ -429,18 +477,22 @@ describe('discovery', () => { undefined, undefined, expect.any(Object), + expect.any(Object), ), ], undefined, expect.any(Object), + expect.any(Object), ), ], 'mounted', expect.any(Object), + expect.any(Object), ), ], undefined, expect.any(Object), + expect.any(Object), ), ]); }); @@ -448,34 +500,34 @@ describe('discovery', () => { it('should account for loose route paths', () => { const info = routeInfoFromExtensions([ createTestExtension({ - id: 'r', + name: 'r', path: 'r', }), createTestExtension({ - id: 'page1', + name: 'page1', parent: 'r', path: 'x', routeRef: ref1, }), createTestExtension({ - id: 'y', + name: 'y', path: 'y', parent: 'r', }), createTestExtension({ - id: 'page2', + name: 'page2', parent: 'y', path: '1', routeRef: ref2, }), createTestExtension({ - id: 'page3', + name: 'page3', parent: 'page2', path: 'a', routeRef: ref3, }), createTestExtension({ - id: 'page4', + name: 'page4', parent: 'page2', path: 'b', routeRef: ref4, @@ -499,7 +551,14 @@ describe('discovery', () => { 'r', [], [ - routeObj('x', [ref1], [], 'mounted', expect.any(Object)), + routeObj( + 'x', + [ref1], + [], + 'mounted', + expect.any(Object), + expect.any(Object), + ), routeObj( 'y', [], @@ -514,6 +573,7 @@ describe('discovery', () => { undefined, 'mounted', expect.any(Object), + expect.any(Object), ), routeObj( 'b', @@ -521,15 +581,22 @@ describe('discovery', () => { undefined, 'mounted', expect.any(Object), + expect.any(Object), ), ], 'mounted', expect.any(Object), + expect.any(Object), ), ], 'mounted', + undefined, + expect.any(Object), ), ], + undefined, + undefined, + expect.any(Object), ), ]); }); diff --git a/packages/frontend-app-api/src/routing/extractRouteInfoFromAppNode.ts b/packages/frontend-app-api/src/routing/extractRouteInfoFromAppNode.ts index 2bfd09a3e8..17afbeb4c9 100644 --- a/packages/frontend-app-api/src/routing/extractRouteInfoFromAppNode.ts +++ b/packages/frontend-app-api/src/routing/extractRouteInfoFromAppNode.ts @@ -84,6 +84,7 @@ export function extractRouteInfoFromAppNode(node: AppNode): { caseSensitive: false, children: [MATCH_ALL_ROUTE], plugins: new Set(), + appNode: current, }; parentChildren.push(currentObj); diff --git a/packages/frontend-app-api/src/routing/types.ts b/packages/frontend-app-api/src/routing/types.ts index 5f1ed9be84..05ea73dff8 100644 --- a/packages/frontend-app-api/src/routing/types.ts +++ b/packages/frontend-app-api/src/routing/types.ts @@ -15,6 +15,7 @@ */ import { + AppNode, ExternalRouteRef, RouteRef, SubRouteRef, @@ -35,4 +36,5 @@ export interface BackstageRouteObject { path: string; routeRefs: Set; plugins: Set; + appNode?: AppNode; } diff --git a/packages/frontend-app-api/src/tree/createAppTree.test.ts b/packages/frontend-app-api/src/tree/createAppTree.test.ts index 667e3a6fc3..15c8ab4750 100644 --- a/packages/frontend-app-api/src/tree/createAppTree.test.ts +++ b/packages/frontend-app-api/src/tree/createAppTree.test.ts @@ -54,12 +54,11 @@ describe('createAppTree', () => { it('throws an error when a core extension is overridden', () => { const config = new MockConfigApi({}); const features = [ - createPlugin({ - id: 'plugin', + createExtensionOverrides({ extensions: [ createExtension({ - id: 'core', - attachTo: { id: 'core.routes', input: 'route' }, + name: 'core', + attachTo: { id: 'core/routes', input: 'route' }, inputs: {}, output: {}, factory: () => ({}), @@ -70,33 +69,7 @@ describe('createAppTree', () => { expect(() => createAppTree({ features, config, builtinExtensions: [] }), ).toThrow( - "It is forbidden to override the following extension(s): 'core', which is done by the following plugin(s): 'plugin'", - ); - }); - - it('throws an error when duplicated extensions are detected', () => { - const config = new MockConfigApi({}); - - const ExtensionA = createExtension({ ...extBase, id: 'A' }); - - const ExtensionB = createExtension({ ...extBase, id: 'B' }); - - const PluginA = createPlugin({ - id: 'A', - extensions: [ExtensionA, ExtensionA], - }); - - const PluginB = createPlugin({ - id: 'B', - extensions: [ExtensionA, ExtensionB, ExtensionB], - }); - - const features = [PluginA, PluginB]; - - expect(() => - createAppTree({ features, config, builtinExtensions: [] }), - ).toThrow( - "The following extensions are duplicated: The extension 'A' was provided 2 time(s) by the plugin 'A' and 1 time(s) by the plugin 'B', The extension 'B' was provided 2 time(s) by the plugin 'B'", + "It is forbidden to override the following extension(s): 'core', which is done by one or more extension overrides", ); }); @@ -106,13 +79,13 @@ describe('createAppTree', () => { features: [ createExtensionOverrides({ extensions: [ - createExtension({ ...extBase, id: 'a' }), - createExtension({ ...extBase, id: 'a' }), - createExtension({ ...extBase, id: 'b' }), + createExtension({ ...extBase, name: 'a' }), + createExtension({ ...extBase, name: 'a' }), + createExtension({ ...extBase, name: 'b' }), ], }), createExtensionOverrides({ - extensions: [createExtension({ ...extBase, id: 'b' })], + extensions: [createExtension({ ...extBase, name: 'b' })], }), ], config: new MockConfigApi({}), diff --git a/packages/frontend-app-api/src/tree/instantiateAppNodeTree.test.ts b/packages/frontend-app-api/src/tree/instantiateAppNodeTree.test.ts index 802c71f729..0be4f0a0c4 100644 --- a/packages/frontend-app-api/src/tree/instantiateAppNodeTree.test.ts +++ b/packages/frontend-app-api/src/tree/instantiateAppNodeTree.test.ts @@ -28,28 +28,33 @@ import { } from './instantiateAppNodeTree'; import { AppNodeInstance, AppNodeSpec } from '@backstage/frontend-plugin-api'; import { resolveAppTree } from './resolveAppTree'; +// eslint-disable-next-line @backstage/no-relative-monorepo-imports +import { resolveExtensionDefinition } from '../../../frontend-plugin-api/src/wiring/resolveExtensionDefinition'; const testDataRef = createExtensionDataRef('test'); const otherDataRef = createExtensionDataRef('other'); const inputMirrorDataRef = createExtensionDataRef('mirror'); -const simpleExtension = createExtension({ - id: 'core.test', - attachTo: { id: 'ignored', input: 'ignored' }, - output: { - test: testDataRef, - other: otherDataRef.optional(), - }, - configSchema: createSchemaFromZod(z => - z.object({ - output: z.string().default('test'), - other: z.number().optional(), - }), - ), - factory({ config }) { - return { test: config.output, other: config.other }; - }, -}); +const simpleExtension = resolveExtensionDefinition( + createExtension({ + namespace: 'core', + name: 'test', + attachTo: { id: 'ignored', input: 'ignored' }, + output: { + test: testDataRef, + other: otherDataRef.optional(), + }, + configSchema: createSchemaFromZod(z => + z.object({ + output: z.string().default('test'), + other: z.number().optional(), + }), + ), + factory({ config }) { + return { test: config.output, other: config.other }; + }, + }), +); function makeSpec( extension: Extension, @@ -117,19 +122,21 @@ describe('instantiateAppNodeTree', () => { it('should instantiate a node with attachments', () => { const tree = resolveAppTree('root-node', [ makeSpec( - createExtension({ - id: 'root-node', - attachTo: { id: 'ignored', input: 'ignored' }, - inputs: { - test: createExtensionInput({ test: testDataRef }), - }, - output: { - inputMirror: inputMirrorDataRef, - }, - factory({ inputs }) { - return { inputMirror: inputs }; - }, - }), + resolveExtensionDefinition( + createExtension({ + namespace: 'root-node', + attachTo: { id: 'ignored', input: 'ignored' }, + inputs: { + test: createExtensionInput({ test: testDataRef }), + }, + output: { + inputMirror: inputMirrorDataRef, + }, + factory({ inputs }) { + return { inputMirror: inputs }; + }, + }), + ), ), makeSpec(simpleExtension, { id: 'child-node', @@ -146,7 +153,7 @@ describe('instantiateAppNodeTree', () => { expect(tree.root.instance).toBeDefined(); expect(childNode?.instance).toBeDefined(); expect(tree.root.instance?.getData(inputMirrorDataRef)).toEqual({ - test: [{ test: 'test' }], + test: [{ extensionId: 'child-node', output: { test: 'test' } }], }); // Multiple calls should have no effect @@ -159,19 +166,21 @@ describe('instantiateAppNodeTree', () => { const tree = resolveAppTree('root-node', [ { ...makeSpec( - createExtension({ - id: 'root-node', - attachTo: { id: 'ignored', input: 'ignored' }, - inputs: { - test: createExtensionInput({ test: testDataRef }), - }, - output: { - inputMirror: inputMirrorDataRef, - }, - factory({ inputs }) { - return { inputMirror: inputs }; - }, - }), + resolveExtensionDefinition( + createExtension({ + namespace: 'root-node', + attachTo: { id: 'ignored', input: 'ignored' }, + inputs: { + test: createExtensionInput({ test: testDataRef }), + }, + output: { + inputMirror: inputMirrorDataRef, + }, + factory({ inputs }) { + return { inputMirror: inputs }; + }, + }), + ), ), }, { @@ -242,51 +251,63 @@ describe('createAppNodeInstance', () => { const instance = createAppNodeInstance({ attachments, node: makeNode( - createExtension({ - id: 'core.test', - attachTo: { id: 'ignored', input: 'ignored' }, - inputs: { - optionalSingletonPresent: createExtensionInput( - { + resolveExtensionDefinition( + createExtension({ + namespace: 'core', + name: 'test', + attachTo: { id: 'ignored', input: 'ignored' }, + inputs: { + optionalSingletonPresent: createExtensionInput( + { + test: testDataRef, + other: otherDataRef.optional(), + }, + { singleton: true, optional: true }, + ), + optionalSingletonMissing: createExtensionInput( + { + test: testDataRef, + other: otherDataRef.optional(), + }, + { singleton: true, optional: true }, + ), + singleton: createExtensionInput( + { + test: testDataRef, + other: otherDataRef.optional(), + }, + { singleton: true }, + ), + many: createExtensionInput({ test: testDataRef, other: otherDataRef.optional(), - }, - { singleton: true, optional: true }, - ), - optionalSingletonMissing: createExtensionInput( - { - test: testDataRef, - other: otherDataRef.optional(), - }, - { singleton: true, optional: true }, - ), - singleton: createExtensionInput( - { - test: testDataRef, - other: otherDataRef.optional(), - }, - { singleton: true }, - ), - many: createExtensionInput({ - test: testDataRef, - other: otherDataRef.optional(), - }), - }, - output: { - inputMirror: inputMirrorDataRef, - }, - factory({ inputs }) { - return { inputMirror: inputs }; - }, - }), + }), + }, + output: { + inputMirror: inputMirrorDataRef, + }, + factory({ inputs }) { + return { inputMirror: inputs }; + }, + }), + ), ), }); expect(Array.from(instance.getDataRefs())).toEqual([inputMirrorDataRef]); expect(instance.getData(inputMirrorDataRef)).toEqual({ - optionalSingletonPresent: { test: 'optionalSingletonPresent' }, - singleton: { test: 'singleton', other: 2 }, - many: [{ test: 'many1' }, { test: 'many2', other: 3 }], + optionalSingletonPresent: { + extensionId: 'core/test', + output: { test: 'optionalSingletonPresent' }, + }, + singleton: { + extensionId: 'core/test', + output: { test: 'singleton', other: 2 }, + }, + many: [ + { extensionId: 'core/test', output: { test: 'many1' } }, + { extensionId: 'core/test', output: { test: 'many2', other: 3 } }, + ], }); }); @@ -297,7 +318,7 @@ describe('createAppNodeInstance', () => { attachments: new Map(), }), ).toThrow( - "Invalid configuration for extension 'core.test'; caused by Error: Expected number, received string at 'other'", + "Invalid configuration for extension 'core/test'; caused by Error: Expected number, received string at 'other'", ); }); @@ -305,21 +326,24 @@ describe('createAppNodeInstance', () => { expect(() => createAppNodeInstance({ node: makeNode( - createExtension({ - id: 'core.test', - attachTo: { id: 'ignored', input: 'ignored' }, - output: {}, - factory() { - const error = new Error('NOPE'); - error.name = 'NopeError'; - throw error; - }, - }), + resolveExtensionDefinition( + createExtension({ + namespace: 'core', + name: 'test', + attachTo: { id: 'ignored', input: 'ignored' }, + output: {}, + factory() { + const error = new Error('NOPE'); + error.name = 'NopeError'; + throw error; + }, + }), + ), ), attachments: new Map(), }), ).toThrow( - "Failed to instantiate extension 'core.test'; caused by NopeError: NOPE", + "Failed to instantiate extension 'core/test'; caused by NopeError: NOPE", ); }); @@ -327,22 +351,25 @@ describe('createAppNodeInstance', () => { expect(() => createAppNodeInstance({ node: makeNode( - createExtension({ - id: 'core.test', - attachTo: { id: 'ignored', input: 'ignored' }, - output: { - test1: testDataRef, - test2: testDataRef, - }, - factory({}) { - return { test1: 'test', test2: 'test2' }; - }, - }), + resolveExtensionDefinition( + createExtension({ + namespace: 'core', + name: 'test', + attachTo: { id: 'ignored', input: 'ignored' }, + output: { + test1: testDataRef, + test2: testDataRef, + }, + factory({}) { + return { test1: 'test', test2: 'test2' }; + }, + }), + ), ), attachments: new Map(), }), ).toThrow( - "Failed to instantiate extension 'core.test', duplicate extension data 'test' received via output 'test2'", + "Failed to instantiate extension 'core/test', duplicate extension data 'test' received via output 'test2'", ); }); @@ -350,21 +377,24 @@ describe('createAppNodeInstance', () => { expect(() => createAppNodeInstance({ node: makeNode( - createExtension({ - id: 'core.test', - attachTo: { id: 'ignored', input: 'ignored' }, - output: { - test: testDataRef, - }, - factory({}) { - return { nonexistent: 'test' } as any; - }, - }), + resolveExtensionDefinition( + createExtension({ + namespace: 'core', + name: 'test', + attachTo: { id: 'ignored', input: 'ignored' }, + output: { + test: testDataRef, + }, + factory({}) { + return { nonexistent: 'test' } as any; + }, + }), + ), ), attachments: new Map(), }), ).toThrow( - "Failed to instantiate extension 'core.test', unknown output provided via 'nonexistent'", + "Failed to instantiate extension 'core/test', unknown output provided via 'nonexistent'", ); }); @@ -372,25 +402,28 @@ describe('createAppNodeInstance', () => { expect(() => createAppNodeInstance({ node: makeNode( - createExtension({ - id: 'core.test', - attachTo: { id: 'ignored', input: 'ignored' }, - inputs: { - singleton: createExtensionInput( - { - test: testDataRef, - }, - { singleton: true }, - ), - }, - output: {}, - factory: () => ({}), - }), + resolveExtensionDefinition( + createExtension({ + namespace: 'core', + name: 'test', + attachTo: { id: 'ignored', input: 'ignored' }, + inputs: { + singleton: createExtensionInput( + { + test: testDataRef, + }, + { singleton: true }, + ), + }, + output: {}, + factory: () => ({}), + }), + ), ), attachments: new Map(), }), ).toThrow( - "Failed to instantiate extension 'core.test', input 'singleton' is required but was not received", + "Failed to instantiate extension 'core/test', input 'singleton' is required but was not received", ); }); @@ -416,21 +449,24 @@ describe('createAppNodeInstance', () => { ], ]), node: makeNode( - createExtension({ - id: 'core.test', - attachTo: { id: 'ignored', input: 'ignored' }, - inputs: { - declared: createExtensionInput({ - test: testDataRef, - }), - }, - output: {}, - factory: () => ({}), - }), + resolveExtensionDefinition( + createExtension({ + namespace: 'core', + name: 'test', + attachTo: { id: 'ignored', input: 'ignored' }, + inputs: { + declared: createExtensionInput({ + test: testDataRef, + }), + }, + output: {}, + factory: () => ({}), + }), + ), ), }), ).toThrow( - "Failed to instantiate extension 'core.test', received undeclared input 'undeclared' from extension 'core.test'", + "Failed to instantiate extension 'core/test', received undeclared input 'undeclared' from extension 'core/test'", ); }); @@ -451,16 +487,19 @@ describe('createAppNodeInstance', () => { ], ]), node: makeNode( - createExtension({ - id: 'core.test', - attachTo: { id: 'ignored', input: 'ignored' }, - output: {}, - factory: () => ({}), - }), + resolveExtensionDefinition( + createExtension({ + namespace: 'core', + name: 'test', + attachTo: { id: 'ignored', input: 'ignored' }, + output: {}, + factory: () => ({}), + }), + ), ), }), ).toThrow( - "Failed to instantiate extension 'core.test', received undeclared inputs 'undeclared1' from extension 'core.test' and 'undeclared2' from extensions 'core.test', 'core.test'", + "Failed to instantiate extension 'core/test', received undeclared inputs 'undeclared1' from extension 'core/test' and 'undeclared2' from extensions 'core/test', 'core/test'", ); }); @@ -477,24 +516,27 @@ describe('createAppNodeInstance', () => { ], ]), node: makeNode( - createExtension({ - id: 'core.test', - attachTo: { id: 'ignored', input: 'ignored' }, - inputs: { - singleton: createExtensionInput( - { - test: testDataRef, - }, - { singleton: true }, - ), - }, - output: {}, - factory: () => ({}), - }), + resolveExtensionDefinition( + createExtension({ + namespace: 'core', + name: 'test', + attachTo: { id: 'ignored', input: 'ignored' }, + inputs: { + singleton: createExtensionInput( + { + test: testDataRef, + }, + { singleton: true }, + ), + }, + output: {}, + factory: () => ({}), + }), + ), ), }), ).toThrow( - "Failed to instantiate extension 'core.test', expected exactly one 'singleton' input but received multiple: 'core.test', 'core.test'", + "Failed to instantiate extension 'core/test', expected exactly one 'singleton' input but received multiple: 'core/test', 'core/test'", ); }); @@ -511,24 +553,27 @@ describe('createAppNodeInstance', () => { ], ]), node: makeNode( - createExtension({ - id: 'core.test', - attachTo: { id: 'ignored', input: 'ignored' }, - inputs: { - singleton: createExtensionInput( - { - test: testDataRef, - }, - { singleton: true, optional: true }, - ), - }, - output: {}, - factory: () => ({}), - }), + resolveExtensionDefinition( + createExtension({ + namespace: 'core', + name: 'test', + attachTo: { id: 'ignored', input: 'ignored' }, + inputs: { + singleton: createExtensionInput( + { + test: testDataRef, + }, + { singleton: true, optional: true }, + ), + }, + output: {}, + factory: () => ({}), + }), + ), ), }), ).toThrow( - "Failed to instantiate extension 'core.test', expected at most one 'singleton' input but received multiple: 'core.test', 'core.test'", + "Failed to instantiate extension 'core/test', expected at most one 'singleton' input but received multiple: 'core/test', 'core/test'", ); }); @@ -539,24 +584,27 @@ describe('createAppNodeInstance', () => { ['singleton', [makeInstanceWithId(simpleExtension, undefined)]], ]), node: makeNode( - createExtension({ - id: 'core.test', - attachTo: { id: 'ignored', input: 'ignored' }, - inputs: { - singleton: createExtensionInput( - { - other: otherDataRef, - }, - { singleton: true }, - ), - }, - output: {}, - factory: () => ({}), - }), + resolveExtensionDefinition( + createExtension({ + namespace: 'core', + name: 'test', + attachTo: { id: 'ignored', input: 'ignored' }, + inputs: { + singleton: createExtensionInput( + { + other: otherDataRef, + }, + { singleton: true }, + ), + }, + output: {}, + factory: () => ({}), + }), + ), ), }), ).toThrow( - "Failed to instantiate extension 'core.test', input 'singleton' did not receive required extension data 'other' from extension 'core.test'", + "Failed to instantiate extension 'core/test', input 'singleton' did not receive required extension data 'other' from extension 'core/test'", ); }); }); diff --git a/packages/frontend-app-api/src/tree/instantiateAppNodeTree.ts b/packages/frontend-app-api/src/tree/instantiateAppNodeTree.ts index 2b1ab04c25..ca51f10921 100644 --- a/packages/frontend-app-api/src/tree/instantiateAppNodeTree.ts +++ b/packages/frontend-app-api/src/tree/instantiateAppNodeTree.ts @@ -18,6 +18,7 @@ import { AnyExtensionDataMap, AnyExtensionInputMap, ExtensionDataRef, + ResolvedExtensionInputs, } from '@backstage/frontend-plugin-api'; import mapValues from 'lodash/mapValues'; import { AppNode, AppNodeInstance } from '@backstage/frontend-plugin-api'; @@ -45,7 +46,7 @@ function resolveInputData( function resolveInputs( inputMap: AnyExtensionInputMap, attachments: ReadonlyMap, -) { +): ResolvedExtensionInputs { const undeclaredAttachments = Array.from(attachments.entries()).filter( ([inputName]) => inputMap[inputName] === undefined, ); @@ -84,13 +85,21 @@ function resolveInputs( } throw Error(`input '${inputName}' is required but was not received`); } - return resolveInputData(input.extensionData, attachedNodes[0], inputName); + return { + extensionId: attachedNodes[0].id, + output: resolveInputData( + input.extensionData, + attachedNodes[0], + inputName, + ), + }; } - return attachedNodes.map(attachment => - resolveInputData(input.extensionData, attachment, inputName), - ); - }); + return attachedNodes.map(attachment => ({ + extensionId: attachment.id, + output: resolveInputData(input.extensionData, attachment, inputName), + })); + }) as ResolvedExtensionInputs; } /** @internal */ @@ -135,7 +144,7 @@ export function createAppNodeInstance(options: { } catch (e) { throw new Error( `Failed to instantiate extension '${id}'${ - e.name === 'Error' ? `, ${e.message}` : `; caused by ${e}` + e.name === 'Error' ? `, ${e.message}` : `; caused by ${e.stack}` }`, ); } diff --git a/packages/frontend-app-api/src/tree/readAppExtensionsConfig.test.ts b/packages/frontend-app-api/src/tree/readAppExtensionsConfig.test.ts index 91f6992f29..504d5a6197 100644 --- a/packages/frontend-app-api/src/tree/readAppExtensionsConfig.test.ts +++ b/packages/frontend-app-api/src/tree/readAppExtensionsConfig.test.ts @@ -25,18 +25,18 @@ describe('readAppExtensionsConfig', () => { it('should disable extension with shorthand notation', () => { expect( readAppExtensionsConfig( - new ConfigReader({ app: { extensions: [{ 'core.router': false }] } }), + new ConfigReader({ app: { extensions: [{ 'core/router': false }] } }), ), ).toEqual([ { - id: 'core.router', + id: 'core/router', disabled: true, }, ]); expect( readAppExtensionsConfig( new ConfigReader({ - app: { extensions: [{ 'core.router': { disabled: true } }] }, + app: { extensions: [{ 'core/router': { disabled: true } }] }, }), ), ).toEqual([ @@ -44,7 +44,7 @@ describe('readAppExtensionsConfig', () => { at: undefined, config: undefined, disabled: true, - id: 'core.router', + id: 'core/router', }, ]); }); @@ -52,33 +52,33 @@ describe('readAppExtensionsConfig', () => { it('should enable extension with shorthand notation', () => { expect( readAppExtensionsConfig( - new ConfigReader({ app: { extensions: ['core.router'] } }), + new ConfigReader({ app: { extensions: ['core/router'] } }), ), ).toEqual([ { - id: 'core.router', + id: 'core/router', disabled: false, }, ]); expect( readAppExtensionsConfig( - new ConfigReader({ app: { extensions: [{ 'core.router': true }] } }), + new ConfigReader({ app: { extensions: [{ 'core/router': true }] } }), ), ).toEqual([ { - id: 'core.router', + id: 'core/router', disabled: false, }, ]); expect( readAppExtensionsConfig( new ConfigReader({ - app: { extensions: [{ 'core.router': { disabled: false } }] }, + app: { extensions: [{ 'core/router': { disabled: false } }] }, }), ), ).toEqual([ { - id: 'core.router', + id: 'core/router', disabled: false, }, ]); @@ -89,12 +89,12 @@ describe('readAppExtensionsConfig', () => { readAppExtensionsConfig( new ConfigReader({ app: { - extensions: [{ 'core.router': 'some-string' }], + extensions: [{ 'core/router': 'some-string' }], }, }), ), ).toThrow( - 'Invalid extension configuration at app.extensions[0][core.router], value must be a boolean or object', + 'Invalid extension configuration at app.extensions[0][core/router], value must be a boolean or object', ); }); @@ -105,7 +105,7 @@ describe('readAppExtensionsConfig', () => { app: { extensions: [ { - 'core.router/routes': { + '': { extension: 'example-package#MyPage', config: { foo: 'bar' }, }, @@ -115,7 +115,7 @@ describe('readAppExtensionsConfig', () => { }), ), ).toThrowErrorMatchingInlineSnapshot( - `"Invalid extension configuration at app.extensions[0], extension ID must not contain slashes; got 'core.router/routes', did you mean 'core.router'?"`, + `"Invalid extension configuration at app.extensions[0], extension ID must not be empty or contain whitespace"`, ); }); }); @@ -156,8 +156,8 @@ describe('expandShorthandExtensionParameters', () => { }); it('supports string key', () => { - expect(run('core.router')).toEqual({ - id: 'core.router', + expect(run('core/router')).toEqual({ + id: 'core/router', disabled: false, }); expect(() => run('')).toThrowErrorMatchingInlineSnapshot( @@ -166,103 +166,100 @@ describe('expandShorthandExtensionParameters', () => { expect(() => run(' a')).toThrowErrorMatchingInlineSnapshot( `"Invalid extension configuration at app.extensions[1], extension ID must not be empty or contain whitespace"`, ); - expect(() => run('core.router/routes')).toThrowErrorMatchingInlineSnapshot( - `"Invalid extension configuration at app.extensions[1], extension ID must not contain slashes; got 'core.router/routes', did you mean 'core.router'?"`, - ); }); it('supports null value', () => { // this is the result of typing: - // - core.router: + // - core/router: // The missing value is interpreted as null by the yaml parser so we deal with that - expect(run({ 'core.router': null })).toEqual({ - id: 'core.router', + expect(run({ 'core/router': null })).toEqual({ + id: 'core/router', disabled: false, }); }); it('supports boolean value', () => { - expect(run({ 'core.router': true })).toEqual({ - id: 'core.router', + expect(run({ 'core/router': true })).toEqual({ + id: 'core/router', disabled: false, }); - expect(run({ 'core.router': false })).toEqual({ - id: 'core.router', + expect(run({ 'core/router': false })).toEqual({ + id: 'core/router', disabled: true, }); }); it('should not support string values', () => { expect(() => - run({ 'core.router': 'example-package#MyRouter' }), + run({ 'core/router': 'example-package#MyRouter' }), ).toThrowErrorMatchingInlineSnapshot( - `"Invalid extension configuration at app.extensions[1][core.router], value must be a boolean or object"`, + `"Invalid extension configuration at app.extensions[1][core/router], value must be a boolean or object"`, ); }); it('supports object id only in the key', () => { expect(() => - run({ 'core.router': { id: 'some.id' } }), + run({ 'core/router': { id: 'some.id' } }), ).toThrowErrorMatchingInlineSnapshot( - `"Invalid extension configuration at app.extensions[1][core.router].id, unknown parameter; expected one of 'attachTo', 'disabled', 'config'"`, + `"Invalid extension configuration at app.extensions[1][core/router].id, unknown parameter; expected one of 'attachTo', 'disabled', 'config'"`, ); }); it('supports object attachTo', () => { expect( run({ - 'core.router': { attachTo: { id: 'other.root', input: 'inputs' } }, + 'core/router': { attachTo: { id: 'other.root', input: 'inputs' } }, }), ).toEqual({ - id: 'core.router', + id: 'core/router', attachTo: { id: 'other.root', input: 'inputs' }, }); expect(() => run({ - 'core.router': { + 'core/router': { id: 'other-id', }, }), ).toThrowErrorMatchingInlineSnapshot( - `"Invalid extension configuration at app.extensions[1][core.router].id, unknown parameter; expected one of 'attachTo', 'disabled', 'config'"`, + `"Invalid extension configuration at app.extensions[1][core/router].id, unknown parameter; expected one of 'attachTo', 'disabled', 'config'"`, ); }); it('supports object disabled', () => { - expect(run({ 'core.router': { disabled: true } })).toEqual({ - id: 'core.router', + expect(run({ 'core/router': { disabled: true } })).toEqual({ + id: 'core/router', disabled: true, }); - expect(run({ 'core.router': { disabled: false } })).toEqual({ - id: 'core.router', + expect(run({ 'core/router': { disabled: false } })).toEqual({ + id: 'core/router', disabled: false, }); expect(() => - run({ 'core.router': { disabled: 0 } }), + run({ 'core/router': { disabled: 0 } }), ).toThrowErrorMatchingInlineSnapshot( - `"Invalid extension configuration at app.extensions[1][core.router].disabled, must be a boolean"`, + `"Invalid extension configuration at app.extensions[1][core/router].disabled, must be a boolean"`, ); }); it('supports object config', () => { expect( - run({ 'core.router': { config: { disableRedirects: true } } }), + run({ 'core/router': { config: { disableRedirects: true } } }), ).toEqual({ - id: 'core.router', + id: 'core/router', config: { disableRedirects: true }, }); expect(() => - run({ 'core.router': { config: 0 } }), + run({ 'core/router': { config: 0 } }), ).toThrowErrorMatchingInlineSnapshot( - `"Invalid extension configuration at app.extensions[1][core.router].config, must be an object"`, + `"Invalid extension configuration at app.extensions[1][core/router].config, must be an object"`, ); }); it('rejects unknown object keys', () => { expect(() => - run({ 'core.router': { foo: { settings: true } } }), + run({ 'core/router': { foo: { settings: true } } }), ).toThrowErrorMatchingInlineSnapshot( - `"Invalid extension configuration at app.extensions[1][core.router].foo, unknown parameter; expected one of 'attachTo', 'disabled', 'config'"`, + `"Invalid extension configuration at app.extensions[1][core/router].foo, unknown parameter; expected one of 'attachTo', 'disabled', 'config'"`, ); }); }); diff --git a/packages/frontend-app-api/src/tree/readAppExtensionsConfig.ts b/packages/frontend-app-api/src/tree/readAppExtensionsConfig.ts index 2b5b51ce92..cca7064add 100644 --- a/packages/frontend-app-api/src/tree/readAppExtensionsConfig.ts +++ b/packages/frontend-app-api/src/tree/readAppExtensionsConfig.ts @@ -70,15 +70,6 @@ export function expandShorthandExtensionParameters( errorMsg('extension ID must not be empty or contain whitespace'), ); } - - if (id.includes('/')) { - let message = `extension ID must not contain slashes; got '${id}'`; - const good = id.split('/')[0]; - if (good) { - message += `, did you mean '${good}'?`; - } - throw new Error(errorMsg(message)); - } } // Example YAML: diff --git a/packages/frontend-app-api/src/tree/resolveAppNodeSpecs.test.ts b/packages/frontend-app-api/src/tree/resolveAppNodeSpecs.test.ts index 39eceaaf28..2e9f73d87b 100644 --- a/packages/frontend-app-api/src/tree/resolveAppNodeSpecs.test.ts +++ b/packages/frontend-app-api/src/tree/resolveAppNodeSpecs.test.ts @@ -18,6 +18,7 @@ import { createExtensionOverrides, createPlugin, Extension, + ExtensionDefinition, } from '@backstage/frontend-plugin-api'; import { resolveAppNodeSpecs } from './resolveAppNodeSpecs'; @@ -27,12 +28,26 @@ function makeExt( attachId: string = 'root', ) { return { + $$type: '@backstage/Extension', id, attachTo: { id: attachId, input: 'default' }, disabled: status === 'disabled', } as Extension; } +function makeExtDef( + name: string, + status: 'disabled' | 'enabled' = 'enabled', + attachId: string = 'root', +) { + return { + $$type: '@backstage/ExtensionDefinition', + name, + attachTo: { id: attachId, input: 'default' }, + disabled: status === 'disabled', + } as ExtensionDefinition; +} + describe('resolveAppNodeSpecs', () => { it('should not filter out disabled extension instances', () => { const a = makeExt('a', 'disabled'); @@ -78,9 +93,8 @@ describe('resolveAppNodeSpecs', () => { }); it('should override attachment points', () => { - const a = makeExt('a'); const b = makeExt('b'); - const pluginA = createPlugin({ id: 'test', extensions: [a] }); + const pluginA = createPlugin({ id: 'test', extensions: [makeExtDef('a')] }); expect( resolveAppNodeSpecs({ features: [pluginA], @@ -94,8 +108,8 @@ describe('resolveAppNodeSpecs', () => { }), ).toEqual([ { - id: 'a', - extension: a, + id: 'test/a', + extension: makeExt('test/a'), attachTo: { id: 'root', input: 'default' }, source: pluginA, disabled: false, @@ -110,31 +124,34 @@ describe('resolveAppNodeSpecs', () => { }); it('should fully override configuration and duplicate', () => { - const a = makeExt('a'); - const b = makeExt('b'); - const plugin = createPlugin({ id: 'test', extensions: [a, b] }); + const a = makeExt('test/a'); + const b = makeExt('test/b'); + const plugin = createPlugin({ + id: 'test', + extensions: [makeExtDef('a'), makeExtDef('b')], + }); expect( resolveAppNodeSpecs({ features: [plugin], builtinExtensions: [], parameters: [ { - id: 'a', + id: 'test/a', config: { foo: { bar: 1 } }, }, { - id: 'b', + id: 'test/b', config: { foo: { bar: 2 } }, }, { - id: 'b', + id: 'test/b', config: { foo: { qux: 3 } }, }, ], }), ).toEqual([ { - id: 'a', + id: 'test/a', extension: a, attachTo: { id: 'root', input: 'default' }, source: plugin, @@ -142,7 +159,7 @@ describe('resolveAppNodeSpecs', () => { disabled: false, }, { - id: 'b', + id: 'test/b', extension: b, attachTo: { id: 'root', input: 'default' }, source: plugin, @@ -187,11 +204,12 @@ describe('resolveAppNodeSpecs', () => { }); it('should apply extension overrides', () => { - const a = makeExt('a'); - const b = makeExt('b'); - const plugin = createPlugin({ id: 'test', extensions: [a, b] }); - const aOverride = makeExt('a', 'enabled', 'other'); - const bOverride = makeExt('b', 'disabled', 'other'); + const plugin = createPlugin({ + id: 'test', + extensions: [makeExtDef('a'), makeExtDef('b')], + }); + const aOverride = makeExt('test/a', 'enabled', 'other'); + const bOverride = makeExt('test/b', 'disabled', 'other'); const cOverride = makeExt('c'); expect( @@ -199,7 +217,11 @@ describe('resolveAppNodeSpecs', () => { features: [ plugin, createExtensionOverrides({ - extensions: [aOverride, bOverride, cOverride], + extensions: [ + makeExtDef('test/a', 'enabled', 'other'), + makeExtDef('test/b', 'disabled', 'other'), + makeExtDef('c'), + ], }), ], builtinExtensions: [], @@ -207,14 +229,14 @@ describe('resolveAppNodeSpecs', () => { }), ).toEqual([ { - id: 'a', + id: 'test/a', extension: aOverride, attachTo: { id: 'other', input: 'default' }, source: plugin, disabled: false, }, { - id: 'b', + id: 'test/b', extension: bOverride, attachTo: { id: 'other', input: 'default' }, source: plugin, @@ -231,39 +253,50 @@ describe('resolveAppNodeSpecs', () => { }); it('should use order from configuration when rather than overrides', () => { - const a = makeExt('a', 'disabled'); - const b = makeExt('b', 'disabled'); - const c = makeExt('c', 'disabled'); - const aOverride = makeExt('c', 'disabled'); - const bOverride = makeExt('b', 'disabled'); - const cOverride = makeExt('a', 'disabled'); - const result = resolveAppNodeSpecs({ features: [ - createPlugin({ id: 'test', extensions: [a, b, c] }), + createPlugin({ + id: 'test', + extensions: [ + makeExtDef('a', 'disabled'), + makeExtDef('b', 'disabled'), + makeExtDef('c', 'disabled'), + ], + }), createExtensionOverrides({ - extensions: [cOverride, bOverride, aOverride], + extensions: [ + makeExtDef('test/c', 'disabled'), + makeExtDef('test/b', 'disabled'), + makeExtDef('test/a', 'disabled'), + ], }), ], builtinExtensions: [], - parameters: ['b', 'c', 'a'].map(id => ({ id, disabled: false })), + parameters: ['test/b', 'test/c', 'test/a'].map(id => ({ + id, + disabled: false, + })), }); - expect(result.map(r => r.extension.id)).toEqual(['b', 'c', 'a']); + expect(result.map(r => r.extension.id)).toEqual([ + 'test/b', + 'test/c', + 'test/a', + ]); }); it('throws an error when a forbidden extension is overridden by a plugin', () => { expect(() => resolveAppNodeSpecs({ features: [ - createPlugin({ id: 'test', extensions: [makeExt('forbidden')] }), + createPlugin({ id: 'test', extensions: [makeExtDef('forbidden')] }), ], builtinExtensions: [], parameters: [], - forbidden: new Set(['forbidden']), + forbidden: new Set(['test/forbidden']), }), ).toThrow( - "It is forbidden to override the following extension(s): 'forbidden', which is done by the following plugin(s): 'test'", + "It is forbidden to override the following extension(s): 'test/forbidden', which is done by the following plugin(s): 'test'", ); }); @@ -271,7 +304,7 @@ describe('resolveAppNodeSpecs', () => { expect(() => resolveAppNodeSpecs({ features: [ - createExtensionOverrides({ extensions: [makeExt('forbidden')] }), + createExtensionOverrides({ extensions: [makeExtDef('forbidden')] }), ], builtinExtensions: [], parameters: [], diff --git a/packages/frontend-app-api/src/tree/resolveAppNodeSpecs.ts b/packages/frontend-app-api/src/tree/resolveAppNodeSpecs.ts index 0a024c0a3f..34ec29b8f7 100644 --- a/packages/frontend-app-api/src/tree/resolveAppNodeSpecs.ts +++ b/packages/frontend-app-api/src/tree/resolveAppNodeSpecs.ts @@ -23,6 +23,8 @@ import { import { toInternalExtensionOverrides } from '../../../frontend-plugin-api/src/wiring/createExtensionOverrides'; import { ExtensionParameters } from './readAppExtensionsConfig'; import { AppNodeSpec } from '@backstage/frontend-plugin-api'; +// eslint-disable-next-line @backstage/no-relative-monorepo-imports +import { toInternalBackstagePlugin } from '../../../frontend-plugin-api/src/wiring/createPlugin'; /** @internal */ export function resolveAppNodeSpecs(options: { @@ -42,7 +44,10 @@ export function resolveAppNodeSpecs(options: { ); const pluginExtensions = plugins.flatMap(source => { - return source.extensions.map(extension => ({ ...extension, source })); + return toInternalBackstagePlugin(source).extensions.map(extension => ({ + ...extension, + source, + })); }); const overrideExtensions = overrides.flatMap( override => toInternalExtensionOverrides(override).extensions, diff --git a/packages/frontend-app-api/src/tree/resolveAppTree.test.ts b/packages/frontend-app-api/src/tree/resolveAppTree.test.ts index d84064b26a..4fca671554 100644 --- a/packages/frontend-app-api/src/tree/resolveAppTree.test.ts +++ b/packages/frontend-app-api/src/tree/resolveAppTree.test.ts @@ -16,15 +16,17 @@ import { createExtension } from '@backstage/frontend-plugin-api'; import { resolveAppTree } from './resolveAppTree'; +// eslint-disable-next-line @backstage/no-relative-monorepo-imports +import { resolveExtensionDefinition } from '../../../frontend-plugin-api/src/wiring/resolveExtensionDefinition'; -const extBaseConfig = { - id: 'test', - attachTo: { id: 'nonexistent', input: 'nonexistent' }, - output: {}, - factory: () => ({}), -}; - -const extension = createExtension(extBaseConfig); +const extension = resolveExtensionDefinition( + createExtension({ + name: 'test', + attachTo: { id: 'nonexistent', input: 'nonexistent' }, + output: {}, + factory: () => ({}), + }), +); const baseSpec = { extension, diff --git a/packages/frontend-app-api/src/wiring/InternalAppContext.ts b/packages/frontend-app-api/src/wiring/InternalAppContext.ts index 805c46a067..81264bd81b 100644 --- a/packages/frontend-app-api/src/wiring/InternalAppContext.ts +++ b/packages/frontend-app-api/src/wiring/InternalAppContext.ts @@ -17,10 +17,12 @@ import { createContext } from 'react'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports import { AppIdentityProxy } from '../../../core-app-api/src/apis/implementations/IdentityApi/AppIdentityProxy'; +import { BackstageRouteObject } from '../routing/types'; export const InternalAppContext = createContext< | undefined | { appIdentityProxy: AppIdentityProxy; + routeObjects: BackstageRouteObject[]; } >(undefined); diff --git a/packages/frontend-app-api/src/wiring/createApp.test.tsx b/packages/frontend-app-api/src/wiring/createApp.test.tsx index 52186a5029..2713a9743e 100644 --- a/packages/frontend-app-api/src/wiring/createApp.test.tsx +++ b/packages/frontend-app-api/src/wiring/createApp.test.tsx @@ -17,6 +17,9 @@ import { AppTreeApi, appTreeApiRef, + coreExtensionData, + createExtension, + createExtensionOverrides, createPageExtension, createPlugin, createThemeExtension, @@ -25,7 +28,7 @@ import { screen, waitFor } from '@testing-library/react'; import { createApp } from './createApp'; import { MockConfigApi, renderWithEffects } from '@backstage/test-utils'; import React from 'react'; -import { useApi } from '@backstage/core-plugin-api'; +import { featureFlagsApiRef, useApi } from '@backstage/core-plugin-api'; describe('createApp', () => { it('should allow themes to be installed', async () => { @@ -33,7 +36,10 @@ describe('createApp', () => { configLoader: async () => new MockConfigApi({ app: { - extensions: [{ 'themes.light': false }, { 'themes.dark': false }], + extensions: [ + { 'theme:app/light': false }, + { 'theme:app/dark': false }, + ], }, }), features: [ @@ -65,7 +71,6 @@ describe('createApp', () => { id: duplicatedFeatureId, extensions: [ createPageExtension({ - id: 'test.page.first', defaultPath: '/', loader: async () =>
First Page
, }), @@ -75,7 +80,6 @@ describe('createApp', () => { id: duplicatedFeatureId, extensions: [ createPageExtension({ - id: 'test.page.last', defaultPath: '/', loader: async () =>
Last Page
, }), @@ -94,6 +98,59 @@ describe('createApp', () => { ); }); + it('should register feature flags', async () => { + const app = createApp({ + configLoader: async () => new MockConfigApi({}), + features: [ + createPlugin({ + id: 'test', + featureFlags: [{ name: 'test-1' }], + extensions: [ + createExtension({ + name: 'first', + attachTo: { id: 'core', input: 'root' }, + output: { element: coreExtensionData.reactElement }, + factory() { + const Component = () => { + const flagsApi = useApi(featureFlagsApiRef); + return ( +
+ Flags:{' '} + {flagsApi + .getRegisteredFlags() + .map(flag => `${flag.name} from '${flag.pluginId}'`) + .join(', ')} +
+ ); + }; + return { element: }; + }, + }), + ], + }), + createExtensionOverrides({ + featureFlags: [{ name: 'test-2' }], + extensions: [ + createExtension({ + namespace: 'core', + name: 'router', + attachTo: { id: 'core', input: 'root' }, + disabled: true, + output: {}, + factory: () => ({}), + }), + ], + }), + ], + }); + + await renderWithEffects(app.createRoot()); + + await expect( + screen.findByText("Flags: test-1 from 'test', test-2 from ''"), + ).resolves.toBeInTheDocument(); + }); + it('should make the app structure available through the AppTreeApi', async () => { let appTreeApi: AppTreeApi | undefined = undefined; @@ -104,7 +161,6 @@ describe('createApp', () => { id: 'my-plugin', extensions: [ createPageExtension({ - id: 'plugin.my-plugin.page', defaultPath: '/', loader: async () => { const Component = () => { @@ -127,26 +183,32 @@ describe('createApp', () => { expect(String(tree.root)).toMatchInlineSnapshot(` " root [ - + children [ - + content [ - + routes [ - + ] - + ] nav [ - + ] - + ] - + + ] + components [ + + + + ] themes [ - - + + ] " `); diff --git a/packages/frontend-app-api/src/wiring/createApp.tsx b/packages/frontend-app-api/src/wiring/createApp.tsx index 1858bd4884..f58dfb3159 100644 --- a/packages/frontend-app-api/src/wiring/createApp.tsx +++ b/packages/frontend-app-api/src/wiring/createApp.tsx @@ -20,6 +20,8 @@ import { AppTree, appTreeApiRef, BackstagePlugin, + ComponentRef, + componentsApiRef, coreExtensionData, ExtensionDataRef, ExtensionOverrides, @@ -71,6 +73,8 @@ import { AppLanguageSelector } from '../../../core-app-api/src/apis/implementati // eslint-disable-next-line @backstage/no-relative-monorepo-imports import { I18nextTranslationApi } from '../../../core-app-api/src/apis/implementations/TranslationApi/I18nextTranslationApi'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports +import { resolveExtensionDefinition } from '../../../frontend-plugin-api/src/wiring/resolveExtensionDefinition'; +// eslint-disable-next-line @backstage/no-relative-monorepo-imports import { apis as defaultApis, components as defaultComponents, @@ -89,20 +93,35 @@ import { RoutingProvider } from '../routing/RoutingProvider'; import { resolveRouteBindings } from '../routing/resolveRouteBindings'; import { collectRouteIds } from '../routing/collectRouteIds'; import { createAppTree } from '../tree'; +import { + DefaultProgressComponent, + DefaultErrorBoundaryComponent, + DefaultBootErrorPageComponent, + DefaultNotFoundErrorPageComponent, +} from '../extensions/components'; import { AppNode } from '@backstage/frontend-plugin-api'; import { toLegacyPlugin } from '../routing/toLegacyPlugin'; import { InternalAppContext } from './InternalAppContext'; import { CoreRouter } from '../extensions/CoreRouter'; +// eslint-disable-next-line @backstage/no-relative-monorepo-imports +import { toInternalBackstagePlugin } from '../../../frontend-plugin-api/src/wiring/createPlugin'; +// eslint-disable-next-line @backstage/no-relative-monorepo-imports +import { toInternalExtensionOverrides } from '../../../frontend-plugin-api/src/wiring/createExtensionOverrides'; +import { DefaultComponentsApi } from '../apis/implementations/ComponentsApi'; -const builtinExtensions = [ +export const builtinExtensions = [ Core, CoreRouter, CoreRoutes, CoreNav, CoreLayout, + DefaultProgressComponent, + DefaultErrorBoundaryComponent, + DefaultBootErrorPageComponent, + DefaultNotFoundErrorPageComponent, LightTheme, DarkTheme, -]; +].map(def => resolveExtensionDefinition(def)); /** @public */ export interface ExtensionTreeNode { @@ -157,7 +176,7 @@ export function createExtensionTree(options: { ); }, getRootRoutes(): JSX.Element[] { - return this.getExtensionAttachments('core.routes', 'routes').map(node => { + return this.getExtensionAttachments('core/routes', 'routes').map(node => { const path = node.getData(coreExtensionData.routePath); const element = node.getData(coreExtensionData.reactElement); const routeRef = node.getData(coreExtensionData.routeRef); @@ -184,7 +203,7 @@ export function createExtensionTree(options: { ); }; - return this.getExtensionAttachments('core.nav', 'items') + return this.getExtensionAttachments('core/nav', 'items') .map((node, index) => { const target = node.getData(coreExtensionData.navTarget); if (!target) { @@ -304,6 +323,26 @@ export function createSpecializedApp(options?: { const appIdentityProxy = new AppIdentityProxy(); const apiHolder = createApiHolder(tree, config, appIdentityProxy); + + const featureFlagApi = apiHolder.get(featureFlagsApiRef); + if (featureFlagApi) { + for (const feature of features) { + if (feature.$$type === '@backstage/BackstagePlugin') { + toInternalBackstagePlugin(feature).featureFlags.forEach(flag => + featureFlagApi.registerFlag({ + name: flag.name, + pluginId: feature.id, + }), + ); + } + if (feature.$$type === '@backstage/ExtensionOverrides') { + toInternalExtensionOverrides(feature).featureFlags.forEach(flag => + featureFlagApi.registerFlag({ name: flag.name, pluginId: '' }), + ); + } + } + } + const routeInfo = extractRouteInfoFromAppNode(tree.root); const routeBindings = resolveRouteBindings( options?.bindRoutes, @@ -317,7 +356,9 @@ export function createSpecializedApp(options?: { - + {rootEl} @@ -399,6 +440,24 @@ function createApiHolder( }), }); + const componentsExtensions = + tree.root.edges.attachments + .get('components') + ?.map(e => e.instance?.getData(coreExtensionData.component)) + .filter(x => !!x) ?? []; + + const componentsMap = componentsExtensions.reduce( + (components, component) => + component ? components.set(component.ref, component?.impl) : components, + new Map, any>(), + ); + + factoryRegistry.register('static', { + api: componentsApiRef, + deps: {}, + factory: () => new DefaultComponentsApi(componentsMap), + }); + factoryRegistry.register('static', { api: appThemeApiRef, deps: {}, diff --git a/packages/frontend-plugin-api/CHANGELOG.md b/packages/frontend-plugin-api/CHANGELOG.md index 94f90d6717..74c9e784ee 100644 --- a/packages/frontend-plugin-api/CHANGELOG.md +++ b/packages/frontend-plugin-api/CHANGELOG.md @@ -1,5 +1,25 @@ # @backstage/frontend-plugin-api +## 0.4.0-next.1 + +### Minor Changes + +- a5a04739e1: The extension `factory` function now longer receives `id` or `source`, but instead now provides the extension's `AppNode` as `node`. The `ExtensionBoundary` component has also been updated to receive a `node` prop rather than `id` and `source`. + +### Patch Changes + +- 5eb6b8a7bc: Added the nav logo extension for customization of sidebar logo +- 1f12fb762c: Create factories for overriding default core components extensions. +- 59709286b3: Add feature flags to plugins and extension overrides. +- e539735435: Added `createSignInPageExtension`. +- f27ee7d937: Migrate analytics api and context files. +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.7 + ## 0.3.1-next.0 ### Patch Changes diff --git a/packages/frontend-plugin-api/api-report.md b/packages/frontend-plugin-api/api-report.md index bcc6115ff3..e8f4f16e97 100644 --- a/packages/frontend-plugin-api/api-report.md +++ b/packages/frontend-plugin-api/api-report.md @@ -22,6 +22,7 @@ import { AuthProviderInfo } from '@backstage/core-plugin-api'; import { AuthRequestOptions } from '@backstage/core-plugin-api'; import { BackstageIdentityApi } from '@backstage/core-plugin-api'; import { BackstageIdentityResponse } from '@backstage/core-plugin-api'; +import { BackstagePlugin as BackstagePlugin_2 } from '@backstage/core-plugin-api'; import { BackstageUserIdentity } from '@backstage/core-plugin-api'; import { bitbucketAuthApiRef } from '@backstage/core-plugin-api'; import { bitbucketServerAuthApiRef } from '@backstage/core-plugin-api'; @@ -64,6 +65,7 @@ import { OpenIdConnectApi } from '@backstage/core-plugin-api'; import { PendingOAuthRequest } from '@backstage/core-plugin-api'; import { ProfileInfo } from '@backstage/core-plugin-api'; import { ProfileInfoApi } from '@backstage/core-plugin-api'; +import { PropsWithChildren } from 'react'; import { default as React_2 } from 'react'; import { ReactNode } from 'react'; import { SessionApi } from '@backstage/core-plugin-api'; @@ -86,6 +88,51 @@ export { alertApiRef }; export { AlertMessage }; +// @public +export type AnalyticsApi = { + captureEvent(event: AnalyticsEvent): void; +}; + +// @public +export const analyticsApiRef: ApiRef; + +// @public +export const AnalyticsContext: (options: { + attributes: Partial; + children: ReactNode; +}) => React_2.JSX.Element; + +// @public +export type AnalyticsContextValue = CommonAnalyticsContext & { + [param in string]: string | boolean | number | undefined; +}; + +// @public +export type AnalyticsEvent = { + action: string; + subject: string; + value?: number; + attributes?: AnalyticsEventAttributes; + context: AnalyticsContextValue; +}; + +// @public +export type AnalyticsEventAttributes = { + [attribute in string]: string | boolean | number; +}; + +// @public +export type AnalyticsTracker = { + captureEvent: ( + action: string, + subject: string, + options?: { + value?: number; + attributes?: AnalyticsEventAttributes; + }, + ) => void; +}; + export { AnyApiFactory }; export { AnyApiRef }; @@ -218,15 +265,13 @@ export interface BackstagePlugin< ExternalRoutes extends AnyExternalRoutes = AnyExternalRoutes, > { // (undocumented) - $$type: '@backstage/BackstagePlugin'; + readonly $$type: '@backstage/BackstagePlugin'; // (undocumented) - extensions: Extension[]; + readonly externalRoutes: ExternalRoutes; // (undocumented) - externalRoutes: ExternalRoutes; + readonly id: string; // (undocumented) - id: string; - // (undocumented) - routes: Routes; + readonly routes: Routes; } export { BackstageUserIdentity }; @@ -235,6 +280,27 @@ export { bitbucketAuthApiRef }; export { bitbucketServerAuthApiRef }; +// @public +export type CommonAnalyticsContext = { + pluginId: string; + extensionId: string; +}; + +// @public (undocumented) +export type ComponentRef = { + id: string; + T: T; +}; + +// @public +export interface ComponentsApi { + // (undocumented) + getComponent(ref: ComponentRef): T; +} + +// @public +export const componentsApiRef: ApiRef; + export { ConfigApi }; export { configApiRef }; @@ -255,6 +321,31 @@ export interface ConfigurableExtensionDataRef< >; } +// @public (undocumented) +export type CoreBootErrorPageComponent = ComponentType< + PropsWithChildren<{ + step: 'load-config' | 'load-chunk'; + error: Error; + }> +>; + +// @public (undocumented) +export const coreComponentsRefs: { + progress: ComponentRef; + bootErrorPage: ComponentRef; + notFoundErrorPage: ComponentRef; + errorBoundaryFallback: ComponentRef; +}; + +// @public (undocumented) +export type CoreErrorBoundaryFallbackComponent = ComponentType< + PropsWithChildren<{ + plugin?: BackstagePlugin_2; + error: Error; + resetError: () => void; + }> +>; + // @public (undocumented) export const coreExtensionData: { reactElement: ConfigurableExtensionDataRef; @@ -264,8 +355,23 @@ export const coreExtensionData: { navTarget: ConfigurableExtensionDataRef; theme: ConfigurableExtensionDataRef; logoElements: ConfigurableExtensionDataRef; + component: ConfigurableExtensionDataRef< + { + ref: ComponentRef>; + impl: ComponentType; + }, + {} + >; }; +// @public (undocumented) +export type CoreNotFoundErrorPageComponent = ComponentType< + PropsWithChildren<{}> +>; + +// @public (undocumented) +export type CoreProgressComponent = ComponentType>; + // @public (undocumented) export function createApiExtension< TConfig extends {}, @@ -276,7 +382,7 @@ export function createApiExtension< api: AnyApiRef; factory: (options: { config: TConfig; - inputs: Expand>; + inputs: Expand>; }) => AnyApiFactory; } | { @@ -286,12 +392,38 @@ export function createApiExtension< configSchema?: PortableSchema; inputs?: TInputs; }, -): Extension; +): ExtensionDefinition; export { createApiFactory }; export { createApiRef }; +// @public (undocumented) +export function createComponentExtension< + TRef extends ComponentRef, + TConfig extends {}, + TInputs extends AnyExtensionInputMap, +>(options: { + ref: TRef; + name?: string; + disabled?: boolean; + inputs?: TInputs; + configSchema?: PortableSchema; + component: + | { + lazy: (values: { + config: TConfig; + inputs: Expand>; + }) => Promise; + } + | { + sync: (values: { + config: TConfig; + inputs: Expand>; + }) => TRef['T']; + }; +}): ExtensionDefinition; + // @public (undocumented) export function createExtension< TOutput extends AnyExtensionDataMap, @@ -299,7 +431,7 @@ export function createExtension< TConfig = never, >( options: CreateExtensionOptions, -): Extension; +): ExtensionDefinition; // @public (undocumented) export function createExtensionDataRef( @@ -343,13 +475,17 @@ export interface CreateExtensionOptions< factory(options: { node: AppNode; config: TConfig; - inputs: Expand>; + inputs: Expand>; }): Expand>; // (undocumented) - id: string; - // (undocumented) inputs?: TInputs; // (undocumented) + kind?: string; + // (undocumented) + name?: string; + // (undocumented) + namespace?: string; + // (undocumented) output: TOutput; } @@ -385,11 +521,12 @@ export function createExternalRouteRef< // @public export function createNavItemExtension(options: { - id: string; + namespace?: string; + name?: string; routeRef: RouteRef; title: string; icon: IconComponent_2; -}): Extension<{ +}): ExtensionDefinition<{ title: string; }>; @@ -408,7 +545,8 @@ export function createPageExtension< configSchema: PortableSchema; } ) & { - id: string; + namespace?: string; + name?: string; attachTo?: { id: string; input: string; @@ -418,10 +556,10 @@ export function createPageExtension< routeRef?: RouteRef; loader: (options: { config: TConfig; - inputs: Expand>; + inputs: Expand>; }) => Promise; }, -): Extension; +): ExtensionDefinition; // @public (undocumented) export function createPlugin< @@ -461,7 +599,8 @@ export function createSignInPageExtension< TConfig extends {}, TInputs extends AnyExtensionInputMap, >(options: { - id: string; + namespace?: string; + name?: string; attachTo?: { id: string; input: string; @@ -471,9 +610,9 @@ export function createSignInPageExtension< inputs?: TInputs; loader: (options: { config: TConfig; - inputs: Expand>; + inputs: Expand>; }) => Promise>; -}): Extension; +}): ExtensionDefinition; // @public export function createSubRouteRef< @@ -485,7 +624,9 @@ export function createSubRouteRef< }): MakeSubRouteRef, ParentParams>; // @public (undocumented) -export function createThemeExtension(theme: AppTheme): Extension; +export function createThemeExtension( + theme: AppTheme, +): ExtensionDefinition; export { DiscoveryApi }; @@ -516,10 +657,7 @@ export interface Extension { factory(options: { node: AppNode; config: TConfig; - inputs: Record< - string, - undefined | Record | Array> - >; + inputs: ResolvedExtensionInputs; }): ExtensionDataValues; // (undocumented) id: string; @@ -572,6 +710,37 @@ export type ExtensionDataValues = { : never]?: TExtensionData[DataName]['T']; }; +// @public (undocumented) +export interface ExtensionDefinition { + // (undocumented) + $$type: '@backstage/ExtensionDefinition'; + // (undocumented) + attachTo: { + id: string; + input: string; + }; + // (undocumented) + configSchema?: PortableSchema; + // (undocumented) + disabled: boolean; + // (undocumented) + factory(options: { + node: AppNode; + config: TConfig; + inputs: ResolvedExtensionInputs; + }): ExtensionDataValues; + // (undocumented) + inputs: AnyExtensionInputMap; + // (undocumented) + kind?: string; + // (undocumented) + name?: string; + // (undocumented) + namespace?: string; + // (undocumented) + output: AnyExtensionDataMap; +} + // @public (undocumented) export interface ExtensionInput< TExtensionData extends AnyExtensionDataMap, @@ -588,31 +757,18 @@ export interface ExtensionInput< extensionData: TExtensionData; } -// @public -export type ExtensionInputValues< - TInputs extends { - [name in string]: ExtensionInput; - }, -> = { - [InputName in keyof TInputs]: false extends TInputs[InputName]['config']['singleton'] - ? Array>> - : false extends TInputs[InputName]['config']['optional'] - ? Expand> - : Expand< - ExtensionDataValues | undefined - >; -}; - // @public (undocumented) export interface ExtensionOverrides { // (undocumented) - $$type: '@backstage/ExtensionOverrides'; + readonly $$type: '@backstage/ExtensionOverrides'; } // @public (undocumented) export interface ExtensionOverridesOptions { // (undocumented) - extensions: Extension[]; + extensions: ExtensionDefinition[]; + // (undocumented) + featureFlags?: FeatureFlagConfig[]; } // @public @@ -630,6 +786,11 @@ export interface ExternalRouteRef< export { FeatureFlag }; +// @public +export type FeatureFlagConfig = { + name: string; +}; + export { FeatureFlagsApi }; export { featureFlagsApiRef }; @@ -703,10 +864,12 @@ export interface PluginOptions< ExternalRoutes extends AnyExternalRoutes, > { // (undocumented) - extensions?: Extension[]; + extensions?: ExtensionDefinition[]; // (undocumented) externalRoutes?: ExternalRoutes; // (undocumented) + featureFlags?: FeatureFlagConfig[]; + // (undocumented) id: string; // (undocumented) routes?: Routes; @@ -722,6 +885,28 @@ export { ProfileInfo }; export { ProfileInfoApi }; +// @public +export type ResolvedExtensionInput = + { + extensionId: string; + output: ExtensionDataValues; + }; + +// @public +export type ResolvedExtensionInputs< + TInputs extends { + [name in string]: ExtensionInput; + }, +> = { + [InputName in keyof TInputs]: false extends TInputs[InputName]['config']['singleton'] + ? Array>> + : false extends TInputs[InputName]['config']['optional'] + ? Expand> + : Expand< + ResolvedExtensionInput | undefined + >; +}; + // @public export type RouteFunc = ( ...[params]: TParams extends undefined @@ -763,6 +948,9 @@ export interface SubRouteRef< export { TypesToApiRefs }; +// @public +export function useAnalytics(): AnalyticsTracker; + export { useApi }; export { useApiHolder }; diff --git a/packages/frontend-plugin-api/package.json b/packages/frontend-plugin-api/package.json index 45888aff39..4137aaabc6 100644 --- a/packages/frontend-plugin-api/package.json +++ b/packages/frontend-plugin-api/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/frontend-plugin-api", - "version": "0.3.1-next.0", + "version": "0.4.0-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/packages/frontend-plugin-api/src/analytics/AnalyticsContext.test.tsx b/packages/frontend-plugin-api/src/analytics/AnalyticsContext.test.tsx new file mode 100644 index 0000000000..8e78bdc8c4 --- /dev/null +++ b/packages/frontend-plugin-api/src/analytics/AnalyticsContext.test.tsx @@ -0,0 +1,82 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import { render } from '@testing-library/react'; +import { renderHook } from '@testing-library/react'; +import { AnalyticsContext, useAnalyticsContext } from './AnalyticsContext'; + +const AnalyticsSpy = () => { + const context = useAnalyticsContext(); + return ( + <> +
{context.pluginId}
+
{context.extensionId}
+
{context.custom}
+ + ); +}; + +describe('AnalyticsContext', () => { + describe('useAnalyticsContext', () => { + it('returns default values', () => { + const { result } = renderHook(() => useAnalyticsContext()); + expect(result.current).toEqual({ + extensionId: 'App', + pluginId: 'root', + }); + }); + }); + + describe('AnalyticsContext', () => { + it('uses default analytics context', () => { + const result = render( + + + , + ); + + expect(result.getByTestId('extension-id')).toHaveTextContent('App'); + expect(result.getByTestId('plugin-id')).toHaveTextContent('root'); + }); + + it('uses provided analytics context', () => { + const result = render( + + + , + ); + + expect(result.getByTestId('extension-id')).toHaveTextContent('App'); + expect(result.getByTestId('plugin-id')).toHaveTextContent('custom'); + }); + + it('uses nested analytics context', () => { + const result = render( + + + + + , + ); + + expect(result.getByTestId('extension-id')).toHaveTextContent( + 'AnalyticsSpy', + ); + expect(result.getByTestId('plugin-id')).toHaveTextContent('custom'); + }); + }); +}); diff --git a/packages/frontend-plugin-api/src/analytics/AnalyticsContext.tsx b/packages/frontend-plugin-api/src/analytics/AnalyticsContext.tsx new file mode 100644 index 0000000000..10ef475803 --- /dev/null +++ b/packages/frontend-plugin-api/src/analytics/AnalyticsContext.tsx @@ -0,0 +1,108 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + createVersionedContext, + createVersionedValueMap, +} from '@backstage/version-bridge'; +import React, { ReactNode, useContext } from 'react'; +import { AnalyticsContextValue } from './types'; + +const AnalyticsReactContext = createVersionedContext<{ + 1: AnalyticsContextValue; +}>('analytics-context'); + +/** + * A "private" (to this package) hook that enables context inheritance and a + * way to read Analytics Context values at event capture-time. + * + * @internal + */ +export const useAnalyticsContext = (): AnalyticsContextValue => { + const theContext = useContext(AnalyticsReactContext); + + // Provide a default value if no value exists. + if (theContext === undefined) { + return { + pluginId: 'root', + extensionId: 'App', + }; + } + + // This should probably never happen, but check for it. + const theValue = theContext.atVersion(1); + if (theValue === undefined) { + throw new Error('No context found for version 1.'); + } + + return theValue; +}; + +/** + * Provides components in the child react tree an Analytics Context, ensuring + * all analytics events captured within the context have relevant attributes. + * + * @remarks + * + * Analytics contexts are additive, meaning the context ultimately emitted with + * an event is the combination of all contexts in the parent tree. + * + * @public + */ +export const AnalyticsContext = (options: { + attributes: Partial; + children: ReactNode; +}) => { + const { attributes, children } = options; + + const parentValues = useAnalyticsContext(); + const combinedValue = { + ...parentValues, + ...attributes, + }; + + const versionedCombinedValue = createVersionedValueMap({ 1: combinedValue }); + return ( + + {children} + + ); +}; + +/** + * Returns an HOC wrapping the provided component in an Analytics context with + * the given values. + * + * @param Component - Component to be wrapped with analytics context attributes + * @param values - Analytics context key/value pairs. + * @internal + */ +export function withAnalyticsContext( + Component: React.ComponentType, + values: AnalyticsContextValue, +) { + const ComponentWithAnalyticsContext = (props: TProps) => { + return ( + + + + ); + }; + ComponentWithAnalyticsContext.displayName = `WithAnalyticsContext(${ + Component.displayName || Component.name || 'Component' + })`; + return ComponentWithAnalyticsContext; +} diff --git a/packages/frontend-plugin-api/src/analytics/Tracker.test.ts b/packages/frontend-plugin-api/src/analytics/Tracker.test.ts new file mode 100644 index 0000000000..f1670cf9c4 --- /dev/null +++ b/packages/frontend-plugin-api/src/analytics/Tracker.test.ts @@ -0,0 +1,285 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { getOrCreateGlobalSingleton } from '@backstage/version-bridge'; +import { Tracker, routableExtensionRenderedEvent } from './Tracker'; + +describe('Tracker', () => { + const defaultContext = { + pluginId: 'root', + extensionId: 'App', + }; + const globalEvents = getOrCreateGlobalSingleton( + 'core-plugin-api:analytics-tracker-events', + () => ({}), + ); + const analyticsApiSpy = { + captureEvent: jest.fn(), + }; + let trackerUnderTest: Tracker; + + beforeEach(() => { + // Reset mocks and global state + jest.resetAllMocks(); + globalEvents.mostRecentGatheredNavigation = undefined; + globalEvents.mostRecentRoutableExtensionRender = undefined; + + // Set up a new tracker to test. + trackerUnderTest = new Tracker(analyticsApiSpy); + }); + + it('captures simple event with default context', () => { + trackerUnderTest.captureEvent('click', 'test 1'); + + expect(analyticsApiSpy.captureEvent).toHaveBeenCalledWith( + expect.objectContaining({ + action: 'click', + subject: 'test 1', + context: defaultContext, + }), + ); + }); + + it('captures simple event with set context', () => { + trackerUnderTest.setContext({ + pluginId: 'catalog', + extensionId: 'App', + }); + trackerUnderTest.captureEvent('click', 'test 2', { + value: 2, + attributes: { to: '/page-2' }, + }); + + expect(analyticsApiSpy.captureEvent).toHaveBeenCalledWith( + expect.objectContaining({ + action: 'click', + subject: 'test 2', + value: 2, + attributes: { to: '/page-2' }, + context: { + pluginId: 'catalog', + extensionId: 'App', + }, + }), + ); + }); + + describe('accurate navigate events', () => { + it('never captures _routeNodeType context key on navigate event', () => { + trackerUnderTest.setContext({ + pluginId: 'catalog', + extensionId: 'App', + }); + trackerUnderTest.captureEvent('navigate', '/page-3'); + + const receivedContext = + analyticsApiSpy.captureEvent.mock.calls[0][0].context; + expect(receivedContext.pluginId).toBe('catalog'); + expect(receivedContext._routeNodeType).toBe(undefined); + }); + + it('never immediately captures navigate event with _routeNodeType "gathered"', () => { + trackerUnderTest.setContext({ + ...defaultContext, + }); + trackerUnderTest.captureEvent('navigate', '/page-4'); + + expect(analyticsApiSpy.captureEvent).not.toHaveBeenCalled(); + }); + + it('never captures "routable-extension-rendered" events', () => { + trackerUnderTest.captureEvent(routableExtensionRenderedEvent, ''); + + expect(analyticsApiSpy.captureEvent).not.toHaveBeenCalled(); + }); + + it('captures deferred navigate event with expected context', () => { + // User navigates to a gathered mountpoint with multiple plugins + trackerUnderTest.setContext({ + ...defaultContext, + }); + trackerUnderTest.captureEvent('navigate', '/page-5'); + + // A routable extension is rendered with specific plugin context + trackerUnderTest.setContext({ + pluginId: 'some-plugin', + extensionId: 'App', + }); + trackerUnderTest.captureEvent(routableExtensionRenderedEvent, ''); + + // A non-navigate event + trackerUnderTest.captureEvent('click', 'test 5'); + + expect(analyticsApiSpy.captureEvent).toHaveBeenCalledTimes(2); + expect(analyticsApiSpy.captureEvent).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + action: 'navigate', + subject: '/page-5', + context: { + pluginId: 'some-plugin', + extensionId: 'App', + }, + }), + ); + expect(analyticsApiSpy.captureEvent).toHaveBeenNthCalledWith( + 2, + expect.objectContaining({ + action: 'click', + subject: 'test 5', + context: { + pluginId: 'some-plugin', + extensionId: 'App', + }, + }), + ); + }); + + it('captures deferred navigate event with expected context when second event is also deferrable', () => { + // User navigates to a gathered mountpoint with multiple plugins + trackerUnderTest.setContext({ + ...defaultContext, + }); + trackerUnderTest.captureEvent('navigate', '/page-6'); + + // A routable extension is rendered with specific plugin context + trackerUnderTest.setContext({ + pluginId: 'another-plugin', + extensionId: 'App', + }); + trackerUnderTest.captureEvent(routableExtensionRenderedEvent, ''); + + // User navigates to another gathered mountpoint with multiple plugins + trackerUnderTest.setContext({ + ...defaultContext, + }); + trackerUnderTest.captureEvent('navigate', '/page-6-2'); + + expect(analyticsApiSpy.captureEvent).toHaveBeenCalledTimes(1); + expect(analyticsApiSpy.captureEvent).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + action: 'navigate', + subject: '/page-6', + context: { + pluginId: 'another-plugin', + extensionId: 'App', + }, + }), + ); + }); + + it('captures deferred navigate event with default context when no extension is rendered in between', () => { + // User navigates to a gathered mountpoint with multiple plugins + trackerUnderTest.setContext({ + ...defaultContext, + }); + trackerUnderTest.captureEvent('navigate', '/page-7'); + + // A non-navigate event with no routable extension render in between + trackerUnderTest.captureEvent('click', 'test 7'); + + expect(analyticsApiSpy.captureEvent).toHaveBeenCalledTimes(2); + expect(analyticsApiSpy.captureEvent).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + action: 'navigate', + subject: '/page-7', + context: defaultContext, + }), + ); + expect(analyticsApiSpy.captureEvent).toHaveBeenNthCalledWith( + 2, + expect.objectContaining({ + action: 'click', + subject: 'test 7', + context: defaultContext, + }), + ); + }); + + it('captures deferred navigate event with expected context across separate trackers', () => { + // User navigates to a gathered mountpoint with multiple plugins + trackerUnderTest.setContext({ + ...defaultContext, + }); + trackerUnderTest.captureEvent('navigate', '/page-8'); + + // A routable extension is rendered with specific plugin context and + // captured via a separate tracker instance. + const anotherTracker = new Tracker(analyticsApiSpy, { + pluginId: 'the-plugin', + extensionId: 'App', + }); + anotherTracker.captureEvent(routableExtensionRenderedEvent, ''); + + // A non-navigate event is captured + const aThirdTracker = new Tracker(analyticsApiSpy); + aThirdTracker.captureEvent('click', 'test 8'); + + expect(analyticsApiSpy.captureEvent).toHaveBeenCalledTimes(2); + expect(analyticsApiSpy.captureEvent).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + action: 'navigate', + subject: '/page-8', + context: { + pluginId: 'the-plugin', + extensionId: 'App', + }, + }), + ); + expect(analyticsApiSpy.captureEvent).toHaveBeenNthCalledWith( + 2, + expect.objectContaining({ + action: 'click', + subject: 'test 8', + }), + ); + }); + + it('replaces extension context with "App" when capturing deferred events', () => { + // User navigates to a gathered mountpoint with multiple plugins + trackerUnderTest.setContext({ + ...defaultContext, + }); + trackerUnderTest.captureEvent('navigate', '/page-9'); + + // A routable extension is rendered with specific plugin context + trackerUnderTest.setContext({ + pluginId: 'very-plugin', + extensionId: 'ShouldBeReplaced', + }); + trackerUnderTest.captureEvent(routableExtensionRenderedEvent, ''); + + // A non-navigate event + trackerUnderTest.captureEvent('click', 'test 9'); + + // Extension context should have been replaced with just "App" + expect(analyticsApiSpy.captureEvent).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + action: 'navigate', + subject: '/page-9', + context: { + pluginId: 'very-plugin', + extensionId: 'App', + }, + }), + ); + }); + }); +}); diff --git a/packages/frontend-plugin-api/src/analytics/Tracker.ts b/packages/frontend-plugin-api/src/analytics/Tracker.ts new file mode 100644 index 0000000000..dd0d770354 --- /dev/null +++ b/packages/frontend-plugin-api/src/analytics/Tracker.ts @@ -0,0 +1,175 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { getOrCreateGlobalSingleton } from '@backstage/version-bridge'; +import { + AnalyticsApi, + AnalyticsEventAttributes, + AnalyticsTracker, +} from '../apis'; +import { AnalyticsContextValue } from './'; + +type TempGlobalEvents = { + /** + * Stores the most recent "gathered" mountpoint navigation. + */ + mostRecentGatheredNavigation?: { + action: string; + subject: string; + value?: number; + attributes?: AnalyticsEventAttributes; + context: AnalyticsContextValue; + }; + /** + * Stores the most recent routable extension render. + */ + mostRecentRoutableExtensionRender?: { + context: AnalyticsContextValue; + }; + /** + * Tracks whether or not a beforeunload event listener has already been + * registered. + */ + beforeUnloadRegistered: boolean; +}; + +/** + * Temporary global store for select event data. Used to make `navigate` events + * more accurate when gathered mountpoints are used. + */ +const globalEvents = getOrCreateGlobalSingleton( + 'core-plugin-api:analytics-tracker-events', + () => ({ + mostRecentGatheredNavigation: undefined, + mostRecentRoutableExtensionRender: undefined, + beforeUnloadRegistered: false, + }), +); + +/** + * Internal-only event representing when a routable extension is rendered. + */ +export const routableExtensionRenderedEvent = '_ROUTABLE-EXTENSION-RENDERED'; + +export class Tracker implements AnalyticsTracker { + constructor( + private readonly analyticsApi: AnalyticsApi, + private context: AnalyticsContextValue = { + pluginId: 'root', + extensionId: 'App', + }, + ) { + // Only register a single beforeunload event across all trackers. + if (!globalEvents.beforeUnloadRegistered) { + // Before the page unloads, attempt to capture any deferred navigation + // events that haven't yet been captured. + addEventListener( + 'beforeunload', + () => { + if (globalEvents.mostRecentGatheredNavigation) { + this.analyticsApi.captureEvent({ + ...globalEvents.mostRecentGatheredNavigation, + ...globalEvents.mostRecentRoutableExtensionRender, + }); + globalEvents.mostRecentGatheredNavigation = undefined; + globalEvents.mostRecentRoutableExtensionRender = undefined; + } + }, + { once: true, passive: true }, + ); + + // Prevent duplicate handlers from being registered. + globalEvents.beforeUnloadRegistered = true; + } + } + + setContext(context: AnalyticsContextValue) { + this.context = context; + } + + captureEvent( + action: string, + subject: string, + { + value, + attributes, + }: { value?: number; attributes?: AnalyticsEventAttributes } = {}, + ) { + // Never pass internal "_routeNodeType" context value. + const context = this.context; + + // Never fire the special "_routable-extension-rendered" internal event. + if (action === routableExtensionRenderedEvent) { + // But keep track of it if we're delaying a `navigate` event for a + // a gathered route node type. + if (globalEvents.mostRecentGatheredNavigation) { + globalEvents.mostRecentRoutableExtensionRender = { + context: { + ...context, + extensionId: 'App', + }, + }; + } + return; + } + + // If we are about to fire a real event, and we have an un-fired gathered + // mountpoint navigation on the global store, we need to fire the navigate + // event first, so this real event happens accurately after the navigation. + if (globalEvents.mostRecentGatheredNavigation) { + try { + this.analyticsApi.captureEvent({ + ...globalEvents.mostRecentGatheredNavigation, + ...globalEvents.mostRecentRoutableExtensionRender, + }); + } catch (e) { + // eslint-disable-next-line no-console + console.warn('Error during analytics event capture. %o', e); + } + + // Clear the global stores. + globalEvents.mostRecentGatheredNavigation = undefined; + globalEvents.mostRecentRoutableExtensionRender = undefined; + } + + // Never directly fire a navigation event on a gathered route with default + // contextual details. + if (action === 'navigate' && context.pluginId === 'root') { + // Instead, set it on the global store. + globalEvents.mostRecentGatheredNavigation = { + action, + subject, + value, + attributes, + context, + }; + return; + } + + try { + this.analyticsApi.captureEvent({ + action, + subject, + value, + attributes, + context, + }); + } catch (e) { + // eslint-disable-next-line no-console + console.warn('Error during analytics event capture. %o', e); + } + } +} diff --git a/packages/frontend-plugin-api/src/analytics/index.ts b/packages/frontend-plugin-api/src/analytics/index.ts new file mode 100644 index 0000000000..651e8e105e --- /dev/null +++ b/packages/frontend-plugin-api/src/analytics/index.ts @@ -0,0 +1,19 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { AnalyticsContext } from './AnalyticsContext'; +export type { AnalyticsContextValue, CommonAnalyticsContext } from './types'; +export { useAnalytics } from './useAnalytics'; diff --git a/packages/frontend-plugin-api/src/analytics/types.ts b/packages/frontend-plugin-api/src/analytics/types.ts new file mode 100644 index 0000000000..21347e2fc7 --- /dev/null +++ b/packages/frontend-plugin-api/src/analytics/types.ts @@ -0,0 +1,44 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Common analytics context attributes. + * + * @public + */ +export type CommonAnalyticsContext = { + /** + * The nearest known parent plugin where the event was captured. + */ + pluginId: string; + + /** + * The nearest known parent extension where the event was captured. + */ + /** + * The nearest known parent extension where the event was captured. + */ + extensionId: string; +}; + +/** + * Analytics context envelope. + * + * @public + */ +export type AnalyticsContextValue = CommonAnalyticsContext & { + [param in string]: string | boolean | number | undefined; +}; diff --git a/packages/frontend-plugin-api/src/analytics/useAnalytics.test.tsx b/packages/frontend-plugin-api/src/analytics/useAnalytics.test.tsx new file mode 100644 index 0000000000..beb16b1fbb --- /dev/null +++ b/packages/frontend-plugin-api/src/analytics/useAnalytics.test.tsx @@ -0,0 +1,61 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { renderHook } from '@testing-library/react'; +import { useAnalytics } from './useAnalytics'; +import { analyticsApiRef } from '@backstage/core-plugin-api'; +import { TestApiProvider } from '@backstage/test-utils'; +import React from 'react'; + +describe('useAnalytics', () => { + it('returns tracker with no implementation defined', () => { + // useApi throws an error because the Analytics API is not implemented + // But the result should still have a captureEvent method. + const { result } = renderHook(() => useAnalytics(), {}); + expect(result.current.captureEvent).toBeDefined(); + }); + + it('returns tracker from defined analytics api', () => { + const captureEvent = jest.fn(); + + // Calling the captureEvent method of the underlying implementation should + // pass along the given event as well as the default context. + const { result } = renderHook(() => useAnalytics(), { + wrapper: ({ children }) => ( + // Simulate useApi returning a valid tracker. + + {children} + + ), + }); + result.current.captureEvent('an action', 'a subject', { + value: 42, + attributes: { some: 'value' }, + }); + expect(captureEvent).toHaveBeenCalledWith({ + action: 'an action', + subject: 'a subject', + value: 42, + attributes: { + some: 'value', + }, + context: { + extensionId: 'App', + pluginId: 'root', + }, + }); + }); +}); diff --git a/packages/frontend-plugin-api/src/analytics/useAnalytics.tsx b/packages/frontend-plugin-api/src/analytics/useAnalytics.tsx new file mode 100644 index 0000000000..f3b5998de4 --- /dev/null +++ b/packages/frontend-plugin-api/src/analytics/useAnalytics.tsx @@ -0,0 +1,57 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { useApi } from '@backstage/core-plugin-api'; +import { useAnalyticsContext } from './AnalyticsContext'; +import { analyticsApiRef, AnalyticsTracker, AnalyticsApi } from '../apis'; +import { useRef } from 'react'; +import { Tracker } from './Tracker'; + +function useAnalyticsApi(): AnalyticsApi { + try { + return useApi(analyticsApiRef); + } catch { + return { captureEvent: () => {} }; + } +} + +/** + * Gets a pre-configured analytics tracker. + * + * @public + */ +export function useAnalytics(): AnalyticsTracker { + const trackerRef = useRef(null); + const context = useAnalyticsContext(); + // Our goal is to make this API truly optional for any/all consuming code + // (including tests). This hook runs last to ensure hook order is, as much as + // possible, maintained. + const analyticsApi = useAnalyticsApi(); + + function getTracker(): Tracker { + if (trackerRef.current === null) { + trackerRef.current = new Tracker(analyticsApi); + } + return trackerRef.current; + } + + const tracker = getTracker(); + // this is not ideal, but it allows to memoize the tracker + // without explicitly set the context as dependency. + tracker.setContext(context); + + return tracker; +} diff --git a/packages/frontend-plugin-api/src/apis/definitions/AnalyticsApi.ts b/packages/frontend-plugin-api/src/apis/definitions/AnalyticsApi.ts new file mode 100644 index 0000000000..15b09482b7 --- /dev/null +++ b/packages/frontend-plugin-api/src/apis/definitions/AnalyticsApi.ts @@ -0,0 +1,131 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ApiRef, createApiRef } from '@backstage/core-plugin-api'; +import { AnalyticsContextValue } from '../../analytics/types'; + +/** + * Represents an event worth tracking in an analytics system that could inform + * how users of a Backstage instance are using its features. + * + * @public + */ +export type AnalyticsEvent = { + /** + * A string that identifies the event being tracked by the type of action the + * event represents. Be careful not to encode extra metadata in this string + * that should instead be placed in the Analytics Context or attributes. + * Examples include: + * + * - view + * - click + * - filter + * - search + * - hover + * - scroll + */ + action: string; + + /** + * A string that uniquely identifies the object that the action is being + * taken on. Examples include: + * + * - The path of the page viewed + * - The url of the link clicked + * - The value that was filtered by + * - The text that was searched for + */ + subject: string; + + /** + * An optional numeric value relevant to the event that could be aggregated + * by analytics tools. Examples include: + * + * - The index or position of the clicked element in an ordered list + * - The percentage of an element that has been scrolled through + * - The amount of time that has elapsed since a fixed point + * - A satisfaction score on a fixed scale + */ + value?: number; + + /** + * Optional, additional attributes (representing dimensions or metrics) + * specific to the event that could be forwarded on to analytics systems. + */ + attributes?: AnalyticsEventAttributes; + + /** + * Contextual metadata relating to where the event was captured and by whom. + * This could include information about the route, plugin, or extension in + * which an event was captured. + */ + context: AnalyticsContextValue; +}; + +/** + * A structure allowing other arbitrary metadata to be provided by analytics + * event emitters. + * + * @public + */ +export type AnalyticsEventAttributes = { + [attribute in string]: string | boolean | number; +}; + +/** + * Represents a tracker with methods that can be called to track events in a + * configured analytics service. + * + * @public + */ +export type AnalyticsTracker = { + captureEvent: ( + action: string, + subject: string, + options?: { + value?: number; + attributes?: AnalyticsEventAttributes; + }, + ) => void; +}; + +/** + * The Analytics API is used to track user behavior in a Backstage instance. + * + * @remarks + * + * To instrument your App or Plugin, retrieve an analytics tracker using the + * useAnalytics() hook. This will return a pre-configured AnalyticsTracker + * with relevant methods for instrumentation. + * + * @public + */ +export type AnalyticsApi = { + /** + * Primary event handler responsible for compiling and forwarding events to + * an analytics system. + */ + captureEvent(event: AnalyticsEvent): void; +}; + +/** + * The API reference of {@link AnalyticsApi}. + * + * @public + */ +export const analyticsApiRef: ApiRef = createApiRef({ + id: 'core.analytics', +}); diff --git a/packages/frontend-plugin-api/src/apis/definitions/ComponentsApi.ts b/packages/frontend-plugin-api/src/apis/definitions/ComponentsApi.ts new file mode 100644 index 0000000000..b55ed96549 --- /dev/null +++ b/packages/frontend-plugin-api/src/apis/definitions/ComponentsApi.ts @@ -0,0 +1,37 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { createApiRef } from '@backstage/core-plugin-api'; +import { ComponentRef } from '../../components'; + +/** + * API for looking up components based on component refs. + * + * @public + */ +export interface ComponentsApi { + // TODO: Should component refs also provide the default implementation so that we're guaranteed to get a component? + getComponent(ref: ComponentRef): T; +} + +/** + * The `ApiRef` of {@link ComponentsApi}. + * + * @public + */ +export const componentsApiRef = createApiRef({ + id: 'core.components', +}); diff --git a/packages/frontend-plugin-api/src/apis/definitions/index.ts b/packages/frontend-plugin-api/src/apis/definitions/index.ts index 420942cb19..f2496ee61a 100644 --- a/packages/frontend-plugin-api/src/apis/definitions/index.ts +++ b/packages/frontend-plugin-api/src/apis/definitions/index.ts @@ -34,6 +34,7 @@ export * from './auth'; export * from './AlertApi'; export * from './AppThemeApi'; +export * from './ComponentsApi'; export * from './ConfigApi'; export * from './DiscoveryApi'; export * from './ErrorApi'; @@ -42,3 +43,4 @@ export * from './FetchApi'; export * from './IdentityApi'; export * from './OAuthRequestApi'; export * from './StorageApi'; +export * from './AnalyticsApi'; diff --git a/packages/frontend-plugin-api/src/components/ComponentRef.tsx b/packages/frontend-plugin-api/src/components/ComponentRef.tsx new file mode 100644 index 0000000000..2db85b9a42 --- /dev/null +++ b/packages/frontend-plugin-api/src/components/ComponentRef.tsx @@ -0,0 +1,68 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + CoreBootErrorPageComponent, + CoreErrorBoundaryFallbackComponent, + CoreNotFoundErrorPageComponent, + CoreProgressComponent, +} from '../types'; + +/** @public */ +export type ComponentRef = { + id: string; + T: T; +}; + +/** @public */ +export function createComponentRef(options: { + id: string; +}): ComponentRef { + const { id } = options; + return { + id, + get T(): T { + throw new Error(`tried to read ComponentRef.T of ${id}`); + }, + }; +} + +const coreProgressComponentRef = createComponentRef({ + id: 'core.components.progress', +}); + +const coreBootErrorPageComponentRef = + createComponentRef({ + id: 'core.components.bootErrorPage', + }); + +const coreNotFoundErrorPageComponentRef = + createComponentRef({ + id: 'core.components.notFoundErrorPage', + }); + +const coreErrorBoundaryFallbackComponentRef = + createComponentRef({ + id: 'core.components.errorBoundaryFallback', + }); + +/** @public */ +export const coreComponentsRefs = { + progress: coreProgressComponentRef, + bootErrorPage: coreBootErrorPageComponentRef, + notFoundErrorPage: coreNotFoundErrorPageComponentRef, + errorBoundaryFallback: coreErrorBoundaryFallbackComponentRef, +}; diff --git a/packages/frontend-plugin-api/src/components/ExtensionBoundary.test.tsx b/packages/frontend-plugin-api/src/components/ExtensionBoundary.test.tsx index 49a9f95ed2..669335d577 100644 --- a/packages/frontend-plugin-api/src/components/ExtensionBoundary.test.tsx +++ b/packages/frontend-plugin-api/src/components/ExtensionBoundary.test.tsx @@ -24,11 +24,10 @@ import { createRouteRef } from '../routing'; import { createExtensionTester } from '@backstage/frontend-test-utils'; const wrapInBoundaryExtension = (element: JSX.Element) => { - const id = 'plugin.extension'; const routeRef = createRouteRef(); return createExtension({ - id, - attachTo: { id: 'core.routes', input: 'routes' }, + name: 'test', + attachTo: { id: 'core/routes', input: 'routes' }, output: { element: coreExtensionData.reactElement, path: coreExtensionData.routePath, @@ -84,15 +83,18 @@ describe('ExtensionBoundary', () => { ), ).render(); - await waitFor(() => - expect(analyticsApiMock.getEvents()[0]).toMatchObject({ + await waitFor(() => { + const event = analyticsApiMock + .getEvents() + .find(e => e.subject === subject); + + expect(event).toMatchObject({ action, subject, context: { - extension: 'plugin.extension', - routeRef: 'unknown', + extensionId: 'test', }, - }), - ); + }); + }); }); }); diff --git a/packages/frontend-plugin-api/src/components/ExtensionBoundary.tsx b/packages/frontend-plugin-api/src/components/ExtensionBoundary.tsx index 8fad8d8336..623c875837 100644 --- a/packages/frontend-plugin-api/src/components/ExtensionBoundary.tsx +++ b/packages/frontend-plugin-api/src/components/ExtensionBoundary.tsx @@ -14,10 +14,14 @@ * limitations under the License. */ -import React, { PropsWithChildren, ReactNode, useEffect } from 'react'; +import React, { + PropsWithChildren, + ReactNode, + Suspense, + useEffect, +} from 'react'; import { AnalyticsContext, useAnalytics } from '@backstage/core-plugin-api'; import { ErrorBoundary } from './ErrorBoundary'; -import { ExtensionSuspense } from './ExtensionSuspense'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports import { routableExtensionRenderedEvent } from '../../../core-plugin-api/src/analytics/Tracker'; import { AppNode } from '../apis'; @@ -55,17 +59,17 @@ export function ExtensionBoundary(props: ExtensionBoundaryProps) { // Skipping "routeRef" attribute in the new system, the extension "id" should provide more insight const attributes = { - extension: node.spec.id, + extensionId: node.spec.id, pluginId: node.spec.source?.id, }; return ( - + {children} - + ); } diff --git a/packages/frontend-plugin-api/src/components/index.ts b/packages/frontend-plugin-api/src/components/index.ts index 7056a59271..a7d947599f 100644 --- a/packages/frontend-plugin-api/src/components/index.ts +++ b/packages/frontend-plugin-api/src/components/index.ts @@ -14,6 +14,8 @@ * limitations under the License. */ +export { coreComponentsRefs, type ComponentRef } from './ComponentRef'; + export { ExtensionBoundary, type ExtensionBoundaryProps, diff --git a/packages/frontend-plugin-api/src/extensions/createApiExtension.test.ts b/packages/frontend-plugin-api/src/extensions/createApiExtension.test.ts index e90c0b6b85..2585b0d694 100644 --- a/packages/frontend-plugin-api/src/extensions/createApiExtension.test.ts +++ b/packages/frontend-plugin-api/src/extensions/createApiExtension.test.ts @@ -26,13 +26,14 @@ describe('createApiExtension', () => { factory: () => ({ foo: 'bar' }), }); - const extension = createApiExtension({ - factory, - }); - - expect(extension).toEqual({ - $$type: '@backstage/Extension', - id: 'apis.test', + expect( + createApiExtension({ + factory, + }), + ).toEqual({ + $$type: '@backstage/ExtensionDefinition', + kind: 'api', + namespace: 'test', attachTo: { id: 'core', input: 'apis' }, disabled: false, configSchema: undefined, @@ -65,8 +66,9 @@ describe('createApiExtension', () => { }); // boo expect(extension).toEqual({ - $$type: '@backstage/Extension', - id: 'apis.test', + $$type: '@backstage/ExtensionDefinition', + kind: 'api', + namespace: 'test', attachTo: { id: 'core', input: 'apis' }, disabled: false, configSchema: undefined, diff --git a/packages/frontend-plugin-api/src/extensions/createApiExtension.ts b/packages/frontend-plugin-api/src/extensions/createApiExtension.ts index 7b6dded022..bbaef3c849 100644 --- a/packages/frontend-plugin-api/src/extensions/createApiExtension.ts +++ b/packages/frontend-plugin-api/src/extensions/createApiExtension.ts @@ -17,7 +17,7 @@ import { AnyApiFactory, AnyApiRef } from '@backstage/core-plugin-api'; import { PortableSchema } from '../schema'; import { - ExtensionInputValues, + ResolvedExtensionInputs, createExtension, coreExtensionData, } from '../wiring'; @@ -34,7 +34,7 @@ export function createApiExtension< api: AnyApiRef; factory: (options: { config: TConfig; - inputs: Expand>; + inputs: Expand>; }) => AnyApiFactory; } | { @@ -51,7 +51,10 @@ export function createApiExtension< 'api' in options ? options.api : (factory as { api: AnyApiRef }).api; return createExtension({ - id: `apis.${apiRef.id}`, + kind: 'api', + // Since ApiRef IDs use a global namespace we use the namespace here in order to override + // potential plugin IDs and always end up with the format `api:` + namespace: apiRef.id, attachTo: { id: 'core', input: 'apis' }, inputs: extensionInputs, configSchema, diff --git a/packages/frontend-plugin-api/src/extensions/createComponentExtension.tsx b/packages/frontend-plugin-api/src/extensions/createComponentExtension.tsx new file mode 100644 index 0000000000..601ee81d11 --- /dev/null +++ b/packages/frontend-plugin-api/src/extensions/createComponentExtension.tsx @@ -0,0 +1,88 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React, { lazy } from 'react'; +import { + AnyExtensionInputMap, + ResolvedExtensionInputs, + coreExtensionData, + createExtension, +} from '../wiring'; +import { Expand } from '../types'; +import { PortableSchema } from '../schema'; +import { ExtensionBoundary, ComponentRef } from '../components'; + +/** @public */ +export function createComponentExtension< + TRef extends ComponentRef, + TConfig extends {}, + TInputs extends AnyExtensionInputMap, +>(options: { + ref: TRef; + name?: string; + disabled?: boolean; + inputs?: TInputs; + configSchema?: PortableSchema; + component: + | { + lazy: (values: { + config: TConfig; + inputs: Expand>; + }) => Promise; + } + | { + sync: (values: { + config: TConfig; + inputs: Expand>; + }) => TRef['T']; + }; +}) { + return createExtension({ + kind: 'component', + namespace: options.ref.id, + name: options.name, + attachTo: { id: 'core', input: 'components' }, + inputs: options.inputs, + disabled: options.disabled, + configSchema: options.configSchema, + output: { + component: coreExtensionData.component, + }, + factory({ config, inputs, node }) { + let ExtensionComponent: TRef['T']; + + if ('sync' in options.component) { + ExtensionComponent = options.component.sync({ config, inputs }); + } else { + const loader = options.component.lazy({ config, inputs }); + ExtensionComponent = lazy(() => + loader.then(component => ({ default: component })), + ); + } + + return { + component: { + ref: options.ref, + impl: props => ( + + + + ), + }, + }; + }, + }); +} diff --git a/packages/frontend-plugin-api/src/extensions/createNavItemExtension.tsx b/packages/frontend-plugin-api/src/extensions/createNavItemExtension.tsx index 84fbe5ffee..207778fbd7 100644 --- a/packages/frontend-plugin-api/src/extensions/createNavItemExtension.tsx +++ b/packages/frontend-plugin-api/src/extensions/createNavItemExtension.tsx @@ -24,15 +24,18 @@ import { RouteRef } from '../routing'; * @public */ export function createNavItemExtension(options: { - id: string; + namespace?: string; + name?: string; routeRef: RouteRef; title: string; icon: IconComponent; }) { - const { id, routeRef, title, icon } = options; + const { routeRef, title, icon, namespace, name } = options; return createExtension({ - id, - attachTo: { id: 'core.nav', input: 'items' }, + namespace, + name, + kind: 'nav-item', + attachTo: { id: 'core/nav', input: 'items' }, configSchema: createSchemaFromZod(z => z.object({ title: z.string().default(title), diff --git a/packages/frontend-plugin-api/src/extensions/createNavLogoExtension.test.tsx b/packages/frontend-plugin-api/src/extensions/createNavLogoExtension.test.tsx index 93efadb0e1..ca95fad7bc 100644 --- a/packages/frontend-plugin-api/src/extensions/createNavLogoExtension.test.tsx +++ b/packages/frontend-plugin-api/src/extensions/createNavLogoExtension.test.tsx @@ -25,14 +25,15 @@ describe('createNavLogoExtension', () => { it('creates the extension properly', () => { expect( createNavLogoExtension({ - id: 'test', + name: 'test', logoFull:
Logo Full
, logoIcon:
Logo Icon
, }), ).toEqual({ - $$type: '@backstage/Extension', - id: 'test', - attachTo: { id: 'core.nav', input: 'logos' }, + $$type: '@backstage/ExtensionDefinition', + kind: 'nav-logo', + name: 'test', + attachTo: { id: 'core/nav', input: 'logos' }, disabled: false, inputs: {}, output: { diff --git a/packages/frontend-plugin-api/src/extensions/createNavLogoExtension.tsx b/packages/frontend-plugin-api/src/extensions/createNavLogoExtension.tsx index 5cfff81a9d..878764bad7 100644 --- a/packages/frontend-plugin-api/src/extensions/createNavLogoExtension.tsx +++ b/packages/frontend-plugin-api/src/extensions/createNavLogoExtension.tsx @@ -21,14 +21,17 @@ import { coreExtensionData, createExtension } from '../wiring'; * @public */ export function createNavLogoExtension(options: { - id: string; + name?: string; + namespace?: string; logoIcon: JSX.Element; logoFull: JSX.Element; }) { - const { id, logoIcon, logoFull } = options; + const { logoIcon, logoFull } = options; return createExtension({ - id, - attachTo: { id: 'core.nav', input: 'logos' }, + kind: 'nav-logo', + name: options?.name, + namespace: options?.namespace, + attachTo: { id: 'core/nav', input: 'logos' }, output: { logos: coreExtensionData.logoElements, }, diff --git a/packages/frontend-plugin-api/src/extensions/createPageExtension.test.tsx b/packages/frontend-plugin-api/src/extensions/createPageExtension.test.tsx index f75821944a..525f10ee61 100644 --- a/packages/frontend-plugin-api/src/extensions/createPageExtension.test.tsx +++ b/packages/frontend-plugin-api/src/extensions/createPageExtension.test.tsx @@ -36,14 +36,15 @@ describe('createPageExtension', () => { expect( createPageExtension({ - id: 'test', + name: 'test', configSchema, loader: async () =>
, }), ).toEqual({ - $$type: '@backstage/Extension', - id: 'test', - attachTo: { id: 'core.routes', input: 'routes' }, + $$type: '@backstage/ExtensionDefinition', + name: 'test', + kind: 'page', + attachTo: { id: 'core/routes', input: 'routes' }, configSchema: expect.anything(), disabled: false, inputs: {}, @@ -57,7 +58,7 @@ describe('createPageExtension', () => { expect( createPageExtension({ - id: 'test', + name: 'test', attachTo: { id: 'other', input: 'place' }, disabled: true, configSchema, @@ -69,8 +70,9 @@ describe('createPageExtension', () => { loader: async () =>
, }), ).toEqual({ - $$type: '@backstage/Extension', - id: 'test', + $$type: '@backstage/ExtensionDefinition', + name: 'test', + kind: 'page', attachTo: { id: 'other', input: 'place' }, configSchema: expect.anything(), disabled: true, @@ -89,14 +91,15 @@ describe('createPageExtension', () => { expect( createPageExtension({ - id: 'test', + name: 'test', defaultPath: '/here', loader: async () =>
, }), ).toEqual({ - $$type: '@backstage/Extension', - id: 'test', - attachTo: { id: 'core.routes', input: 'routes' }, + $$type: '@backstage/ExtensionDefinition', + name: 'test', + kind: 'page', + attachTo: { id: 'core/routes', input: 'routes' }, configSchema: expect.anything(), disabled: false, inputs: {}, @@ -118,7 +121,6 @@ describe('createPageExtension', () => { createExtensionTester( createPageExtension({ - id: 'plugin.page', defaultPath: '/', loader: async () =>
Component
, }), diff --git a/packages/frontend-plugin-api/src/extensions/createPageExtension.tsx b/packages/frontend-plugin-api/src/extensions/createPageExtension.tsx index 99733ba367..3b936ef210 100644 --- a/packages/frontend-plugin-api/src/extensions/createPageExtension.tsx +++ b/packages/frontend-plugin-api/src/extensions/createPageExtension.tsx @@ -20,12 +20,12 @@ import { createSchemaFromZod, PortableSchema } from '../schema'; import { coreExtensionData, createExtension, - Extension, - ExtensionInputValues, + ResolvedExtensionInputs, AnyExtensionInputMap, } from '../wiring'; import { RouteRef } from '../routing'; import { Expand } from '../types'; +import { ExtensionDefinition } from '../wiring/createExtension'; /** * Helper for creating extensions for a routable React page component. @@ -44,19 +44,18 @@ export function createPageExtension< configSchema: PortableSchema; } ) & { - id: string; + namespace?: string; + name?: string; attachTo?: { id: string; input: string }; disabled?: boolean; inputs?: TInputs; routeRef?: RouteRef; loader: (options: { config: TConfig; - inputs: Expand>; + inputs: Expand>; }) => Promise; }, -): Extension { - const { id } = options; - +): ExtensionDefinition { const configSchema = 'configSchema' in options ? options.configSchema @@ -65,8 +64,10 @@ export function createPageExtension< ) as PortableSchema); return createExtension({ - id, - attachTo: options.attachTo ?? { id: 'core.routes', input: 'routes' }, + kind: 'page', + namespace: options.namespace, + name: options.name, + attachTo: options.attachTo ?? { id: 'core/routes', input: 'routes' }, configSchema, inputs: options.inputs, disabled: options.disabled, diff --git a/packages/frontend-plugin-api/src/extensions/createSignInPageExtension.test.tsx b/packages/frontend-plugin-api/src/extensions/createSignInPageExtension.test.tsx index 964941e213..d8835ae788 100644 --- a/packages/frontend-plugin-api/src/extensions/createSignInPageExtension.test.tsx +++ b/packages/frontend-plugin-api/src/extensions/createSignInPageExtension.test.tsx @@ -23,13 +23,13 @@ import { coreExtensionData, createExtension } from '../wiring'; describe('createSignInPageExtension', () => { it('renders a sign-in page', async () => { const SignInPage = createSignInPageExtension({ - id: 'test', + name: 'test', loader: async () => () =>
, }); createExtensionTester( createExtension({ - id: 'dummy', + name: 'dummy', attachTo: { id: 'ignored', input: 'ignored' }, output: { element: coreExtensionData.reactElement, diff --git a/packages/frontend-plugin-api/src/extensions/createSignInPageExtension.tsx b/packages/frontend-plugin-api/src/extensions/createSignInPageExtension.tsx index f869ed8f13..d04c29fb9b 100644 --- a/packages/frontend-plugin-api/src/extensions/createSignInPageExtension.tsx +++ b/packages/frontend-plugin-api/src/extensions/createSignInPageExtension.tsx @@ -19,10 +19,10 @@ import { ExtensionBoundary } from '../components'; import { PortableSchema } from '../schema'; import { createExtension, - Extension, - ExtensionInputValues, + ResolvedExtensionInputs, AnyExtensionInputMap, createExtensionDataRef, + ExtensionDefinition, } from '../wiring'; import { Expand } from '../types'; import { SignInPageProps } from '@backstage/core-plugin-api'; @@ -39,21 +39,22 @@ export function createSignInPageExtension< TConfig extends {}, TInputs extends AnyExtensionInputMap, >(options: { - id: string; + namespace?: string; + name?: string; attachTo?: { id: string; input: string }; configSchema?: PortableSchema; disabled?: boolean; inputs?: TInputs; loader: (options: { config: TConfig; - inputs: Expand>; + inputs: Expand>; }) => Promise>; -}): Extension { - const { id } = options; - +}): ExtensionDefinition { return createExtension({ - id, - attachTo: options.attachTo ?? { id: 'core.router', input: 'signInPage' }, + kind: 'sign-in-page', + namespace: options?.namespace, + name: options?.name, + attachTo: options.attachTo ?? { id: 'core/router', input: 'signInPage' }, configSchema: options.configSchema, inputs: options.inputs, disabled: options.disabled, diff --git a/packages/frontend-plugin-api/src/extensions/createThemeExtension.ts b/packages/frontend-plugin-api/src/extensions/createThemeExtension.ts index d41121d51c..5554a08874 100644 --- a/packages/frontend-plugin-api/src/extensions/createThemeExtension.ts +++ b/packages/frontend-plugin-api/src/extensions/createThemeExtension.ts @@ -20,7 +20,9 @@ import { AppTheme } from '@backstage/core-plugin-api'; /** @public */ export function createThemeExtension(theme: AppTheme) { return createExtension({ - id: `themes.${theme.id}`, + kind: 'theme', + namespace: 'app', + name: theme.id, attachTo: { id: 'core', input: 'themes' }, output: { theme: coreExtensionData.theme, diff --git a/packages/frontend-plugin-api/src/extensions/index.ts b/packages/frontend-plugin-api/src/extensions/index.ts index d696774272..a3be81f2a3 100644 --- a/packages/frontend-plugin-api/src/extensions/index.ts +++ b/packages/frontend-plugin-api/src/extensions/index.ts @@ -19,3 +19,4 @@ export { createPageExtension } from './createPageExtension'; export { createNavItemExtension } from './createNavItemExtension'; export { createSignInPageExtension } from './createSignInPageExtension'; export { createThemeExtension } from './createThemeExtension'; +export { createComponentExtension } from './createComponentExtension'; diff --git a/packages/frontend-plugin-api/src/index.ts b/packages/frontend-plugin-api/src/index.ts index 3248e79945..a38a7991fb 100644 --- a/packages/frontend-plugin-api/src/index.ts +++ b/packages/frontend-plugin-api/src/index.ts @@ -20,6 +20,7 @@ * @packageDocumentation */ +export * from './analytics'; export * from './apis'; export * from './components'; export * from './extensions'; @@ -28,3 +29,10 @@ export * from './routing'; export * from './schema'; export * from './apis/system'; export * from './wiring'; + +export type { + CoreProgressComponent, + CoreBootErrorPageComponent, + CoreNotFoundErrorPageComponent, + CoreErrorBoundaryFallbackComponent, +} from './types'; diff --git a/packages/frontend-plugin-api/src/types.ts b/packages/frontend-plugin-api/src/types.ts index 8b6f02a942..f3f68c362c 100644 --- a/packages/frontend-plugin-api/src/types.ts +++ b/packages/frontend-plugin-api/src/types.ts @@ -14,9 +14,37 @@ * limitations under the License. */ +import { BackstagePlugin } from '@backstage/core-plugin-api'; +import { ComponentType, PropsWithChildren } from 'react'; + // TODO(Rugvip): This might be a quite useful utility type, maybe add to @backstage/types? /** * Utility type to expand type aliases into their equivalent type. * @ignore */ export type Expand = T extends infer O ? { [K in keyof O]: O[K] } : never; + +/** @public */ +export type CoreProgressComponent = ComponentType>; + +/** @public */ +export type CoreBootErrorPageComponent = ComponentType< + PropsWithChildren<{ + step: 'load-config' | 'load-chunk'; + error: Error; + }> +>; + +/** @public */ +export type CoreNotFoundErrorPageComponent = ComponentType< + PropsWithChildren<{}> +>; + +/** @public */ +export type CoreErrorBoundaryFallbackComponent = ComponentType< + PropsWithChildren<{ + plugin?: BackstagePlugin; + error: Error; + resetError: () => void; + }> +>; diff --git a/packages/frontend-plugin-api/src/wiring/coreExtensionData.ts b/packages/frontend-plugin-api/src/wiring/coreExtensionData.ts index 9ffdb835ba..9bdb4b23eb 100644 --- a/packages/frontend-plugin-api/src/wiring/coreExtensionData.ts +++ b/packages/frontend-plugin-api/src/wiring/coreExtensionData.ts @@ -14,14 +14,15 @@ * limitations under the License. */ -import { JSX } from 'react'; +import { ComponentType, JSX } from 'react'; import { AnyApiFactory, AppTheme, IconComponent, } from '@backstage/core-plugin-api'; -import { createExtensionDataRef } from './createExtensionDataRef'; import { RouteRef } from '../routing'; +import { ComponentRef } from '../components'; +import { createExtensionDataRef } from './createExtensionDataRef'; /** @public */ export type NavTarget = { @@ -45,4 +46,8 @@ export const coreExtensionData = { navTarget: createExtensionDataRef('core.nav.target'), theme: createExtensionDataRef('core.theme'), logoElements: createExtensionDataRef('core.logos'), + component: createExtensionDataRef<{ + ref: ComponentRef>; + impl: ComponentType; + }>('component.ref'), }; diff --git a/packages/frontend-plugin-api/src/wiring/createExtension.test.ts b/packages/frontend-plugin-api/src/wiring/createExtension.test.ts index 071272d58f..22db95082a 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtension.test.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtension.test.ts @@ -25,7 +25,7 @@ function unused(..._any: any[]) {} describe('createExtension', () => { it('should create an extension with a simple output', () => { const baseConfig = { - id: 'test', + namespace: 'test', attachTo: { id: 'root', input: 'default' }, output: { foo: stringData, @@ -39,7 +39,7 @@ describe('createExtension', () => { }; }, }); - expect(extension.id).toBe('test'); + expect(extension.namespace).toBe('test'); // When declared as an error function without a block the TypeScript errors // are a more specific and will point at the property that is problematic. @@ -163,7 +163,7 @@ describe('createExtension', () => { it('should create an extension with a some optional output', () => { const baseConfig = { - id: 'test', + namespace: 'test', attachTo: { id: 'root', input: 'default' }, output: { foo: stringData, @@ -176,7 +176,7 @@ describe('createExtension', () => { foo: 'bar', }), }); - expect(extension.id).toBe('test'); + expect(extension.namespace).toBe('test'); createExtension({ ...baseConfig, @@ -233,7 +233,7 @@ describe('createExtension', () => { it('should create an extension with input', () => { const extension = createExtension({ - id: 'test', + namespace: 'test', attachTo: { id: 'root', input: 'default' }, inputs: { mixed: createExtensionInput({ @@ -251,34 +251,34 @@ describe('createExtension', () => { foo: stringData, }, factory({ inputs }) { - const a1: string = inputs.mixed?.[0].required; + const a1: string = inputs.mixed?.[0].output.required; // @ts-expect-error - const a2: number = inputs.mixed?.[0].required; + const a2: number = inputs.mixed?.[0].output.required; // @ts-expect-error - const a3: any = inputs.mixed?.[0].nonExistent; + const a3: any = inputs.mixed?.[0].output.nonExistent; unused(a1, a2, a3); - const b1: string | undefined = inputs.mixed?.[0].optional; + const b1: string | undefined = inputs.mixed?.[0].output.optional; // @ts-expect-error - const b2: string = inputs.mixed?.[0].optional; + const b2: string = inputs.mixed?.[0].output.optional; // @ts-expect-error - const b3: number = inputs.mixed?.[0].optional; + const b3: number = inputs.mixed?.[0].output.optional; // @ts-expect-error - const b4: number | undefined = inputs.mixed?.[0].optional; + const b4: number | undefined = inputs.mixed?.[0].output.optional; unused(b1, b2, b3, b4); - const c1: string = inputs.onlyRequired?.[0].required; + const c1: string = inputs.onlyRequired?.[0].output.required; // @ts-expect-error - const c2: number = inputs.onlyRequired?.[0].required; + const c2: number = inputs.onlyRequired?.[0].output.required; unused(c1, c2); - const d1: string | undefined = inputs.onlyOptional?.[0].optional; + const d1: string | undefined = inputs.onlyOptional?.[0].output.optional; // @ts-expect-error - const d2: string = inputs.onlyOptional?.[0].optional; + const d2: string = inputs.onlyOptional?.[0].output.optional; // @ts-expect-error - const d3: number = inputs.onlyOptional?.[0].optional; + const d3: number = inputs.onlyOptional?.[0].output.optional; // @ts-expect-error - const d4: number | undefined = inputs.onlyOptional?.[0].optional; + const d4: number | undefined = inputs.onlyOptional?.[0].output.optional; unused(d1, d2, d3, d4); return { @@ -286,6 +286,6 @@ describe('createExtension', () => { }; }, }); - expect(extension.id).toBe('test'); + expect(extension.namespace).toBe('test'); }); }); diff --git a/packages/frontend-plugin-api/src/wiring/createExtension.ts b/packages/frontend-plugin-api/src/wiring/createExtension.ts index 9443caeb66..cac2ac7b49 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtension.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtension.ts @@ -52,18 +52,28 @@ export type ExtensionDataValues = { }; /** - * Converts an extension input map into the matching concrete input values type. + * Convert a single extension input into a matching resolved input. * @public */ -export type ExtensionInputValues< +export type ResolvedExtensionInput = + { + extensionId: string; + output: ExtensionDataValues; + }; + +/** + * Converts an extension input map into a matching collection of resolved inputs. + * @public + */ +export type ResolvedExtensionInputs< TInputs extends { [name in string]: ExtensionInput }, > = { [InputName in keyof TInputs]: false extends TInputs[InputName]['config']['singleton'] - ? Array>> + ? Array>> : false extends TInputs[InputName]['config']['optional'] - ? Expand> + ? Expand> : Expand< - ExtensionDataValues | undefined + ResolvedExtensionInput | undefined >; }; @@ -73,7 +83,9 @@ export interface CreateExtensionOptions< TInputs extends AnyExtensionInputMap, TConfig, > { - id: string; + kind?: string; + namespace?: string; + name?: string; attachTo: { id: string; input: string }; disabled?: boolean; inputs?: TInputs; @@ -82,10 +94,28 @@ export interface CreateExtensionOptions< factory(options: { node: AppNode; config: TConfig; - inputs: Expand>; + inputs: Expand>; }): Expand>; } +/** @public */ +export interface ExtensionDefinition { + $$type: '@backstage/ExtensionDefinition'; + kind?: string; + namespace?: string; + name?: string; + attachTo: { id: string; input: string }; + disabled: boolean; + inputs: AnyExtensionInputMap; + output: AnyExtensionDataMap; + configSchema?: PortableSchema; + factory(options: { + node: AppNode; + config: TConfig; + inputs: ResolvedExtensionInputs; + }): ExtensionDataValues; +} + /** @public */ export interface Extension { $$type: '@backstage/Extension'; @@ -98,10 +128,7 @@ export interface Extension { factory(options: { node: AppNode; config: TConfig; - inputs: Record< - string, - undefined | Record | Array> - >; + inputs: ResolvedExtensionInputs; }): ExtensionDataValues; } @@ -112,16 +139,21 @@ export function createExtension< TConfig = never, >( options: CreateExtensionOptions, -): Extension { +): ExtensionDefinition { return { - ...options, + $$type: '@backstage/ExtensionDefinition', + kind: options.kind, + namespace: options.namespace, + name: options.name, + attachTo: options.attachTo, disabled: options.disabled ?? false, - $$type: '@backstage/Extension', inputs: options.inputs ?? {}, + output: options.output, + configSchema: options.configSchema, factory({ inputs, ...rest }) { // TODO: Simplify this, but TS wouldn't infer the input type for some reason return options.factory({ - inputs: inputs as Expand>, + inputs: inputs as Expand>, ...rest, }); }, diff --git a/packages/frontend-plugin-api/src/wiring/createExtensionOverrides.test.ts b/packages/frontend-plugin-api/src/wiring/createExtensionOverrides.test.ts index eb69f954e4..772a214490 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtensionOverrides.test.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtensionOverrides.test.ts @@ -26,6 +26,7 @@ describe('createExtensionOverrides', () => { { "$$type": "@backstage/ExtensionOverrides", "extensions": [], + "featureFlags": [], "version": "v1", } `); @@ -36,7 +37,21 @@ describe('createExtensionOverrides', () => { createExtensionOverrides({ extensions: [ createExtension({ - id: 'a', + name: 'a', + attachTo: { id: 'core', input: 'apis' }, + output: {}, + factory: () => ({}), + }), + createExtension({ + namespace: 'b', + attachTo: { id: 'core', input: 'apis' }, + output: {}, + factory: () => ({}), + }), + createExtension({ + kind: 'k', + namespace: 'c', + name: 'n', attachTo: { id: 'core', input: 'apis' }, output: {}, factory: () => ({}), @@ -53,13 +68,41 @@ describe('createExtensionOverrides', () => { "id": "core", "input": "apis", }, + "configSchema": undefined, "disabled": false, "factory": [Function], "id": "a", "inputs": {}, "output": {}, }, + { + "$$type": "@backstage/Extension", + "attachTo": { + "id": "core", + "input": "apis", + }, + "configSchema": undefined, + "disabled": false, + "factory": [Function], + "id": "b", + "inputs": {}, + "output": {}, + }, + { + "$$type": "@backstage/Extension", + "attachTo": { + "id": "core", + "input": "apis", + }, + "configSchema": undefined, + "disabled": false, + "factory": [Function], + "id": "k:c/n", + "inputs": {}, + "output": {}, + }, ], + "featureFlags": [], "version": "v1", } `); @@ -69,7 +112,7 @@ describe('createExtensionOverrides', () => { const overrides = createExtensionOverrides({ extensions: [ createExtension({ - id: 'a', + namespace: 'a', attachTo: { id: 'core', input: 'apis' }, output: {}, factory: () => ({}), diff --git a/packages/frontend-plugin-api/src/wiring/createExtensionOverrides.ts b/packages/frontend-plugin-api/src/wiring/createExtensionOverrides.ts index 2c64229eb9..6c368a0e8b 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtensionOverrides.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtensionOverrides.ts @@ -14,22 +14,26 @@ * limitations under the License. */ -import { Extension } from './createExtension'; +import { Extension, ExtensionDefinition } from './createExtension'; +import { resolveExtensionDefinition } from './resolveExtensionDefinition'; +import { FeatureFlagConfig } from './types'; /** @public */ export interface ExtensionOverridesOptions { - extensions: Extension[]; + extensions: ExtensionDefinition[]; + featureFlags?: FeatureFlagConfig[]; } /** @public */ export interface ExtensionOverrides { - $$type: '@backstage/ExtensionOverrides'; + readonly $$type: '@backstage/ExtensionOverrides'; } /** @internal */ export interface InternalExtensionOverrides extends ExtensionOverrides { - version: string; - extensions: Extension[]; + readonly version: 'v1'; + readonly extensions: Extension[]; + readonly featureFlags: FeatureFlagConfig[]; } /** @public */ @@ -39,7 +43,8 @@ export function createExtensionOverrides( return { $$type: '@backstage/ExtensionOverrides', version: 'v1', - extensions: options.extensions, + extensions: options.extensions.map(def => resolveExtensionDefinition(def)), + featureFlags: options.featureFlags ?? [], } as InternalExtensionOverrides; } @@ -50,12 +55,12 @@ export function toInternalExtensionOverrides( const internal = overrides as InternalExtensionOverrides; if (internal.$$type !== '@backstage/ExtensionOverrides') { throw new Error( - `Invalid translation resource, bad type '${internal.$$type}'`, + `Invalid extension overrides instance, bad type '${internal.$$type}'`, ); } if (internal.version !== 'v1') { throw new Error( - `Invalid translation resource, bad version '${internal.version}'`, + `Invalid extension overrides instance, bad version '${internal.version}'`, ); } return internal; diff --git a/packages/frontend-plugin-api/src/wiring/createPlugin.test.ts b/packages/frontend-plugin-api/src/wiring/createPlugin.test.ts index f69b54aa42..18054a5de2 100644 --- a/packages/frontend-plugin-api/src/wiring/createPlugin.test.ts +++ b/packages/frontend-plugin-api/src/wiring/createPlugin.test.ts @@ -28,48 +28,34 @@ import { createExtensionInput } from './createExtensionInput'; const nameExtensionDataRef = createExtensionDataRef('name'); -const TechRadarPage = createExtension({ - id: 'plugin.techradar.page', - attachTo: { id: 'test.output', input: 'names' }, +const Extension1 = createExtension({ + name: '1', + attachTo: { id: 'test/output', input: 'names' }, output: { name: nameExtensionDataRef, }, factory() { - return { name: 'TechRadar' }; + return { name: 'extension-1' }; }, }); -const CatalogPage = createExtension({ - id: 'plugin.catalog.page', - attachTo: { id: 'test.output', input: 'names' }, +const Extension2 = createExtension({ + name: '2', + attachTo: { id: 'test/output', input: 'names' }, output: { name: nameExtensionDataRef, }, configSchema: createSchemaFromZod(z => - z.object({ name: z.string().default('Catalog') }), + z.object({ name: z.string().default('extension-2') }), ), factory({ config }) { return { name: config.name }; }, }); -const TechDocsAddon = createExtension({ - id: 'plugin.techdocs.addon.example', - attachTo: { id: 'plugin.techdocs.page', input: 'addons' }, - output: { - name: nameExtensionDataRef, - }, - configSchema: createSchemaFromZod(z => - z.object({ name: z.string().default('TechDocsAddon') }), - ), - factory({ config }) { - return { name: config.name }; - }, -}); - -const TechDocsPage = createExtension({ - id: 'plugin.techdocs.page', - attachTo: { id: 'test.output', input: 'names' }, +const Extension3 = createExtension({ + name: '3', + attachTo: { id: 'test/output', input: 'names' }, inputs: { addons: createExtensionInput({ name: nameExtensionDataRef, @@ -79,12 +65,42 @@ const TechDocsPage = createExtension({ name: nameExtensionDataRef, }, factory({ inputs }) { - return { name: `TechDocs-${inputs.addons.map(n => n.name).join('-')}` }; + return { + name: `extension-3:${inputs.addons.map(n => n.output.name).join('-')}`, + }; + }, +}); + +const Child = createExtension({ + name: 'child', + attachTo: { id: 'test/3', input: 'addons' }, + output: { + name: nameExtensionDataRef, + }, + configSchema: createSchemaFromZod(z => + z.object({ name: z.string().default('child') }), + ), + factory({ config }) { + return { name: config.name }; + }, +}); + +const Child2 = createExtension({ + name: 'child2', + attachTo: { id: 'test/3', input: 'addons' }, + output: { + name: nameExtensionDataRef, + }, + configSchema: createSchemaFromZod(z => + z.object({ name: z.string().default('child2') }), + ), + factory({ config }) { + return { name: config.name }; }, }); const outputExtension = createExtension({ - id: 'test.output', + name: 'output', attachTo: { id: 'core', input: 'root' }, inputs: { names: createExtensionInput({ @@ -97,7 +113,7 @@ const outputExtension = createExtension({ factory({ inputs }) { return { element: React.createElement('span', {}, [ - `Names: ${inputs.names.map(n => n.name).join(', ')}`, + `Names: ${inputs.names.map(n => n.output.name).join(', ')}`, ]), }; }, @@ -118,40 +134,34 @@ function createTestAppRoot({ describe('createPlugin', () => { it('should create an empty plugin', () => { - const plugin = createPlugin({ id: 'empty' }); + const plugin = createPlugin({ id: 'test' }); expect(plugin).toBeDefined(); }); it('should create a plugin with extension instances', async () => { const plugin = createPlugin({ - id: 'empty', - extensions: [TechRadarPage, CatalogPage, outputExtension], + id: 'test', + extensions: [Extension1, Extension2, outputExtension], }); expect(plugin).toBeDefined(); await renderWithEffects( createTestAppRoot({ features: [plugin], - config: { app: { extensions: [{ 'core.router': false }] } }, + config: { app: { extensions: [{ 'core/router': false }] } }, }), ); await expect( - screen.findByText('Names: TechRadar, Catalog'), + screen.findByText('Names: extension-1, extension-2'), ).resolves.toBeInTheDocument(); }); it('should create a plugin with nested extension instances', async () => { const plugin = createPlugin({ - id: 'empty', - extensions: [ - TechRadarPage, - CatalogPage, - TechDocsPage, - TechDocsAddon, - outputExtension, - ], + id: 'test', + extensions: [Extension1, Extension2, Extension3, Child, outputExtension], }); expect(plugin).toBeDefined(); @@ -161,10 +171,10 @@ describe('createPlugin', () => { config: { app: { extensions: [ - { 'core.router': false }, + { 'core/router': false }, { - 'plugin.catalog.page': { - config: { name: 'CatalogRenamed' }, + 'test/2': { + config: { name: 'extension-2-renamed' }, }, }, ], @@ -175,8 +185,63 @@ describe('createPlugin', () => { await expect( screen.findByText( - 'Names: TechRadar, CatalogRenamed, TechDocs-TechDocsAddon', + 'Names: extension-1, extension-2-renamed, extension-3:child', ), ).resolves.toBeInTheDocument(); }); + + it('should create a plugin with nested extension instances and multiple children', async () => { + const plugin = createPlugin({ + id: 'test', + extensions: [ + Extension1, + Extension2, + Extension3, + Child, + Child2, + outputExtension, + ], + }); + expect(plugin).toBeDefined(); + + await renderWithEffects( + createTestAppRoot({ + features: [plugin], + config: { + app: { + extensions: [{ 'core/router': false }], + }, + }, + }), + ); + + await expect( + screen.findByText( + 'Names: extension-1, extension-2, extension-3:child-child2', + ), + ).resolves.toBeInTheDocument(); + }); + + it('should throw on duplicate extensions', async () => { + expect(() => + createPlugin({ + id: 'test', + extensions: [Extension1, Extension1], + }), + ).toThrow("Plugin 'test' provided duplicate extensions: test/1"); + + expect(() => + createPlugin({ + id: 'test', + extensions: [ + Extension1, + Extension2, + Extension2, + Extension3, + Extension3, + Extension3, + ], + }), + ).toThrow("Plugin 'test' provided duplicate extensions: test/2, test/3"); + }); }); diff --git a/packages/frontend-plugin-api/src/wiring/createPlugin.ts b/packages/frontend-plugin-api/src/wiring/createPlugin.ts index 84edd954ef..ecc4d08f9a 100644 --- a/packages/frontend-plugin-api/src/wiring/createPlugin.ts +++ b/packages/frontend-plugin-api/src/wiring/createPlugin.ts @@ -14,8 +14,10 @@ * limitations under the License. */ -import { Extension } from './createExtension'; +import { Extension, ExtensionDefinition } from './createExtension'; import { ExternalRouteRef, RouteRef } from '../routing'; +import { FeatureFlagConfig } from './types'; +import { resolveExtensionDefinition } from './resolveExtensionDefinition'; /** @public */ export type AnyRoutes = { [name in string]: RouteRef }; @@ -31,7 +33,8 @@ export interface PluginOptions< id: string; routes?: Routes; externalRoutes?: ExternalRoutes; - extensions?: Extension[]; + extensions?: ExtensionDefinition[]; + featureFlags?: FeatureFlagConfig[]; } /** @public */ @@ -39,11 +42,20 @@ export interface BackstagePlugin< Routes extends AnyRoutes = AnyRoutes, ExternalRoutes extends AnyExternalRoutes = AnyExternalRoutes, > { - $$type: '@backstage/BackstagePlugin'; - id: string; - extensions: Extension[]; - routes: Routes; - externalRoutes: ExternalRoutes; + readonly $$type: '@backstage/BackstagePlugin'; + readonly id: string; + readonly routes: Routes; + readonly externalRoutes: ExternalRoutes; +} + +/** @public */ +export interface InternalBackstagePlugin< + Routes extends AnyRoutes = AnyRoutes, + ExternalRoutes extends AnyExternalRoutes = AnyExternalRoutes, +> extends BackstagePlugin { + readonly version: 'v1'; + readonly extensions: Extension[]; + readonly featureFlags: FeatureFlagConfig[]; } /** @public */ @@ -53,11 +65,48 @@ export function createPlugin< >( options: PluginOptions, ): BackstagePlugin { + const extensions = (options.extensions ?? []).map(def => + resolveExtensionDefinition(def, { namespace: options.id }), + ); + + const extensionIds = extensions.map(e => e.id); + if (extensionIds.length !== new Set(extensionIds).size) { + const duplicates = Array.from( + new Set( + extensionIds.filter((id, index) => extensionIds.indexOf(id) !== index), + ), + ); + // TODO(Rugvip): This could provide some more information about the kind + name of the extensions + throw new Error( + `Plugin '${options.id}' provided duplicate extensions: ${duplicates.join( + ', ', + )}`, + ); + } + return { - ...options, + $$type: '@backstage/BackstagePlugin', + version: 'v1', + id: options.id, routes: options.routes ?? ({} as Routes), externalRoutes: options.externalRoutes ?? ({} as ExternalRoutes), - extensions: options.extensions ?? [], - $$type: '@backstage/BackstagePlugin', - }; + featureFlags: options.featureFlags ?? [], + extensions, + } as InternalBackstagePlugin; +} + +/** @internal */ +export function toInternalBackstagePlugin( + plugin: BackstagePlugin, +): InternalBackstagePlugin { + const internal = plugin as InternalBackstagePlugin; + if (internal.$$type !== '@backstage/BackstagePlugin') { + throw new Error(`Invalid plugin instance, bad type '${internal.$$type}'`); + } + if (internal.version !== 'v1') { + throw new Error( + `Invalid plugin instance, bad version '${internal.version}'`, + ); + } + return internal; } diff --git a/packages/frontend-plugin-api/src/wiring/index.ts b/packages/frontend-plugin-api/src/wiring/index.ts index 19e11185e9..3f33910ca7 100644 --- a/packages/frontend-plugin-api/src/wiring/index.ts +++ b/packages/frontend-plugin-api/src/wiring/index.ts @@ -22,9 +22,11 @@ export { export { createExtension, type Extension, + type ExtensionDefinition, type CreateExtensionOptions, type ExtensionDataValues, - type ExtensionInputValues, + type ResolvedExtensionInput, + type ResolvedExtensionInputs, type AnyExtensionInputMap, type AnyExtensionDataMap, } from './createExtension'; @@ -49,3 +51,4 @@ export { type ExtensionOverrides, type ExtensionOverridesOptions, } from './createExtensionOverrides'; +export type { FeatureFlagConfig } from './types'; diff --git a/packages/frontend-plugin-api/src/wiring/resolveExtensionDefinition.test.ts b/packages/frontend-plugin-api/src/wiring/resolveExtensionDefinition.test.ts new file mode 100644 index 0000000000..8f3755ac5d --- /dev/null +++ b/packages/frontend-plugin-api/src/wiring/resolveExtensionDefinition.test.ts @@ -0,0 +1,48 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ExtensionDefinition } from './createExtension'; +import { resolveExtensionDefinition } from './resolveExtensionDefinition'; + +describe('resolveExtensionDefinition', () => { + it.each([ + [{ namespace: 'ns' }, 'ns'], + [{ namespace: 'n' }, 'n'], + [{ namespace: 'ns', name: 'n' }, 'ns/n'], + [{ kind: 'k', namespace: 'ns' }, 'k:ns'], + [{ kind: 'k', namespace: 'ns', name: 'n' }, 'k:ns/n'], + ])(`should resolve extension IDs %s`, (definition, expected) => { + const resolved = resolveExtensionDefinition( + definition as ExtensionDefinition, + ); + expect(resolved.id).toBe(expected); + }); + + it('should fail to resolve extension ID without namespace', () => { + expect(() => + resolveExtensionDefinition({ + kind: 'k', + } as ExtensionDefinition), + ).toThrow( + 'Extension must declare an explicit namespace or name as it could not be resolved from context, kind=k namespace=undefined name=undefined', + ); + expect(() => + resolveExtensionDefinition({} as ExtensionDefinition), + ).toThrow( + 'Extension must declare an explicit namespace or name as it could not be resolved from context, kind=undefined namespace=undefined name=undefined', + ); + }); +}); diff --git a/packages/frontend-plugin-api/src/wiring/resolveExtensionDefinition.ts b/packages/frontend-plugin-api/src/wiring/resolveExtensionDefinition.ts new file mode 100644 index 0000000000..42f3a75246 --- /dev/null +++ b/packages/frontend-plugin-api/src/wiring/resolveExtensionDefinition.ts @@ -0,0 +1,40 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Extension, ExtensionDefinition } from './createExtension'; + +/** @internal */ +export function resolveExtensionDefinition( + definition: ExtensionDefinition, + context?: { namespace?: string }, +): Extension { + const { name, kind, namespace: _, ...rest } = definition; + const namespace = definition.namespace ?? context?.namespace; + + const namePart = + name && namespace ? `${namespace}/${name}` : namespace || name; + if (!namePart) { + throw new Error( + `Extension must declare an explicit namespace or name as it could not be resolved from context, kind=${kind} namespace=${namespace} name=${name}`, + ); + } + + return { + ...rest, + id: kind ? `${kind}:${namePart}` : namePart, + $$type: '@backstage/Extension', + }; +} diff --git a/packages/frontend-plugin-api/src/wiring/types.ts b/packages/frontend-plugin-api/src/wiring/types.ts new file mode 100644 index 0000000000..ab9f07b35e --- /dev/null +++ b/packages/frontend-plugin-api/src/wiring/types.ts @@ -0,0 +1,25 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Feature flag configuration. + * + * @public + */ +export type FeatureFlagConfig = { + /** Feature flag name */ + name: string; +}; diff --git a/packages/frontend-test-utils/CHANGELOG.md b/packages/frontend-test-utils/CHANGELOG.md index d112822d13..b25a5a7c38 100644 --- a/packages/frontend-test-utils/CHANGELOG.md +++ b/packages/frontend-test-utils/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/frontend-test-utils +## 0.1.0-next.1 + +### Patch Changes + +- e539735435: Updates for `core.router` addition. +- c21c9cf07b: Re-export mock API implementations as well as `TestApiProvider`, `TestApiRegistry`, `withLogCollector`, and `setupRequestMockHandlers` from `@backstage/test-utils`. +- Updated dependencies + - @backstage/frontend-plugin-api@0.4.0-next.1 + - @backstage/frontend-app-api@0.4.0-next.1 + - @backstage/test-utils@1.4.6-next.1 + - @backstage/types@1.1.1 + ## 0.1.0-next.0 ### Minor Changes diff --git a/packages/frontend-test-utils/api-report.md b/packages/frontend-test-utils/api-report.md index 8d7837b90a..14e1c68c4c 100644 --- a/packages/frontend-test-utils/api-report.md +++ b/packages/frontend-test-utils/api-report.md @@ -3,23 +3,40 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { Extension } from '@backstage/frontend-plugin-api'; +import { ErrorWithContext } from '@backstage/test-utils'; +import { ExtensionDefinition } from '@backstage/frontend-plugin-api'; import { JsonObject } from '@backstage/types'; +import { MockAnalyticsApi } from '@backstage/test-utils'; +import { MockConfigApi } from '@backstage/test-utils'; +import { MockErrorApi } from '@backstage/test-utils'; +import { MockErrorApiOptions } from '@backstage/test-utils'; +import { MockFetchApi } from '@backstage/test-utils'; +import { MockFetchApiOptions } from '@backstage/test-utils'; +import { MockPermissionApi } from '@backstage/test-utils'; +import { MockStorageApi } from '@backstage/test-utils'; +import { MockStorageBucket } from '@backstage/test-utils'; import { RenderResult } from '@testing-library/react'; +import { setupRequestMockHandlers } from '@backstage/test-utils'; +import { TestApiProvider } from '@backstage/test-utils'; +import { TestApiProviderProps } from '@backstage/test-utils'; +import { TestApiRegistry } from '@backstage/test-utils'; +import { withLogCollector } from '@backstage/test-utils'; // @public (undocumented) export function createExtensionTester( - subject: Extension, + subject: ExtensionDefinition, options?: { config?: TConfig; }, ): ExtensionTester; +export { ErrorWithContext }; + // @public (undocumented) export class ExtensionTester { // (undocumented) add( - extension: Extension, + extension: ExtensionDefinition, options?: { config?: TConfig; }, @@ -27,4 +44,32 @@ export class ExtensionTester { // (undocumented) render(options?: { config?: JsonObject }): RenderResult; } + +export { MockAnalyticsApi }; + +export { MockConfigApi }; + +export { MockErrorApi }; + +export { MockErrorApiOptions }; + +export { MockFetchApi }; + +export { MockFetchApiOptions }; + +export { MockPermissionApi }; + +export { MockStorageApi }; + +export { MockStorageBucket }; + +export { setupRequestMockHandlers }; + +export { TestApiProvider }; + +export { TestApiProviderProps }; + +export { TestApiRegistry }; + +export { withLogCollector }; ``` diff --git a/packages/frontend-test-utils/package.json b/packages/frontend-test-utils/package.json index 2434eec37c..93e27869fb 100644 --- a/packages/frontend-test-utils/package.json +++ b/packages/frontend-test-utils/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/frontend-test-utils", - "version": "0.1.0-next.0", + "version": "0.1.0-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/packages/frontend-test-utils/src/apis/index.ts b/packages/frontend-test-utils/src/apis/index.ts new file mode 100644 index 0000000000..81bd8a7abd --- /dev/null +++ b/packages/frontend-test-utils/src/apis/index.ts @@ -0,0 +1,28 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { + MockAnalyticsApi, + MockConfigApi, + type ErrorWithContext, + MockErrorApi, + type MockErrorApiOptions, + MockFetchApi, + type MockFetchApiOptions, + MockPermissionApi, + MockStorageApi, + type MockStorageBucket, +} from '@backstage/test-utils'; diff --git a/packages/frontend-test-utils/src/app/createExtensionTester.test.tsx b/packages/frontend-test-utils/src/app/createExtensionTester.test.tsx index 398acf4c74..e738bee7d1 100644 --- a/packages/frontend-test-utils/src/app/createExtensionTester.test.tsx +++ b/packages/frontend-test-utils/src/app/createExtensionTester.test.tsx @@ -26,7 +26,7 @@ describe('createExtensionTester', () => { it('should render a simple extension', async () => { createExtensionTester( createExtension({ - id: 'test', + namespace: 'test', attachTo: { id: 'ignored', input: 'ignored' }, output: { element: coreExtensionData.reactElement }, factory: () => ({ element:
test
}), @@ -39,7 +39,7 @@ describe('createExtensionTester', () => { it('should render an extension even if disabled by default', async () => { createExtensionTester( createExtension({ - id: 'test', + namespace: 'test', attachTo: { id: 'ignored', input: 'ignored' }, disabled: true, output: { element: coreExtensionData.reactElement }, @@ -54,7 +54,7 @@ describe('createExtensionTester', () => { expect(() => createExtensionTester( createExtension({ - id: 'test', + namespace: 'test', attachTo: { id: 'ignored', input: 'ignored' }, disabled: true, output: { path: coreExtensionData.routePath }, @@ -62,7 +62,7 @@ describe('createExtensionTester', () => { }), ).render(), ).toThrow( - "Failed to instantiate extension 'core.router', input 'children' did not receive required extension data 'core.reactElement' from extension 'test'", + "Failed to instantiate extension 'core/router', input 'children' did not receive required extension data 'core.reactElement' from extension 'test'", ); }); }); diff --git a/packages/frontend-test-utils/src/app/createExtensionTester.ts b/packages/frontend-test-utils/src/app/createExtensionTester.ts index c95ece342d..257a47439c 100644 --- a/packages/frontend-test-utils/src/app/createExtensionTester.ts +++ b/packages/frontend-test-utils/src/app/createExtensionTester.ts @@ -15,7 +15,12 @@ */ import { createSpecializedApp } from '@backstage/frontend-app-api'; -import { Extension, createPlugin } from '@backstage/frontend-plugin-api'; +import { + ExtensionDefinition, + createExtensionOverrides, +} from '@backstage/frontend-plugin-api'; +// eslint-disable-next-line @backstage/no-relative-monorepo-imports +import { resolveExtensionDefinition } from '../../../frontend-plugin-api/src/wiring/resolveExtensionDefinition'; import { MockConfigApi } from '@backstage/test-utils'; import { JsonArray, JsonObject, JsonValue } from '@backstage/types'; import { RenderResult, render } from '@testing-library/react'; @@ -24,7 +29,7 @@ import { RenderResult, render } from '@testing-library/react'; export class ExtensionTester { /** @internal */ static forSubject( - subject: Extension, + subject: ExtensionDefinition, options?: { config?: TConfig }, ): ExtensionTester { const tester = new ExtensionTester(); @@ -33,16 +38,22 @@ export class ExtensionTester { } readonly #extensions = new Array<{ - extension: Extension; + id: string; + extension: ExtensionDefinition; config?: JsonValue; }>(); add( - extension: Extension, + extension: ExtensionDefinition, options?: { config?: TConfig }, ): ExtensionTester { + const withNamespace = { + ...extension, + name: !extension.namespace && !extension.name ? 'test' : extension.name, + }; this.#extensions.push({ - extension, + id: resolveExtensionDefinition(withNamespace).id, + extension: withNamespace, config: options?.config as JsonValue, }); @@ -61,25 +72,25 @@ export class ExtensionTester { const extensionsConfig: JsonArray = [ ...rest.map(entry => ({ - [entry.extension.id]: { + [entry.id]: { config: entry.config, }, })), { - [subject.extension.id]: { - attachTo: { id: 'core.router', input: 'children' }, + [subject.id]: { + attachTo: { id: 'core/router', input: 'children' }, config: subject.config, disabled: false, }, }, { - 'core.layout': false, + 'core/layout': false, }, { - 'core.nav': false, + 'core/nav': false, }, { - 'core.routes': false, + 'core/routes': false, }, ]; @@ -93,8 +104,7 @@ export class ExtensionTester { const app = createSpecializedApp({ features: [ - createPlugin({ - id: 'test', + createExtensionOverrides({ extensions: this.#extensions.map(entry => entry.extension), }), ], @@ -107,7 +117,7 @@ export class ExtensionTester { /** @public */ export function createExtensionTester( - subject: Extension, + subject: ExtensionDefinition, options?: { config?: TConfig }, ): ExtensionTester { return ExtensionTester.forSubject(subject, options); diff --git a/packages/frontend-test-utils/src/index.ts b/packages/frontend-test-utils/src/index.ts index 6a28cc2abe..0dab8e75b7 100644 --- a/packages/frontend-test-utils/src/index.ts +++ b/packages/frontend-test-utils/src/index.ts @@ -20,4 +20,12 @@ * Contains utilities that can be used when testing frontend features such as extensions. */ +export * from './apis'; export * from './app'; + +export { TestApiProvider, TestApiRegistry } from '@backstage/test-utils'; +export type { TestApiProviderProps } from '@backstage/test-utils'; + +export { withLogCollector } from '@backstage/test-utils'; + +export { setupRequestMockHandlers } from '@backstage/test-utils'; diff --git a/packages/integration-react/CHANGELOG.md b/packages/integration-react/CHANGELOG.md index 21a4a75e75..34a24e7602 100644 --- a/packages/integration-react/CHANGELOG.md +++ b/packages/integration-react/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/integration-react +## 1.1.22-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/integration@1.8.0-next.1 + - @backstage/config@1.1.1 + ## 1.1.22-next.0 ### Patch Changes diff --git a/packages/integration-react/package.json b/packages/integration-react/package.json index f3e83da77e..4c7defc2d4 100644 --- a/packages/integration-react/package.json +++ b/packages/integration-react/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/integration-react", "description": "Frontend package for managing integrations towards external systems", - "version": "1.1.22-next.0", + "version": "1.1.22-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/packages/integration/CHANGELOG.md b/packages/integration/CHANGELOG.md index a3392a512d..74dee088d5 100644 --- a/packages/integration/CHANGELOG.md +++ b/packages/integration/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/integration +## 1.8.0-next.1 + +### Patch Changes + +- 99fb54183b: Updated dependency `@azure/identity` to `^4.0.0`. +- Updated dependencies + - @backstage/config@1.1.1 + ## 1.8.0-next.0 ### Minor Changes diff --git a/packages/integration/package.json b/packages/integration/package.json index df927acd55..8e403c2ce4 100644 --- a/packages/integration/package.json +++ b/packages/integration/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/integration", "description": "Helpers for managing integrations towards external systems", - "version": "1.8.0-next.0", + "version": "1.8.0-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/packages/repo-tools/CHANGELOG.md b/packages/repo-tools/CHANGELOG.md index b439c08fc6..0a0802d87e 100644 --- a/packages/repo-tools/CHANGELOG.md +++ b/packages/repo-tools/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/repo-tools +## 0.5.0-next.0 + +### Minor Changes + +- aea8f8d329: **BREAKING**: API Reports generated for sub-path exports now place the name as a suffix rather than prefix, for example `api-report-alpha.md` instead of `alpha-api-report.md`. When upgrading to this version you'll need to re-create any such API reports and delete the old ones. +- 38340678c3: Adds a new command `schema openapi generate-client` that creates a Typescript client with Backstage flavor, including the discovery API and fetch API. This command doesn't currently generate a complete client and needs to be wrapped or exported manually by a separate Backstage plugin. See `@backstage/catalog-client/src/generated` for example output. + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.3 + - @backstage/cli-common@0.1.13 + - @backstage/cli-node@0.2.0 + - @backstage/errors@1.2.3 + ## 0.4.0 ### Minor Changes diff --git a/packages/repo-tools/openapitools.json b/packages/repo-tools/openapitools.json new file mode 100644 index 0000000000..65c0d8ead7 --- /dev/null +++ b/packages/repo-tools/openapitools.json @@ -0,0 +1,7 @@ +{ + "$schema": "../../node_modules/@openapitools/openapi-generator-cli/config.schema.json", + "spaces": 2, + "generator-cli": { + "version": "6.5.0" + } +} diff --git a/packages/repo-tools/package.json b/packages/repo-tools/package.json index 3bd76a6e60..02119d46c5 100644 --- a/packages/repo-tools/package.json +++ b/packages/repo-tools/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/repo-tools", "description": "CLI for Backstage repo tooling ", - "version": "0.4.0", + "version": "0.5.0-next.0", "publishConfig": { "access": "public" }, @@ -39,13 +39,14 @@ "@manypkg/get-packages": "^1.1.3", "@microsoft/api-documenter": "^7.22.33", "@microsoft/api-extractor": "^7.36.4", + "@openapitools/openapi-generator-cli": "^2.7.0", "@stoplight/spectral-core": "^1.18.0", "@stoplight/spectral-formatters": "^1.1.0", "@stoplight/spectral-functions": "^1.7.2", "@stoplight/spectral-parsers": "^1.0.2", "@stoplight/spectral-rulesets": "^1.18.0", "@stoplight/spectral-runtime": "^1.1.2", - "@stoplight/types": "^13.14.0", + "@stoplight/types": "^14.0.0", "chalk": "^4.0.0", "codeowners-utils": "^1.0.2", "commander": "^9.1.0", diff --git a/packages/repo-tools/src/commands/api-reports/api-extractor.ts b/packages/repo-tools/src/commands/api-reports/api-extractor.ts index ce3e6f5b20..c978e49300 100644 --- a/packages/repo-tools/src/commands/api-reports/api-extractor.ts +++ b/packages/repo-tools/src/commands/api-reports/api-extractor.ts @@ -375,8 +375,8 @@ export async function runApiExtraction({ packageDir, ); - const prefix = name === 'index' ? '' : `${name}-`; - const reportFileName = `${prefix}api-report.md`; + const suffix = name === 'index' ? '' : `-${name}`; + const reportFileName = `api-report${suffix}.md`; const reportPath = resolvePath(projectFolder, reportFileName); const warningCountBefore = await countApiReportWarnings(reportPath); @@ -396,7 +396,7 @@ export async function runApiExtraction({ reportFolder: projectFolder, reportTempFolder: resolvePath( outputDir, - `${prefix}`, + `${suffix}`, ), }, @@ -406,7 +406,7 @@ export async function runApiExtraction({ enabled: name === 'index', apiJsonFilePath: resolvePath( outputDir, - `${prefix}.api.json`, + `${suffix}.api.json`, ), }, diff --git a/packages/repo-tools/src/commands/index.ts b/packages/repo-tools/src/commands/index.ts index 4f9ea87708..d9c6d892f5 100644 --- a/packages/repo-tools/src/commands/index.ts +++ b/packages/repo-tools/src/commands/index.ts @@ -32,14 +32,18 @@ function registerSchemaCommand(program: Command) { .description( 'Verify that all OpenAPI schemas are valid and have a matching `schemas/openapi.generated.ts` file.', ) - .action(lazy(() => import('./openapi/verify').then(m => m.bulkCommand))); + .action( + lazy(() => import('./openapi/schema/verify').then(m => m.bulkCommand)), + ); openApiCommand .command('generate [paths...]') .description( 'Generates a Typescript file from an OpenAPI yaml spec. For use with the `@backstage/backend-openapi-utils` ApiRouter type.', ) - .action(lazy(() => import('./openapi/generate').then(m => m.bulkCommand))); + .action( + lazy(() => import('./openapi/schema/generate').then(m => m.bulkCommand)), + ); openApiCommand .command('lint [paths...]') @@ -60,6 +64,16 @@ function registerSchemaCommand(program: Command) { .command('init ') .description('Creates any config needed for the test command.') .action(lazy(() => import('./openapi/test/init').then(m => m.default))); + + openApiCommand + .command('generate-client') + .requiredOption('--input-spec ') + .requiredOption('--output-directory ') + .action( + lazy(() => + import('./openapi/client/generate').then(m => m.singleCommand), + ), + ); } export function registerCommands(program: Command) { diff --git a/packages/repo-tools/src/commands/openapi/client/generate.ts b/packages/repo-tools/src/commands/openapi/client/generate.ts new file mode 100644 index 0000000000..baa7845436 --- /dev/null +++ b/packages/repo-tools/src/commands/openapi/client/generate.ts @@ -0,0 +1,97 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import chalk from 'chalk'; +import { resolve } from 'path'; +import { OPENAPI_IGNORE_FILES, OUTPUT_PATH } from '../constants'; +import { paths as cliPaths } from '../../../lib/paths'; +import { mkdirpSync } from 'fs-extra'; +import fs from 'fs-extra'; +import { exec } from '../../../lib/exec'; + +async function generate(spec: string, outputDirectory: string) { + const resolvedOpenapiPath = resolve(spec); + const resolvedOutputDirectory = resolve(outputDirectory, OUTPUT_PATH); + mkdirpSync(resolvedOutputDirectory); + + await fs.mkdirp(resolvedOutputDirectory); + + await fs.writeFile( + resolve(resolvedOutputDirectory, '.openapi-generator-ignore'), + OPENAPI_IGNORE_FILES.join('\n'), + ); + + await exec( + // The actual main.js file for the binary isn't executable but yarn does _something_ to make it executable. + // TODO (sennyeya@): Make this use the actual binary + `yarn openapi-generator-cli`, + [ + 'generate', + '-i', + resolvedOpenapiPath, + '-o', + resolvedOutputDirectory, + '-g', + 'typescript', + '-c', + 'templates/typescript-backstage.yaml', + '--generator-key', + 'v3.0', + ], + { + maxBuffer: Number.MAX_VALUE, + cwd: cliPaths.ownDir, + env: { + ...process.env, + // PWD: outputDirectory, + }, + }, + ); + + await exec( + `yarn backstage-cli package lint --fix ${resolvedOutputDirectory}`, + ); + + if (cliPaths.resolveTargetRoot('node_modules/.bin/prettier')) { + await exec(`yarn prettier --write ${resolvedOutputDirectory}`); + } + + fs.removeSync(resolve(resolvedOutputDirectory, '.openapi-generator-ignore')); + + fs.rmSync(resolve(resolvedOutputDirectory, '.openapi-generator'), { + recursive: true, + force: true, + }); +} + +export async function singleCommand({ + inputSpec, + outputDirectory, +}: { + inputSpec: string; + outputDirectory: string; +}): Promise { + try { + await generate(inputSpec, outputDirectory); + console.log(chalk.green(`Generated client for ${inputSpec}`)); + } catch (err) { + console.log(); + console.log(chalk.red(`Client generation failed in ${outputDirectory}:`)); + console.log(err); + + process.exit(1); + } +} diff --git a/packages/repo-tools/src/commands/openapi/constants.ts b/packages/repo-tools/src/commands/openapi/constants.ts index defef4ab53..2e7a57522a 100644 --- a/packages/repo-tools/src/commands/openapi/constants.ts +++ b/packages/repo-tools/src/commands/openapi/constants.ts @@ -19,3 +19,44 @@ export const YAML_SCHEMA_PATH = 'src/schema/openapi.yaml'; export const TS_MODULE = 'src/schema/openapi.generated'; export const TS_SCHEMA_PATH = `${TS_MODULE}.ts`; + +export const GENERATOR_VERSION = `1.0.0`; +export const GENERATOR_NAME = 'typescript-backstage'; +export const GENERATOR_FILE = `packages/template-openapi-plugin-client/generator/target/${GENERATOR_NAME}-openapi-generator-${GENERATOR_VERSION}.jar`; + +export const OUTPUT_PATH = 'src/generated'; + +export const OPENAPI_IGNORE_FILES = [ + // Get rid of the default files. + '*.md', + // The rest of these have to be explicit, otherwise they get added if this was a *.* + 'apis/baseapi.ts', + 'apis/exception.ts', + 'auth/*', + 'http/*', + 'middleware.ts', + 'servers.ts', + 'util.ts', + 'configuration.ts', + 'rxjsStub.ts', + '.gitignore', + + // Override the created version. + 'apis/*.ts', + '!apis/*.client.ts', + 'models/*.ts', + '!models/*.model.ts', + + // Always include index.ts files. + '!index.ts', + '!**/index.ts', + + // Weird API typings. + 'types/ObjectParamAPI.ts', + 'types/ObservableAPI.ts', + 'types/PromiseAPI.ts', + + 'git_push.sh', + 'package.json', + 'tsconfig.json', +]; diff --git a/packages/repo-tools/src/commands/openapi/generate.ts b/packages/repo-tools/src/commands/openapi/schema/generate.ts similarity index 95% rename from packages/repo-tools/src/commands/openapi/generate.ts rename to packages/repo-tools/src/commands/openapi/schema/generate.ts index 52b8e5b840..cd9f572f2d 100644 --- a/packages/repo-tools/src/commands/openapi/generate.ts +++ b/packages/repo-tools/src/commands/openapi/schema/generate.ts @@ -18,9 +18,9 @@ import fs from 'fs-extra'; import YAML from 'js-yaml'; import chalk from 'chalk'; import { resolve } from 'path'; -import { paths as cliPaths } from '../../lib/paths'; -import { runner } from './runner'; -import { TS_SCHEMA_PATH, YAML_SCHEMA_PATH } from './constants'; +import { paths as cliPaths } from '../../../lib/paths'; +import { runner } from '../runner'; +import { TS_SCHEMA_PATH, YAML_SCHEMA_PATH } from '../constants'; import { promisify } from 'util'; import { exec as execCb } from 'child_process'; diff --git a/packages/repo-tools/src/commands/openapi/verify.ts b/packages/repo-tools/src/commands/openapi/schema/verify.ts similarity index 93% rename from packages/repo-tools/src/commands/openapi/verify.ts rename to packages/repo-tools/src/commands/openapi/schema/verify.ts index 2808e72702..7120dd67ad 100644 --- a/packages/repo-tools/src/commands/openapi/verify.ts +++ b/packages/repo-tools/src/commands/openapi/schema/verify.ts @@ -21,9 +21,9 @@ import { join } from 'path'; import chalk from 'chalk'; import { relative as relativePath, resolve as resolvePath } from 'path'; import Parser from '@apidevtools/swagger-parser'; -import { runner } from './runner'; -import { paths as cliPaths } from '../../lib/paths'; -import { TS_MODULE, TS_SCHEMA_PATH, YAML_SCHEMA_PATH } from './constants'; +import { runner } from '../runner'; +import { paths as cliPaths } from '../../../lib/paths'; +import { TS_MODULE, TS_SCHEMA_PATH, YAML_SCHEMA_PATH } from '../constants'; async function verify(directoryPath: string) { const openapiPath = join(directoryPath, YAML_SCHEMA_PATH); diff --git a/packages/repo-tools/templates/typescript-backstage.yaml b/packages/repo-tools/templates/typescript-backstage.yaml new file mode 100644 index 0000000000..eaa72d7179 --- /dev/null +++ b/packages/repo-tools/templates/typescript-backstage.yaml @@ -0,0 +1,18 @@ +templateDir: templates/typescript-backstage + +files: + api.mustache: + templateType: API + # For some reason, they check for destinationFilename differences. We have to change the ending to override the file. + destinationFilename: .client.ts + model.mustache: + templateType: Model + destinationFilename: .model.ts + types/fetch.ts: {} + types/discovery.ts: {} + apis/index.mustache: + templateType: SupportingFiles + destinationFilename: apis/index.ts + pluginId.mustache: + templateType: SupportingFiles + destinationFilename: pluginId.ts \ No newline at end of file diff --git a/packages/repo-tools/templates/typescript-backstage/api.mustache b/packages/repo-tools/templates/typescript-backstage/api.mustache new file mode 100644 index 0000000000..1b8739ba57 --- /dev/null +++ b/packages/repo-tools/templates/typescript-backstage/api.mustache @@ -0,0 +1,114 @@ +{{>licenseInfo}} +import { DiscoveryApi } from '../types/discovery'; +import { FetchApi } from '../types/fetch'; +import crossFetch from 'cross-fetch'; +import {pluginId} from '../pluginId'; +import * as parser from 'uri-template'; + +{{#imports}} +import { {{classname}} } from '{{filename}}.model{{importFileExtension}}'; +{{/imports}} + +type TypedResponse = Omit & { + json: () => Promise; +}; + +{{#operations}} + +/** + * Options you can pass into a request for additional information. + * + * @public + */ +export interface RequestOptions { + token?: string; +} + +/** + * {{{description}}}{{^description}}no description{{/description}} + */ +export class {{classname}}Client { + private readonly discoveryApi: DiscoveryApi; + private readonly fetchApi: FetchApi; + + constructor(options: { + discoveryApi: { getBaseUrl(pluginId: string): Promise }; + fetchApi?: { fetch: typeof fetch }; + }) { + this.discoveryApi = options.discoveryApi; + this.fetchApi = options.fetchApi || { fetch: crossFetch }; + } + + {{#operation}} + /** + {{#notes}} + * {{¬es}} + {{/notes}} + {{#summary}} + * {{&summary}} + {{/summary}} + {{#allParams}} + * @param {{paramName}} {{description}} + {{/allParams}} + */ + public async {{nickname}}( + // @ts-ignore + request: { + {{#hasPathParams}} + path: { + {{#pathParams}} + {{paramName}}{{^required}}?{{/required}}: {{{dataType}}}, + {{/pathParams}} + }, + {{/hasPathParams}} + {{#hasBodyParam}} + {{#bodyParam}} + body: {{{dataType}}}, + {{/bodyParam}} + {{/hasBodyParam}} + {{#hasQueryParams}} + query: { + {{#queryParams}} + {{paramName}}{{^required}}?{{/required}}: {{{dataType}}}, + {{/queryParams}} + }, + {{/hasQueryParams}} + {{#hasHeaderParams}} + header: { + {{#headerParams}} + {{paramName}}{{^required}}?{{/required}}: {{{dataType}}}, + {{/headerParams}} + }, + {{/hasHeaderParams}} + }, + options?: RequestOptions + ): Promise> { + const baseUrl = await this.discoveryApi.getBaseUrl(pluginId); + + const uriTemplate = `{{{path}}}{{#hasQueryParams}}{?{{#queryParams}}{{baseName}}{{#isArray}}{{#isExplode}}*{{/isExplode}}{{/isArray}}{{^-last}},{{/-last}}{{/queryParams}}}{{/hasQueryParams}}`; + + const uri = parser.parse(uriTemplate).expand({ + {{#pathParams}} + {{baseName}}: request.path.{{paramName}}, + {{/pathParams}} + {{#hasQueryParams}} + ...request.query, + {{/hasQueryParams}} + }) + + return await this.fetchApi.fetch(`${baseUrl}${uri}`, { + headers: { + {{#hasHeaderParams}} + ...request.header, + {{/hasHeaderParams}} + 'Content-Type': 'application/json', + ...(options?.token && { Authorization: `Bearer ${options?.token}` }), + }, + method: '{{httpMethod}}', + {{#hasBodyParam}} body: JSON.stringify(request.body), {{/hasBodyParam}} + }); + } + + {{/operation}} +} +{{/operations}} diff --git a/packages/repo-tools/templates/typescript-backstage/apis/index.mustache b/packages/repo-tools/templates/typescript-backstage/apis/index.mustache new file mode 100644 index 0000000000..4c8996c5ee --- /dev/null +++ b/packages/repo-tools/templates/typescript-backstage/apis/index.mustache @@ -0,0 +1,3 @@ +// + +export * from './DefaultApi.client'; diff --git a/packages/repo-tools/templates/typescript-backstage/index.mustache b/packages/repo-tools/templates/typescript-backstage/index.mustache new file mode 100644 index 0000000000..8436cce60b --- /dev/null +++ b/packages/repo-tools/templates/typescript-backstage/index.mustache @@ -0,0 +1,3 @@ +// + +export * from './apis' diff --git a/packages/repo-tools/templates/typescript-backstage/model.mustache b/packages/repo-tools/templates/typescript-backstage/model.mustache new file mode 100644 index 0000000000..1f288380e0 --- /dev/null +++ b/packages/repo-tools/templates/typescript-backstage/model.mustache @@ -0,0 +1,43 @@ +// + +{{#models}} +{{#model}} +{{#tsImports}} +import { {{classname}} } from '{{filename}}.model{{importFileExtension}}'; +{{/tsImports}} + +{{#description}} +/** +* {{{.}}} +*/ +{{/description}} +{{^isEnum}} +export interface {{classname}} { +{{#additionalPropertiesType}} + [key: string]: {{{additionalPropertiesType}}}; +{{/additionalPropertiesType}} +{{#vars}} +{{#description}} + /** + * {{{.}}} + */ +{{/description}} + '{{name}}'{{^required}}?{{/required}}: {{#isEnum}}{{{datatypeWithEnum}}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}{{#isNullable}} | null{{/isNullable}}; +{{/vars}} +} + +{{#hasEnums}} + +{{#vars}} +{{#isEnum}} +export type {{classname}}{{enumName}} ={{#allowableValues}}{{#values}} "{{.}}" {{^-last}}|{{/-last}}{{/values}}{{/allowableValues}}; +{{/isEnum}} +{{/vars}} + +{{/hasEnums}} +{{/isEnum}} +{{#isEnum}} +export type {{classname}} ={{#allowableValues}}{{#values}} "{{.}}" {{^-last}}|{{/-last}}{{/values}}{{/allowableValues}}; +{{/isEnum}} +{{/model}} +{{/models}} \ No newline at end of file diff --git a/packages/repo-tools/templates/typescript-backstage/models/models_all.mustache b/packages/repo-tools/templates/typescript-backstage/models/models_all.mustache new file mode 100644 index 0000000000..d076cfb3ff --- /dev/null +++ b/packages/repo-tools/templates/typescript-backstage/models/models_all.mustache @@ -0,0 +1,7 @@ +// + +{{#models}} +{{#model}} +export * from '{{{ importPath }}}.model{{importFileExtension}}' +{{/model}} +{{/models}} \ No newline at end of file diff --git a/packages/repo-tools/templates/typescript-backstage/pluginId.mustache b/packages/repo-tools/templates/typescript-backstage/pluginId.mustache new file mode 100644 index 0000000000..8bc8461cc9 --- /dev/null +++ b/packages/repo-tools/templates/typescript-backstage/pluginId.mustache @@ -0,0 +1,6 @@ + +{{#servers}} +{{#-last}} +export const pluginId = "{{url}}"; +{{/-last}} +{{/servers}} \ No newline at end of file diff --git a/packages/repo-tools/templates/typescript-backstage/types/discovery.ts b/packages/repo-tools/templates/typescript-backstage/types/discovery.ts new file mode 100644 index 0000000000..a7f87d3780 --- /dev/null +++ b/packages/repo-tools/templates/typescript-backstage/types/discovery.ts @@ -0,0 +1,22 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * This is a copy of the DiscoveryApi, to avoid importing core-plugin-api. + */ +export type DiscoveryApi = { + getBaseUrl(pluginId: string): Promise; +}; diff --git a/packages/repo-tools/templates/typescript-backstage/types/fetch.ts b/packages/repo-tools/templates/typescript-backstage/types/fetch.ts new file mode 100644 index 0000000000..3de56c028e --- /dev/null +++ b/packages/repo-tools/templates/typescript-backstage/types/fetch.ts @@ -0,0 +1,22 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * This is a copy of FetchApi, to avoid importing core-plugin-api. + */ +export type FetchApi = { + fetch: typeof fetch; +}; diff --git a/packages/techdocs-cli-embedded-app/CHANGELOG.md b/packages/techdocs-cli-embedded-app/CHANGELOG.md index b75cb32d7a..135f13d075 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.89-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/cli@0.25.0-next.1 + - @backstage/plugin-catalog@1.16.0-next.2 + - @backstage/core-app-api@1.11.2-next.1 + - @backstage/test-utils@1.4.6-next.1 + - @backstage/plugin-techdocs@1.9.2-next.2 + - @backstage/app-defaults@1.4.6-next.1 + - @backstage/integration-react@1.1.22-next.1 + - @backstage/plugin-techdocs-react@1.1.14-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/theme@0.5.0-next.0 + ## 0.2.89-next.1 ### Patch Changes diff --git a/packages/techdocs-cli-embedded-app/package.json b/packages/techdocs-cli-embedded-app/package.json index 21b1ff39f4..f3e5485658 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.89-next.1", + "version": "0.2.89-next.2", "private": true, "backstage": { "role": "frontend" diff --git a/packages/techdocs-cli/CHANGELOG.md b/packages/techdocs-cli/CHANGELOG.md index bdcd94b951..8769bd0c93 100644 --- a/packages/techdocs-cli/CHANGELOG.md +++ b/packages/techdocs-cli/CHANGELOG.md @@ -1,5 +1,20 @@ # @techdocs/cli +## 1.8.0-next.1 + +### Minor Changes + +- b2dccad7b3: Support passing additional `mkdocs-server` CLI parameters (`--dirtyreload`, `--strict` and `--clean`) when run in containerized mode. + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-techdocs-node@1.11.0-next.1 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/cli-common@0.1.13 + - @backstage/config@1.1.1 + ## 1.8.0-next.0 ### Minor Changes diff --git a/packages/techdocs-cli/cli-report.md b/packages/techdocs-cli/cli-report.md index 8f2233b158..9777b2c4b8 100644 --- a/packages/techdocs-cli/cli-report.md +++ b/packages/techdocs-cli/cli-report.md @@ -108,6 +108,9 @@ Options: --preview-app-bundle-path --preview-app-port -c, --mkdocs-config-file-name + --mkdocs-parameter-clean + --mkdocs-parameter-dirtyreload + --mkdocs-parameter-strict -h, --help ``` diff --git a/packages/techdocs-cli/package.json b/packages/techdocs-cli/package.json index fa1c69d09b..94f9c159ee 100644 --- a/packages/techdocs-cli/package.json +++ b/packages/techdocs-cli/package.json @@ -1,7 +1,7 @@ { "name": "@techdocs/cli", "description": "Utility CLI for managing TechDocs sites in Backstage.", - "version": "1.8.0-next.0", + "version": "1.8.0-next.1", "publishConfig": { "access": "public" }, diff --git a/packages/techdocs-cli/src/commands/index.ts b/packages/techdocs-cli/src/commands/index.ts index bb469ec82a..ac9523e639 100644 --- a/packages/techdocs-cli/src/commands/index.ts +++ b/packages/techdocs-cli/src/commands/index.ts @@ -294,6 +294,21 @@ export function registerCommands(program: Command) { '-c, --mkdocs-config-file-name ', 'Mkdocs config file name', ) + .option( + '--mkdocs-parameter-clean', + 'Pass "--clean" parameter to mkdocs server running in containerized environment', + false, + ) + .option( + '--mkdocs-parameter-dirtyreload', + 'Pass "--dirtyreload" parameter to mkdocs server running in containerized environment', + false, + ) + .option( + '--mkdocs-parameter-strict', + 'Pass "--strict" parameter to mkdocs server running in containerized environment', + false, + ) .hook('preAction', command => { if ( command.opts().previewAppPort !== defaultPreviewAppPort && diff --git a/packages/techdocs-cli/src/commands/serve/serve.ts b/packages/techdocs-cli/src/commands/serve/serve.ts index 98c9982b21..de681c2de4 100644 --- a/packages/techdocs-cli/src/commands/serve/serve.ts +++ b/packages/techdocs-cli/src/commands/serve/serve.ts @@ -116,6 +116,9 @@ export default async function serve(opts: OptionValues) { stdoutLogFunc: mkdocsLogFunc, stderrLogFunc: mkdocsLogFunc, mkdocsConfigFileName: mkdocsYmlPath, + mkdocsParameterClean: opts.mkdocsParameterClean, + mkdocsParameterDirtyReload: opts.mkdocsParameterDirtyReload, + mkdocsParameterStrict: opts.mkdocsParameterStrict, }); // Wait until mkdocs server has started so that Backstage starts with docs loaded diff --git a/packages/techdocs-cli/src/lib/mkdocsServer.test.ts b/packages/techdocs-cli/src/lib/mkdocsServer.test.ts index 8484c4e242..83f9c9f382 100644 --- a/packages/techdocs-cli/src/lib/mkdocsServer.test.ts +++ b/packages/techdocs-cli/src/lib/mkdocsServer.test.ts @@ -97,6 +97,24 @@ describe('runMkdocsServer', () => { expect.objectContaining({}), ); }); + + it('should accept additinoal mkdocs CLI parameters', async () => { + await runMkdocsServer({ + mkdocsParameterClean: true, + mkdocsParameterStrict: true, + }); + expect(run).toHaveBeenCalledWith( + 'docker', + expect.arrayContaining([ + 'serve', + '--dev-addr', + '0.0.0.0:8000', + '--clean', + '--strict', + ]), + expect.objectContaining({}), + ); + }); }); describe('mkdocs', () => { diff --git a/packages/techdocs-cli/src/lib/mkdocsServer.ts b/packages/techdocs-cli/src/lib/mkdocsServer.ts index 42409acb46..a793643367 100644 --- a/packages/techdocs-cli/src/lib/mkdocsServer.ts +++ b/packages/techdocs-cli/src/lib/mkdocsServer.ts @@ -26,6 +26,9 @@ export const runMkdocsServer = async (options: { stdoutLogFunc?: LogFunc; stderrLogFunc?: LogFunc; mkdocsConfigFileName?: string; + mkdocsParameterClean?: boolean; + mkdocsParameterDirtyReload?: boolean; + mkdocsParameterStrict?: boolean; }): Promise => { const port = options.port ?? '8000'; const useDocker = options.useDocker ?? true; @@ -55,6 +58,9 @@ export const runMkdocsServer = async (options: { ...(options.mkdocsConfigFileName ? ['--config-file', options.mkdocsConfigFileName] : []), + ...(options.mkdocsParameterClean ? ['--clean'] : []), + ...(options.mkdocsParameterDirtyReload ? ['--dirtyreload'] : []), + ...(options.mkdocsParameterStrict ? ['--strict'] : []), ], { stdoutLogFunc: options.stdoutLogFunc, @@ -72,6 +78,9 @@ export const runMkdocsServer = async (options: { ...(options.mkdocsConfigFileName ? ['--config-file', options.mkdocsConfigFileName] : []), + ...(options.mkdocsParameterClean ? ['--clean'] : []), + ...(options.mkdocsParameterDirtyReload ? ['--dirtyreload'] : []), + ...(options.mkdocsParameterStrict ? ['--strict'] : []), ], { stdoutLogFunc: options.stdoutLogFunc, diff --git a/packages/test-utils/CHANGELOG.md b/packages/test-utils/CHANGELOG.md index 5bde6c3354..9a18091f6e 100644 --- a/packages/test-utils/CHANGELOG.md +++ b/packages/test-utils/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/test-utils +## 1.4.6-next.1 + +### Patch Changes + +- e8f2acef80: Deprecated `mockBreakpoint`, as it is now available from `@backstage/core-components/testUtils` instead. +- Updated dependencies + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/core-app-api@1.11.2-next.1 + - @backstage/plugin-permission-react@0.4.18-next.1 + - @backstage/config@1.1.1 + - @backstage/theme@0.5.0-next.0 + - @backstage/types@1.1.1 + - @backstage/plugin-permission-common@0.7.10 + ## 1.4.6-next.0 ### Patch Changes diff --git a/packages/test-utils/alpha-api-report.md b/packages/test-utils/api-report-alpha.md similarity index 100% rename from packages/test-utils/alpha-api-report.md rename to packages/test-utils/api-report-alpha.md diff --git a/packages/test-utils/api-report.md b/packages/test-utils/api-report.md index 60bd352726..913413acd1 100644 --- a/packages/test-utils/api-report.md +++ b/packages/test-utils/api-report.md @@ -74,7 +74,7 @@ export class MockAnalyticsApi implements AnalyticsApi { getEvents(): AnalyticsEvent[]; } -// @public +// @public @deprecated export function mockBreakpoint(options: { matches: boolean }): void; // @public diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index 15b3a387ae..ec48141e99 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/test-utils", "description": "Utilities to test Backstage plugins and apps.", - "version": "1.4.6-next.0", + "version": "1.4.6-next.1", "publishConfig": { "access": "public" }, diff --git a/packages/test-utils/src/testUtils/mockBreakpoint.ts b/packages/test-utils/src/testUtils/mockBreakpoint.ts index 78d7c9497f..c674c98f06 100644 --- a/packages/test-utils/src/testUtils/mockBreakpoint.ts +++ b/packages/test-utils/src/testUtils/mockBreakpoint.ts @@ -25,6 +25,7 @@ * https://mui.com/material-ui/react-use-media-query/#testing * * @public + * @deprecated Import from `@backstage/core-components/testUtils` instead. */ export default function mockBreakpoint(options: { matches: boolean }) { Object.defineProperty(window, 'matchMedia', { diff --git a/plugins/adr-backend/CHANGELOG.md b/plugins/adr-backend/CHANGELOG.md index 5ea30aee78..8be4a06ac9 100644 --- a/plugins/adr-backend/CHANGELOG.md +++ b/plugins/adr-backend/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-adr-backend +## 0.4.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.5.0-next.0 + - @backstage/integration@1.8.0-next.1 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-adr-common@0.2.18-next.1 + - @backstage/plugin-search-common@1.2.8 + ## 0.4.5-next.0 ### Patch Changes diff --git a/plugins/adr-backend/package.json b/plugins/adr-backend/package.json index 8981a4905d..c758ad68ed 100644 --- a/plugins/adr-backend/package.json +++ b/plugins/adr-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-adr-backend", - "version": "0.4.5-next.0", + "version": "0.4.5-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/adr-common/CHANGELOG.md b/plugins/adr-common/CHANGELOG.md index 2127f06437..3f02901db7 100644 --- a/plugins/adr-common/CHANGELOG.md +++ b/plugins/adr-common/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-adr-common +## 0.2.18-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.8.0-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/plugin-search-common@1.2.8 + ## 0.2.18-next.0 ### Patch Changes diff --git a/plugins/adr-common/package.json b/plugins/adr-common/package.json index 43bc951328..e04af0ade0 100644 --- a/plugins/adr-common/package.json +++ b/plugins/adr-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-adr-common", "description": "Common functionalities for the adr plugin", - "version": "0.2.18-next.0", + "version": "0.2.18-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/adr/CHANGELOG.md b/plugins/adr/CHANGELOG.md index 0e69d1e735..92390fdbb8 100644 --- a/plugins/adr/CHANGELOG.md +++ b/plugins/adr/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-adr +## 0.6.11-next.1 + +### Patch Changes + +- fb8f3bdbc2: Updated alpha translation message keys to use nested format and camel case. +- Updated dependencies + - @backstage/frontend-plugin-api@0.4.0-next.1 + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/plugin-search-react@1.7.4-next.1 + - @backstage/integration-react@1.1.22-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/theme@0.5.0-next.0 + - @backstage/plugin-adr-common@0.2.18-next.1 + - @backstage/plugin-search-common@1.2.8 + ## 0.6.11-next.0 ### Patch Changes diff --git a/plugins/adr/alpha-api-report.md b/plugins/adr/api-report-alpha.md similarity index 67% rename from plugins/adr/alpha-api-report.md rename to plugins/adr/api-report-alpha.md index 5740556904..4c9ac1d17e 100644 --- a/plugins/adr/alpha-api-report.md +++ b/plugins/adr/api-report-alpha.md @@ -4,11 +4,11 @@ ```ts import { BackstagePlugin } from '@backstage/frontend-plugin-api'; -import { Extension } from '@backstage/frontend-plugin-api'; +import { ExtensionDefinition } from '@backstage/frontend-plugin-api'; import { TranslationRef } from '@backstage/core-plugin-api/alpha'; // @alpha (undocumented) -export const AdrSearchResultListItemExtension: Extension<{ +export const AdrSearchResultListItemExtension: ExtensionDefinition<{ lineClamp: number; noTrack: boolean; }>; @@ -17,9 +17,9 @@ export const AdrSearchResultListItemExtension: Extension<{ export const adrTranslationRef: TranslationRef< 'adr', { - readonly content_header_title: 'Architecture Decision Records'; - readonly failed_to_fetch: 'Failed to fetch ADRs'; - readonly no_adrs: 'No ADRs found'; + readonly contentHeaderTitle: 'Architecture Decision Records'; + readonly failedToFetch: 'Failed to fetch ADRs'; + readonly notFound: 'No ADRs found'; } >; diff --git a/plugins/adr/package.json b/plugins/adr/package.json index 47468844ce..54d4bc7b8c 100644 --- a/plugins/adr/package.json +++ b/plugins/adr/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-adr", - "version": "0.6.11-next.0", + "version": "0.6.11-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/adr/src/alpha.tsx b/plugins/adr/src/alpha.tsx index 1f6fa0711d..ea4d32e0da 100644 --- a/plugins/adr/src/alpha.tsx +++ b/plugins/adr/src/alpha.tsx @@ -31,7 +31,6 @@ function isAdrDocument(result: any): result is AdrDocument { /** @alpha */ export const AdrSearchResultListItemExtension = createSearchResultListItemExtension({ - id: 'adr', configSchema: createSchemaFromZod(z => z.object({ // TODO: Define how the icon can be configurable diff --git a/plugins/adr/src/components/EntityAdrContent/EntityAdrContent.tsx b/plugins/adr/src/components/EntityAdrContent/EntityAdrContent.tsx index 261dd994fb..5c8d244d26 100644 --- a/plugins/adr/src/components/EntityAdrContent/EntityAdrContent.tsx +++ b/plugins/adr/src/components/EntityAdrContent/EntityAdrContent.tsx @@ -219,7 +219,7 @@ export const EntityAdrContent = (props: { return ( - + {appSupportConfigured && } @@ -230,7 +230,7 @@ export const EntityAdrContent = (props: { {loading && } {entityHasAdrs && !loading && error && ( - + )} {entityHasAdrs && @@ -257,7 +257,7 @@ export const EntityAdrContent = (props: { ) : ( - {t('no_adrs')} + {t('notFound')} ))} ); diff --git a/plugins/adr/src/translations.ts b/plugins/adr/src/translations.ts index cc0995f597..038b0fd064 100644 --- a/plugins/adr/src/translations.ts +++ b/plugins/adr/src/translations.ts @@ -19,8 +19,8 @@ import { createTranslationRef } from '@backstage/core-plugin-api/alpha'; export const adrTranslationRef = createTranslationRef({ id: 'adr', messages: { - content_header_title: 'Architecture Decision Records', - failed_to_fetch: 'Failed to fetch ADRs', - no_adrs: 'No ADRs found', + contentHeaderTitle: 'Architecture Decision Records', + failedToFetch: 'Failed to fetch ADRs', + notFound: 'No ADRs found', }, }); diff --git a/plugins/airbrake-backend/CHANGELOG.md b/plugins/airbrake-backend/CHANGELOG.md index 4ce8e4b3b5..da4b51d934 100644 --- a/plugins/airbrake-backend/CHANGELOG.md +++ b/plugins/airbrake-backend/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-airbrake-backend +## 0.3.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/config@1.1.1 + ## 0.3.5-next.0 ### Patch Changes diff --git a/plugins/airbrake-backend/package.json b/plugins/airbrake-backend/package.json index a3ac0b4c27..75a2ed104e 100644 --- a/plugins/airbrake-backend/package.json +++ b/plugins/airbrake-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-airbrake-backend", - "version": "0.3.5-next.0", + "version": "0.3.5-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/airbrake/CHANGELOG.md b/plugins/airbrake/CHANGELOG.md index 19bc827a72..19a506f8e6 100644 --- a/plugins/airbrake/CHANGELOG.md +++ b/plugins/airbrake/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-airbrake +## 0.3.28-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/test-utils@1.4.6-next.1 + - @backstage/dev-utils@1.0.25-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/theme@0.5.0-next.0 + ## 0.3.28-next.0 ### Patch Changes diff --git a/plugins/airbrake/package.json b/plugins/airbrake/package.json index 932aa55e91..07a41bb804 100644 --- a/plugins/airbrake/package.json +++ b/plugins/airbrake/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-airbrake", - "version": "0.3.28-next.0", + "version": "0.3.28-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/allure/CHANGELOG.md b/plugins/allure/CHANGELOG.md index c72b6eedb6..ca8cdcc87b 100644 --- a/plugins/allure/CHANGELOG.md +++ b/plugins/allure/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-allure +## 0.1.44-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/theme@0.5.0-next.0 + ## 0.1.44-next.0 ### Patch Changes diff --git a/plugins/allure/package.json b/plugins/allure/package.json index ee1952c819..5778ad0834 100644 --- a/plugins/allure/package.json +++ b/plugins/allure/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-allure", "description": "A Backstage plugin that integrates with Allure", - "version": "0.1.44-next.0", + "version": "0.1.44-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/analytics-module-ga/CHANGELOG.md b/plugins/analytics-module-ga/CHANGELOG.md index acf6f0eb98..ba46e16809 100644 --- a/plugins/analytics-module-ga/CHANGELOG.md +++ b/plugins/analytics-module-ga/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-analytics-module-ga +## 0.1.36-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/config@1.1.1 + - @backstage/theme@0.5.0-next.0 + ## 0.1.36-next.0 ### Patch Changes diff --git a/plugins/analytics-module-ga/package.json b/plugins/analytics-module-ga/package.json index 9cace65a5d..d2744caf9d 100644 --- a/plugins/analytics-module-ga/package.json +++ b/plugins/analytics-module-ga/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-analytics-module-ga", - "version": "0.1.36-next.0", + "version": "0.1.36-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/analytics-module-ga4/CHANGELOG.md b/plugins/analytics-module-ga4/CHANGELOG.md index a929abaf61..2e2b17ec8d 100644 --- a/plugins/analytics-module-ga4/CHANGELOG.md +++ b/plugins/analytics-module-ga4/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-analytics-module-ga4 +## 0.1.7-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/config@1.1.1 + - @backstage/theme@0.5.0-next.0 + ## 0.1.7-next.0 ### Patch Changes diff --git a/plugins/analytics-module-ga4/package.json b/plugins/analytics-module-ga4/package.json index 90155e34a8..246a1bf2d6 100644 --- a/plugins/analytics-module-ga4/package.json +++ b/plugins/analytics-module-ga4/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-analytics-module-ga4", - "version": "0.1.7-next.0", + "version": "0.1.7-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/analytics-module-newrelic-browser/CHANGELOG.md b/plugins/analytics-module-newrelic-browser/CHANGELOG.md index 86a86de836..f243c28481 100644 --- a/plugins/analytics-module-newrelic-browser/CHANGELOG.md +++ b/plugins/analytics-module-newrelic-browser/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-analytics-module-newrelic-browser +## 0.0.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/config@1.1.1 + ## 0.0.5-next.0 ### Patch Changes diff --git a/plugins/analytics-module-newrelic-browser/package.json b/plugins/analytics-module-newrelic-browser/package.json index c9d876c315..0cb59225cd 100644 --- a/plugins/analytics-module-newrelic-browser/package.json +++ b/plugins/analytics-module-newrelic-browser/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-analytics-module-newrelic-browser", - "version": "0.0.5-next.0", + "version": "0.0.5-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/apache-airflow/CHANGELOG.md b/plugins/apache-airflow/CHANGELOG.md index 959d447f55..e38a08e81f 100644 --- a/plugins/apache-airflow/CHANGELOG.md +++ b/plugins/apache-airflow/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-apache-airflow +## 0.2.18-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + ## 0.2.18-next.0 ### Patch Changes diff --git a/plugins/apache-airflow/package.json b/plugins/apache-airflow/package.json index f1d122f4f5..97a63fd4c1 100644 --- a/plugins/apache-airflow/package.json +++ b/plugins/apache-airflow/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-apache-airflow", - "version": "0.2.18-next.0", + "version": "0.2.18-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/api-docs/CHANGELOG.md b/plugins/api-docs/CHANGELOG.md index d88c90e769..594448351b 100644 --- a/plugins/api-docs/CHANGELOG.md +++ b/plugins/api-docs/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-api-docs +## 0.10.2-next.2 + +### Patch Changes + +- e16e7ce6a5: Updated dependency `@asyncapi/react-component` to `1.2.2`. +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/plugin-catalog@1.16.0-next.2 + - @backstage/catalog-model@1.4.3 + - @backstage/theme@0.5.0-next.0 + ## 0.10.2-next.1 ### Patch Changes diff --git a/plugins/api-docs/package.json b/plugins/api-docs/package.json index 5196d13a51..30b8650e19 100644 --- a/plugins/api-docs/package.json +++ b/plugins/api-docs/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-api-docs", "description": "A Backstage plugin that helps represent API entities in the frontend", - "version": "0.10.2-next.1", + "version": "0.10.2-next.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/apollo-explorer/CHANGELOG.md b/plugins/apollo-explorer/CHANGELOG.md index 26cda3787a..ba51605e0d 100644 --- a/plugins/apollo-explorer/CHANGELOG.md +++ b/plugins/apollo-explorer/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-apollo-explorer +## 0.1.18-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/theme@0.5.0-next.0 + ## 0.1.18-next.0 ### Patch Changes diff --git a/plugins/apollo-explorer/package.json b/plugins/apollo-explorer/package.json index 0cbc7cd292..0fbf80783d 100644 --- a/plugins/apollo-explorer/package.json +++ b/plugins/apollo-explorer/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-apollo-explorer", - "version": "0.1.18-next.0", + "version": "0.1.18-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/app-backend/CHANGELOG.md b/plugins/app-backend/CHANGELOG.md index d3c21b03fd..5ac1fe4aec 100644 --- a/plugins/app-backend/CHANGELOG.md +++ b/plugins/app-backend/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-app-backend +## 0.3.56-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/config@1.1.1 + - @backstage/config-loader@1.5.3 + - @backstage/types@1.1.1 + - @backstage/plugin-app-node@0.1.8-next.1 + ## 0.3.56-next.0 ### Patch Changes diff --git a/plugins/app-backend/alpha-api-report.md b/plugins/app-backend/api-report-alpha.md similarity index 100% rename from plugins/app-backend/alpha-api-report.md rename to plugins/app-backend/api-report-alpha.md diff --git a/plugins/app-backend/package.json b/plugins/app-backend/package.json index 87f6b6827c..235a7fd70b 100644 --- a/plugins/app-backend/package.json +++ b/plugins/app-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-app-backend", "description": "A Backstage backend plugin that serves the Backstage frontend app", - "version": "0.3.56-next.0", + "version": "0.3.56-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/app-node/CHANGELOG.md b/plugins/app-node/CHANGELOG.md index f901f5b879..b7069bc1c0 100644 --- a/plugins/app-node/CHANGELOG.md +++ b/plugins/app-node/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-app-node +## 0.1.8-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.8-next.1 + ## 0.1.8-next.0 ### Patch Changes diff --git a/plugins/app-node/package.json b/plugins/app-node/package.json index 042703f5ab..b6e805c857 100644 --- a/plugins/app-node/package.json +++ b/plugins/app-node/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-app-node", "description": "Node.js library for the app plugin", - "version": "0.1.8-next.0", + "version": "0.1.8-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/auth-backend-module-atlassian-provider/CHANGELOG.md b/plugins/auth-backend-module-atlassian-provider/CHANGELOG.md index 7e02d921ea..5eca1e2a07 100644 --- a/plugins/auth-backend-module-atlassian-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-atlassian-provider/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-auth-backend-module-atlassian-provider +## 0.1.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/plugin-auth-node@0.4.2-next.1 + ## 0.1.0-next.0 ### Minor Changes diff --git a/plugins/auth-backend-module-atlassian-provider/package.json b/plugins/auth-backend-module-atlassian-provider/package.json index a75aae73ea..c2bfc37c02 100644 --- a/plugins/auth-backend-module-atlassian-provider/package.json +++ b/plugins/auth-backend-module-atlassian-provider/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-auth-backend-module-atlassian-provider", "description": "The atlassian-provider backend module for the auth plugin.", - "version": "0.1.0-next.0", + "version": "0.1.0-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -27,7 +27,7 @@ "@backstage/backend-plugin-api": "workspace:^", "@backstage/plugin-auth-node": "workspace:^", "express": "^4.18.2", - "passport": "^0.6.0", + "passport": "^0.7.0", "passport-atlassian-oauth2": "^2.1.0" }, "devDependencies": { diff --git a/plugins/auth-backend-module-gcp-iap-provider/CHANGELOG.md b/plugins/auth-backend-module-gcp-iap-provider/CHANGELOG.md index 60aa68020a..cfd164ebfe 100644 --- a/plugins/auth-backend-module-gcp-iap-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-gcp-iap-provider/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-auth-backend-module-gcp-iap-provider +## 0.2.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.2-next.1 + ## 0.2.2-next.0 ### Patch Changes diff --git a/plugins/auth-backend-module-gcp-iap-provider/package.json b/plugins/auth-backend-module-gcp-iap-provider/package.json index 2dbb69c8cd..f7d4ea7bc7 100644 --- a/plugins/auth-backend-module-gcp-iap-provider/package.json +++ b/plugins/auth-backend-module-gcp-iap-provider/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-auth-backend-module-gcp-iap-provider", "description": "A GCP IAP auth provider module for the Backstage auth backend", - "version": "0.2.2-next.0", + "version": "0.2.2-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/auth-backend-module-gcp-iap-provider/src/module.ts b/plugins/auth-backend-module-gcp-iap-provider/src/module.ts index 90771eecb9..23c503c317 100644 --- a/plugins/auth-backend-module-gcp-iap-provider/src/module.ts +++ b/plugins/auth-backend-module-gcp-iap-provider/src/module.ts @@ -26,7 +26,7 @@ import { gcpIapSignInResolvers } from './resolvers'; /** @public */ export const authModuleGcpIapProvider = createBackendModule({ pluginId: 'auth', - moduleId: 'gcpIapProvider', + moduleId: 'gcp-iap-provider', register(reg) { reg.registerInit({ deps: { diff --git a/plugins/auth-backend-module-github-provider/CHANGELOG.md b/plugins/auth-backend-module-github-provider/CHANGELOG.md index 3bf47f8a7a..5b3acd377d 100644 --- a/plugins/auth-backend-module-github-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-github-provider/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-auth-backend-module-github-provider +## 0.1.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/plugin-auth-node@0.4.2-next.1 + ## 0.1.5-next.0 ### Patch Changes diff --git a/plugins/auth-backend-module-github-provider/package.json b/plugins/auth-backend-module-github-provider/package.json index 218ec94cac..9ed4f7d3b9 100644 --- a/plugins/auth-backend-module-github-provider/package.json +++ b/plugins/auth-backend-module-github-provider/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-auth-backend-module-github-provider", "description": "The github-provider backend module for the auth plugin.", - "version": "0.1.5-next.0", + "version": "0.1.5-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/auth-backend-module-gitlab-provider/CHANGELOG.md b/plugins/auth-backend-module-gitlab-provider/CHANGELOG.md index c610e93ec8..03fb405e56 100644 --- a/plugins/auth-backend-module-gitlab-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-gitlab-provider/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-auth-backend-module-gitlab-provider +## 0.1.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/plugin-auth-node@0.4.2-next.1 + ## 0.1.5-next.0 ### Patch Changes diff --git a/plugins/auth-backend-module-gitlab-provider/package.json b/plugins/auth-backend-module-gitlab-provider/package.json index 9d931e628c..56c6b43af3 100644 --- a/plugins/auth-backend-module-gitlab-provider/package.json +++ b/plugins/auth-backend-module-gitlab-provider/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-auth-backend-module-gitlab-provider", "description": "The gitlab-provider backend module for the auth plugin.", - "version": "0.1.5-next.0", + "version": "0.1.5-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -27,7 +27,7 @@ "@backstage/backend-plugin-api": "workspace:^", "@backstage/plugin-auth-node": "workspace:^", "express": "^4.18.2", - "passport": "^0.6.0", + "passport": "^0.7.0", "passport-gitlab2": "^5.0.0" }, "devDependencies": { diff --git a/plugins/auth-backend-module-google-provider/CHANGELOG.md b/plugins/auth-backend-module-google-provider/CHANGELOG.md index a8edf35c7e..30fcf563b7 100644 --- a/plugins/auth-backend-module-google-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-google-provider/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-auth-backend-module-google-provider +## 0.1.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/plugin-auth-node@0.4.2-next.1 + ## 0.1.5-next.0 ### Patch Changes diff --git a/plugins/auth-backend-module-google-provider/package.json b/plugins/auth-backend-module-google-provider/package.json index 1cbb87c5b2..6ba15b3938 100644 --- a/plugins/auth-backend-module-google-provider/package.json +++ b/plugins/auth-backend-module-google-provider/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-auth-backend-module-google-provider", "description": "A Google auth provider module for the Backstage auth backend", - "version": "0.1.5-next.0", + "version": "0.1.5-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/auth-backend-module-google-provider/src/module.ts b/plugins/auth-backend-module-google-provider/src/module.ts index 0354f99880..d0f8020b92 100644 --- a/plugins/auth-backend-module-google-provider/src/module.ts +++ b/plugins/auth-backend-module-google-provider/src/module.ts @@ -26,7 +26,7 @@ import { googleSignInResolvers } from './resolvers'; /** @public */ export const authModuleGoogleProvider = createBackendModule({ pluginId: 'auth', - moduleId: 'googleProvider', + moduleId: 'google-provider', register(reg) { reg.registerInit({ deps: { diff --git a/plugins/auth-backend-module-microsoft-provider/CHANGELOG.md b/plugins/auth-backend-module-microsoft-provider/CHANGELOG.md index 76bf7d60d7..df5aea992d 100644 --- a/plugins/auth-backend-module-microsoft-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-microsoft-provider/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-auth-backend-module-microsoft-provider +## 0.1.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/plugin-auth-node@0.4.2-next.1 + ## 0.1.3-next.0 ### Patch Changes diff --git a/plugins/auth-backend-module-microsoft-provider/package.json b/plugins/auth-backend-module-microsoft-provider/package.json index e67da9228c..47324f9ab9 100644 --- a/plugins/auth-backend-module-microsoft-provider/package.json +++ b/plugins/auth-backend-module-microsoft-provider/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-auth-backend-module-microsoft-provider", "description": "The microsoft-provider backend module for the auth plugin.", - "version": "0.1.3-next.0", + "version": "0.1.3-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -29,7 +29,7 @@ "express": "^4.18.2", "jose": "^4.6.0", "node-fetch": "^2.6.7", - "passport": "^0.6.0", + "passport": "^0.7.0", "passport-microsoft": "^1.0.0" }, "devDependencies": { diff --git a/plugins/auth-backend-module-oauth2-provider/CHANGELOG.md b/plugins/auth-backend-module-oauth2-provider/CHANGELOG.md index bae3565d3b..5f7a5dc4b8 100644 --- a/plugins/auth-backend-module-oauth2-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-oauth2-provider/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-auth-backend-module-oauth2-provider +## 0.1.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/plugin-auth-node@0.4.2-next.1 + ## 0.1.5-next.0 ### Patch Changes diff --git a/plugins/auth-backend-module-oauth2-provider/package.json b/plugins/auth-backend-module-oauth2-provider/package.json index cc672f77b9..f46428f4f3 100644 --- a/plugins/auth-backend-module-oauth2-provider/package.json +++ b/plugins/auth-backend-module-oauth2-provider/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-auth-backend-module-oauth2-provider", "description": "The oauth2-provider backend module for the auth plugin.", - "version": "0.1.5-next.0", + "version": "0.1.5-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -27,7 +27,7 @@ "@backstage/backend-plugin-api": "workspace:^", "@backstage/plugin-auth-node": "workspace:^", "express": "^4.18.2", - "passport": "^0.6.0", + "passport": "^0.7.0", "passport-oauth2": "^1.6.1" }, "devDependencies": { diff --git a/plugins/auth-backend-module-oauth2-proxy-provider/.eslintrc.js b/plugins/auth-backend-module-oauth2-proxy-provider/.eslintrc.js new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/plugins/auth-backend-module-oauth2-proxy-provider/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/auth-backend-module-oauth2-proxy-provider/CHANGELOG.md b/plugins/auth-backend-module-oauth2-proxy-provider/CHANGELOG.md new file mode 100644 index 0000000000..ed2ec641b9 --- /dev/null +++ b/plugins/auth-backend-module-oauth2-proxy-provider/CHANGELOG.md @@ -0,0 +1,15 @@ +# @backstage/plugin-auth-backend-module-oauth2-proxy-provider + +## 0.1.0-next.0 + +### Minor Changes + +- 271aa12c7c: Release of `oauth2-proxy-provider` plugin + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-auth-node@0.4.2-next.1 diff --git a/plugins/auth-backend-module-oauth2-proxy-provider/README.md b/plugins/auth-backend-module-oauth2-proxy-provider/README.md new file mode 100644 index 0000000000..edec8fb3c2 --- /dev/null +++ b/plugins/auth-backend-module-oauth2-proxy-provider/README.md @@ -0,0 +1,5 @@ +# @backstage/plugin-auth-backend-module-oauth2-proxy-provider + +The oauth2-proxy-provider backend module for the auth plugin. + +_This plugin was created through the Backstage CLI_ diff --git a/plugins/auth-backend-module-oauth2-proxy-provider/api-report.md b/plugins/auth-backend-module-oauth2-proxy-provider/api-report.md new file mode 100644 index 0000000000..1a4c1ee6d6 --- /dev/null +++ b/plugins/auth-backend-module-oauth2-proxy-provider/api-report.md @@ -0,0 +1,32 @@ +## API Report File for "@backstage/plugin-auth-backend-module-oauth2-proxy-provider" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +/// + +import { BackendFeature } from '@backstage/backend-plugin-api'; +import { IncomingHttpHeaders } from 'http'; +import { ProxyAuthenticator } from '@backstage/plugin-auth-node'; + +// @public (undocumented) +const authModuleOauth2ProxyProvider: () => BackendFeature; +export default authModuleOauth2ProxyProvider; + +// @public +export const OAUTH2_PROXY_JWT_HEADER = 'X-OAUTH2-PROXY-ID-TOKEN'; + +// @public (undocumented) +export const oauth2ProxyAuthenticator: ProxyAuthenticator< + unknown, + OAuth2ProxyResult +>; + +// @public +export type OAuth2ProxyResult = { + fullProfile: JWTPayload; + accessToken: string; + headers: IncomingHttpHeaders; + getHeader(name: string): string | undefined; +}; +``` diff --git a/plugins/auth-backend-module-oauth2-proxy-provider/catalog-info.yaml b/plugins/auth-backend-module-oauth2-proxy-provider/catalog-info.yaml new file mode 100644 index 0000000000..c26202c97d --- /dev/null +++ b/plugins/auth-backend-module-oauth2-proxy-provider/catalog-info.yaml @@ -0,0 +1,10 @@ +apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + name: backstage-plugin-auth-backend-module-oauth2-proxy-provider + title: '@backstage/plugin-auth-backend-module-oauth2-proxy-provider' + description: The oauth2-proxy-provider backend module for the auth plugin. +spec: + lifecycle: experimental + type: backstage-backend-plugin-module + owner: maintainers diff --git a/plugins/auth-backend-module-oauth2-proxy-provider/package.json b/plugins/auth-backend-module-oauth2-proxy-provider/package.json new file mode 100644 index 0000000000..090a64ae66 --- /dev/null +++ b/plugins/auth-backend-module-oauth2-proxy-provider/package.json @@ -0,0 +1,39 @@ +{ + "name": "@backstage/plugin-auth-backend-module-oauth2-proxy-provider", + "description": "The oauth2-proxy-provider backend module for the auth plugin.", + "version": "0.1.0-next.0", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "publishConfig": { + "access": "public", + "main": "dist/index.cjs.js", + "types": "dist/index.d.ts" + }, + "backstage": { + "role": "backend-plugin-module" + }, + "scripts": { + "start": "backstage-cli package start", + "build": "backstage-cli package build", + "lint": "backstage-cli package lint", + "test": "backstage-cli package test", + "clean": "backstage-cli package clean", + "prepack": "backstage-cli package prepack", + "postpack": "backstage-cli package postpack" + }, + "dependencies": { + "@backstage/backend-common": "workspace:^", + "@backstage/backend-plugin-api": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/plugin-auth-node": "workspace:^", + "jose": "^4.6.0" + }, + "devDependencies": { + "@backstage/backend-test-utils": "workspace:^", + "@backstage/cli": "workspace:^" + }, + "files": [ + "dist" + ] +} diff --git a/plugins/auth-backend-module-oauth2-proxy-provider/src/authenticator.ts b/plugins/auth-backend-module-oauth2-proxy-provider/src/authenticator.ts new file mode 100644 index 0000000000..a3da83791a --- /dev/null +++ b/plugins/auth-backend-module-oauth2-proxy-provider/src/authenticator.ts @@ -0,0 +1,71 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { AuthenticationError } from '@backstage/errors'; +import { + createProxyAuthenticator, + getBearerTokenFromAuthorizationHeader, +} from '@backstage/plugin-auth-node'; +import { decodeJwt } from 'jose'; +import { OAuth2ProxyResult } from './types'; + +/** + * NOTE: This may come in handy if you're doing work on this provider: + * plugins/auth-backend/examples/docker-compose.oauth2-proxy.yaml + * + * @public + */ +export const OAUTH2_PROXY_JWT_HEADER = 'X-OAUTH2-PROXY-ID-TOKEN'; + +/** @public */ +export const oauth2ProxyAuthenticator = createProxyAuthenticator< + unknown, + OAuth2ProxyResult +>({ + defaultProfileTransform: async (result: OAuth2ProxyResult) => { + return { + profile: { + email: result.getHeader('x-forwarded-email'), + displayName: + result.getHeader('x-forwarded-preferred-username') || + result.getHeader('x-forwarded-user'), + }, + }; + }, + async initialize() {}, + async authenticate({ req }) { + try { + const authHeader = req.header(OAUTH2_PROXY_JWT_HEADER); + const jwt = getBearerTokenFromAuthorizationHeader(authHeader); + const decodedJWT = jwt && decodeJwt(jwt); + + const result = { + fullProfile: decodedJWT || {}, + accessToken: jwt || '', + headers: req.headers, + getHeader(name: string) { + if (name.toLocaleLowerCase('en-US') === 'set-cookie') { + throw new Error('Access Set-Cookie via the headers object instead'); + } + return req.get(name); + }, + }; + return { result }; + } catch (e) { + throw new AuthenticationError('Authentication failed', e); + } + }, +}); diff --git a/plugins/auth-backend-module-oauth2-proxy-provider/src/index.ts b/plugins/auth-backend-module-oauth2-proxy-provider/src/index.ts new file mode 100644 index 0000000000..b804f666c7 --- /dev/null +++ b/plugins/auth-backend-module-oauth2-proxy-provider/src/index.ts @@ -0,0 +1,27 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * The oauth2-proxy-provider backend module for the auth plugin. + * + * @packageDocumentation + */ +export { authModuleOauth2ProxyProvider as default } from './module'; +export { + oauth2ProxyAuthenticator, + OAUTH2_PROXY_JWT_HEADER, +} from './authenticator'; +export type { OAuth2ProxyResult } from './types'; diff --git a/plugins/auth-backend-module-oauth2-proxy-provider/src/module.ts b/plugins/auth-backend-module-oauth2-proxy-provider/src/module.ts new file mode 100644 index 0000000000..70ce506d73 --- /dev/null +++ b/plugins/auth-backend-module-oauth2-proxy-provider/src/module.ts @@ -0,0 +1,49 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { createBackendModule } from '@backstage/backend-plugin-api'; +import { + authProvidersExtensionPoint, + createProxyAuthProviderFactory, + commonSignInResolvers, +} from '@backstage/plugin-auth-node'; +import { oauth2ProxyAuthenticator } from './authenticator'; +import { oauth2ProxySignInResolvers } from './resolvers'; + +/** @public */ +export const authModuleOauth2ProxyProvider = createBackendModule({ + pluginId: 'auth', + moduleId: 'oauth2-proxy-provider', + register(reg) { + reg.registerInit({ + deps: { + providers: authProvidersExtensionPoint, + }, + async init({ providers }) { + providers.registerProvider({ + providerId: 'oauth2Proxy', + factory: createProxyAuthProviderFactory({ + authenticator: oauth2ProxyAuthenticator, + signInResolverFactories: { + ...commonSignInResolvers, + ...oauth2ProxySignInResolvers, + }, + }), + }); + }, + }); + }, +}); diff --git a/plugins/auth-backend-module-oauth2-proxy-provider/src/resolvers.ts b/plugins/auth-backend-module-oauth2-proxy-provider/src/resolvers.ts new file mode 100644 index 0000000000..99ca8bc156 --- /dev/null +++ b/plugins/auth-backend-module-oauth2-proxy-provider/src/resolvers.ts @@ -0,0 +1,41 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + createSignInResolverFactory, + SignInInfo, +} from '@backstage/plugin-auth-node'; +import { OAuth2ProxyResult } from './types'; + +/** + * @public + */ +export namespace oauth2ProxySignInResolvers { + export const forwardedUserMatchingUserEntityName = + createSignInResolverFactory({ + create() { + return async (info: SignInInfo, ctx) => { + const name = info.result.getHeader('x-forwarded-user'); + if (!name) { + throw new Error('Request did not contain a user'); + } + return ctx.signInWithCatalogUser({ + entityRef: { name }, + }); + }; + }, + }); +} diff --git a/plugins/auth-backend-module-oauth2-proxy-provider/src/types.ts b/plugins/auth-backend-module-oauth2-proxy-provider/src/types.ts new file mode 100644 index 0000000000..5b9e97ab05 --- /dev/null +++ b/plugins/auth-backend-module-oauth2-proxy-provider/src/types.ts @@ -0,0 +1,60 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { IncomingHttpHeaders } from 'http'; + +/** + * JWT header extraction result, containing the raw value and the parsed JWT + * payload. + * + * @public + */ +export type OAuth2ProxyResult = { + /** + * The parsed payload of the `accessToken`. The token is only parsed, not verified. + * + * @deprecated Access through the `headers` instead. This will be removed in a future release. + */ + fullProfile: JWTPayload; + + /** + * The token received via the X-OAUTH2-PROXY-ID-TOKEN header. Will be an empty string + * if the header is not set. Note the this is typically an OpenID Connect token. + * + * @deprecated Access through the `headers` instead. This will be removed in a future release. + */ + accessToken: string; + + /** + * The headers of the incoming request from the OAuth2 proxy. This will include + * both the headers set by the client as well as the ones added by the OAuth2 proxy. + * You should only trust the headers that are injected by the OAuth2 proxy. + * + * Useful headers to use to complete the sign-in are for example `x-forwarded-user` + * and `x-forwarded-email`. See the OAuth2 proxy documentation for more information + * about the available headers and how to enable them. In particular it is possible + * to forward access and identity tokens, which can be user for additional verification + * and lookups. + */ + headers: IncomingHttpHeaders; + + /** + * Provides convenient access to the request headers. + * + * This call is simply forwarded to `req.get(name)`. + */ + getHeader(name: string): string | undefined; +}; diff --git a/plugins/auth-backend-module-okta-provider/CHANGELOG.md b/plugins/auth-backend-module-okta-provider/CHANGELOG.md index 98782fccb5..6b0f8e6304 100644 --- a/plugins/auth-backend-module-okta-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-okta-provider/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-auth-backend-module-okta-provider +## 0.0.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/plugin-auth-node@0.4.2-next.1 + ## 0.0.1-next.0 ### Patch Changes diff --git a/plugins/auth-backend-module-okta-provider/package.json b/plugins/auth-backend-module-okta-provider/package.json index a9e8606335..178ebf1465 100644 --- a/plugins/auth-backend-module-okta-provider/package.json +++ b/plugins/auth-backend-module-okta-provider/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-auth-backend-module-okta-provider", "description": "The okta-provider backend module for the auth plugin.", - "version": "0.0.1-next.0", + "version": "0.0.1-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -28,7 +28,7 @@ "@backstage/plugin-auth-node": "workspace:^", "@davidzemon/passport-okta-oauth": "^0.0.5", "express": "^4.18.2", - "passport": "^0.6.0" + "passport": "^0.7.0" }, "devDependencies": { "@backstage/backend-defaults": "workspace:^", diff --git a/plugins/auth-backend-module-pinniped-provider/CHANGELOG.md b/plugins/auth-backend-module-pinniped-provider/CHANGELOG.md index 57d4e5611a..c2334029f2 100644 --- a/plugins/auth-backend-module-pinniped-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-pinniped-provider/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-auth-backend-module-pinniped-provider +## 0.1.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/config@1.1.1 + - @backstage/plugin-auth-node@0.4.2-next.1 + ## 0.1.2-next.0 ### Patch Changes diff --git a/plugins/auth-backend-module-pinniped-provider/package.json b/plugins/auth-backend-module-pinniped-provider/package.json index 2b572696fd..3358a9d747 100644 --- a/plugins/auth-backend-module-pinniped-provider/package.json +++ b/plugins/auth-backend-module-pinniped-provider/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-auth-backend-module-pinniped-provider", "description": "The pinniped-provider backend module for the auth plugin.", - "version": "0.1.2-next.0", + "version": "0.1.2-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -41,7 +41,7 @@ "express-session": "^1.17.3", "jose": "^4.14.6", "msw": "^1.3.0", - "passport": "^0.6.0", + "passport": "^0.7.0", "supertest": "^6.3.3" }, "files": [ diff --git a/plugins/auth-backend-module-vmware-cloud-provider/CHANGELOG.md b/plugins/auth-backend-module-vmware-cloud-provider/CHANGELOG.md new file mode 100644 index 0000000000..332872c5cf --- /dev/null +++ b/plugins/auth-backend-module-vmware-cloud-provider/CHANGELOG.md @@ -0,0 +1,15 @@ +# @backstage/plugin-auth-backend-module-vmware-cloud-provider + +## 0.1.0-next.0 + +### Minor Changes + +- ed02c69a3c: Add VMware Cloud auth backend module provider + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/plugin-auth-node@0.4.2-next.1 diff --git a/plugins/auth-backend-module-vmware-cloud-provider/package.json b/plugins/auth-backend-module-vmware-cloud-provider/package.json index f48e69154b..db9de2741d 100644 --- a/plugins/auth-backend-module-vmware-cloud-provider/package.json +++ b/plugins/auth-backend-module-vmware-cloud-provider/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-auth-backend-module-vmware-cloud-provider", "description": "The vmware-cloud-provider backend module for the auth plugin.", - "version": "0.0.0", + "version": "0.1.0-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/auth-backend/CHANGELOG.md b/plugins/auth-backend/CHANGELOG.md index 2ab0a71b77..5e818e299a 100644 --- a/plugins/auth-backend/CHANGELOG.md +++ b/plugins/auth-backend/CHANGELOG.md @@ -1,5 +1,30 @@ # @backstage/plugin-auth-backend +## 0.20.1-next.1 + +### Patch Changes + +- 7ac25759a5: `oauth2-proxy` auth implementation has been moved to `@backstage/plugin-auth-backend-module-oauth2-proxy-provider` +- bcbbf8e042: Updated dependency `@google-cloud/firestore` to `^7.0.0`. +- Updated dependencies + - @backstage/plugin-auth-backend-module-oauth2-proxy-provider@0.1.0-next.0 + - @backstage/catalog-client@1.5.0-next.0 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/plugin-auth-backend-module-atlassian-provider@0.1.0-next.1 + - @backstage/plugin-auth-backend-module-github-provider@0.1.5-next.1 + - @backstage/plugin-auth-backend-module-gitlab-provider@0.1.5-next.1 + - @backstage/plugin-auth-backend-module-google-provider@0.1.5-next.1 + - @backstage/plugin-auth-backend-module-oauth2-provider@0.1.5-next.1 + - @backstage/plugin-auth-backend-module-okta-provider@0.0.1-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-backend-module-gcp-iap-provider@0.2.2-next.1 + - @backstage/plugin-auth-node@0.4.2-next.1 + - @backstage/plugin-catalog-node@1.5.1-next.1 + ## 0.20.1-next.0 ### Patch Changes diff --git a/plugins/auth-backend/api-report.md b/plugins/auth-backend/api-report.md index c20ddbe5a8..c269a09d2c 100644 --- a/plugins/auth-backend/api-report.md +++ b/plugins/auth-backend/api-report.md @@ -3,8 +3,6 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -/// - import { AuthProviderConfig as AuthProviderConfig_2 } from '@backstage/plugin-auth-node'; import { AuthProviderFactory as AuthProviderFactory_2 } from '@backstage/plugin-auth-node'; import { AuthProviderRouteHandlers as AuthProviderRouteHandlers_2 } from '@backstage/plugin-auth-node'; @@ -23,8 +21,8 @@ import { Entity } from '@backstage/catalog-model'; import express from 'express'; import { GcpIapResult as GcpIapResult_2 } from '@backstage/plugin-auth-backend-module-gcp-iap-provider'; import { GcpIapTokenInfo as GcpIapTokenInfo_2 } from '@backstage/plugin-auth-backend-module-gcp-iap-provider'; -import { IncomingHttpHeaders } from 'http'; import { LoggerService } from '@backstage/backend-plugin-api'; +import { OAuth2ProxyResult as OAuth2ProxyResult_2 } from '@backstage/plugin-auth-backend-module-oauth2-proxy-provider'; import { OAuthEnvironmentHandler as OAuthEnvironmentHandler_2 } from '@backstage/plugin-auth-node'; import { OAuthState as OAuthState_2 } from '@backstage/plugin-auth-node'; import { PluginDatabaseManager } from '@backstage/backend-common'; @@ -228,13 +226,8 @@ export type GithubOAuthResult = { refreshToken?: string; }; -// @public -export type OAuth2ProxyResult = { - fullProfile: JWTPayload; - accessToken: string; - headers: IncomingHttpHeaders; - getHeader(name: string): string | undefined; -}; +// @public @deprecated (undocumented) +export type OAuth2ProxyResult = OAuth2ProxyResult_2; // @public @deprecated (undocumented) export class OAuthAdapter implements AuthProviderRouteHandlers { @@ -560,9 +553,9 @@ export const providers: Readonly<{ }>; oauth2Proxy: Readonly<{ create: (options: { - authHandler?: AuthHandler> | undefined; + authHandler?: AuthHandler | undefined; signIn: { - resolver: SignInResolver>; + resolver: SignInResolver; }; }) => AuthProviderFactory_2; resolvers: never; diff --git a/plugins/auth-backend/package.json b/plugins/auth-backend/package.json index 67d1b0734d..2ce075087f 100644 --- a/plugins/auth-backend/package.json +++ b/plugins/auth-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-auth-backend", "description": "A Backstage backend plugin that handles authentication", - "version": "0.20.1-next.0", + "version": "0.20.1-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -44,6 +44,7 @@ "@backstage/plugin-auth-backend-module-gitlab-provider": "workspace:^", "@backstage/plugin-auth-backend-module-google-provider": "workspace:^", "@backstage/plugin-auth-backend-module-oauth2-provider": "workspace:^", + "@backstage/plugin-auth-backend-module-oauth2-proxy-provider": "workspace:^", "@backstage/plugin-auth-backend-module-okta-provider": "workspace:^", "@backstage/plugin-auth-node": "workspace:^", "@backstage/plugin-catalog-node": "workspace:^", @@ -70,7 +71,7 @@ "node-cache": "^5.1.2", "node-fetch": "^2.6.7", "openid-client": "^5.2.1", - "passport": "^0.6.0", + "passport": "^0.7.0", "passport-auth0": "^1.4.3", "passport-bitbucket-oauth2": "^0.1.2", "passport-github2": "^0.1.12", diff --git a/plugins/auth-backend/src/identity/KeyStores.ts b/plugins/auth-backend/src/identity/KeyStores.ts index 7242530c57..16f25817a9 100644 --- a/plugins/auth-backend/src/identity/KeyStores.ts +++ b/plugins/auth-backend/src/identity/KeyStores.ts @@ -76,7 +76,7 @@ export class KeyStores { } if (provider === 'static') { - await StaticKeyStore.fromConfig(config); + return await StaticKeyStore.fromConfig(config); } throw new Error(`Unknown KeyStore provider: ${provider}`); diff --git a/plugins/auth-backend/src/providers/oauth2-proxy/index.ts b/plugins/auth-backend/src/providers/oauth2-proxy/index.ts index d3004f31e4..2e4e7d016f 100644 --- a/plugins/auth-backend/src/providers/oauth2-proxy/index.ts +++ b/plugins/auth-backend/src/providers/oauth2-proxy/index.ts @@ -15,4 +15,10 @@ */ export { oauth2Proxy } from './provider'; -export type { OAuth2ProxyResult } from './provider'; +import { OAuth2ProxyResult as _OAuth2ProxyResult } from '@backstage/plugin-auth-backend-module-oauth2-proxy-provider'; + +/** + * @public + * @deprecated import from `@backstage/plugin-auth-backend-module-oauth2-proxy-provider` instead + */ +export type OAuth2ProxyResult = _OAuth2ProxyResult; diff --git a/plugins/auth-backend/src/providers/oauth2-proxy/provider.test.ts b/plugins/auth-backend/src/providers/oauth2-proxy/provider.test.ts deleted file mode 100644 index f585d4831f..0000000000 --- a/plugins/auth-backend/src/providers/oauth2-proxy/provider.test.ts +++ /dev/null @@ -1,205 +0,0 @@ -/* - * Copyright 2021 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -jest.mock('jose', () => ({ - decodeJwt: jest.fn(), -})); -jest.mock('@backstage/catalog-client'); - -import { AuthenticationError } from '@backstage/errors'; -import express from 'express'; -import * as jose from 'jose'; -import { LoggerService } from '@backstage/backend-plugin-api'; -import { AuthHandler, AuthResolverContext, SignInResolver } from '../types'; -import { - oauth2Proxy, - Oauth2ProxyAuthProvider, - OAuth2ProxyResult, - OAUTH2_PROXY_JWT_HEADER, -} from './provider'; - -describe('Oauth2ProxyAuthProvider', () => { - const mockToken = - 'eyblob.eyJzdWIiOiJ1c2VyOmRlZmF1bHQvamltbXltYXJrdW0iLCJlbnQiOlsidXNlcjpkZWZhdWx0L2ppbW15bWFya3VtIl19.eyblob'; - - let provider: Oauth2ProxyAuthProvider; - let logger: jest.Mocked; - let signInResolver: jest.MockedFunction< - SignInResolver> - >; - let authHandler: jest.MockedFunction>>; - let mockResponse: jest.Mocked; - let mockRequest: jest.Mocked; - let mockJwtDecode: jest.MockedFunction; - - beforeEach(() => { - jest.resetAllMocks(); - - mockJwtDecode = jose.decodeJwt as jest.MockedFunction< - typeof jose.decodeJwt - >; - authHandler = jest.fn(); - signInResolver = jest.fn(); - logger = { error: jest.fn() } as unknown as jest.Mocked; - - mockResponse = { - status: jest.fn(), - end: jest.fn(), - json: jest.fn(), - } as unknown as jest.Mocked; - - mockRequest = { - body: {}, - header: jest.fn(), - headers: { - 'x-mock': 'mock', - }, - } as unknown as jest.Mocked; - - provider = new Oauth2ProxyAuthProvider({ - authHandler, - signInResolver, - resolverContext: { - _: 'resolver-context', - } as unknown as AuthResolverContext, - }); - }); - - describe('frameHandler()', () => { - it('should do nothing and return undefined', async () => { - const result = await provider.frameHandler(); - - expect(result).toBeUndefined(); - }); - }); - - describe('start()', () => { - it('should do nothing and return undefined', async () => { - const result = await provider.start(); - - expect(result).toBeUndefined(); - }); - }); - - describe('refresh()', () => { - it('should throw an error when auth header is missing', async () => { - mockRequest.header.mockReturnValue(undefined); - - await expect(provider.refresh(mockRequest, mockResponse)).rejects.toThrow( - AuthenticationError, - ); - }); - - it('should throw an error if the bearer token is invalid', async () => { - mockRequest.header.mockReturnValue('Basic asdf='); - - await expect(provider.refresh(mockRequest, mockResponse)).rejects.toThrow( - AuthenticationError, - ); - }); - - it('should return if auth header is set and valid', async () => { - mockRequest.header.mockReturnValue(`Bearer token`); - authHandler.mockResolvedValue({ - profile: {}, - }); - signInResolver.mockResolvedValue({ - token: mockToken, - }); - - await provider.refresh(mockRequest, mockResponse); - - expect(mockRequest.header).toHaveBeenCalledWith(OAUTH2_PROXY_JWT_HEADER); - expect(mockJwtDecode).toHaveBeenCalledWith('token'); - expect(mockResponse.json).toHaveBeenCalled(); - }); - - it('should load profile from authHandler and backstage identity from signInResolver', async () => { - const decodedToken = { - oid: 'oid', - name: 'name', - upn: 'john.doe@example.com', - }; - const profile = { displayName: 'some value' }; - mockRequest.header.mockReturnValue(`Bearer token`); - signInResolver.mockResolvedValue({ - token: mockToken, - }); - authHandler.mockResolvedValue({ profile: profile }); - mockJwtDecode.mockReturnValue(decodedToken as any); - - await provider.refresh(mockRequest, mockResponse); - - expect(signInResolver).toHaveBeenCalledWith( - { - profile: profile, - result: { - accessToken: 'token', - fullProfile: decodedToken, - getHeader: expect.any(Function), - headers: { - 'x-mock': 'mock', - }, - }, - }, - { _: 'resolver-context' }, - ); - expect(mockResponse.json).toHaveBeenCalledWith({ - backstageIdentity: { - identity: { - type: 'user', - userEntityRef: 'user:default/jimmymarkum', - ownershipEntityRefs: ['user:default/jimmymarkum'], - }, - token: mockToken, - }, - profile: { displayName: 'some value' }, - providerInfo: { - accessToken: 'token', - }, - }); - }); - }); - - describe('oauth2Proxy.create()', () => { - beforeEach(() => { - mockRequest.header.mockReturnValue(`Bearer token`); - authHandler.mockResolvedValue({ - profile: {}, - }); - signInResolver.mockResolvedValue({ - token: mockToken, - }); - }); - - it('should create a valid provider', async () => { - const factory = oauth2Proxy.create({ - authHandler, - signIn: { resolver: signInResolver }, - }); - const handler = factory({ - logger, - catalogApi: {}, - tokenIssuer: {}, - } as any); - await handler.refresh!(mockRequest, mockResponse); - - expect(mockRequest.header).toHaveBeenCalledWith(OAUTH2_PROXY_JWT_HEADER); - expect(mockJwtDecode).toHaveBeenCalledWith('token'); - expect(mockResponse.json).toHaveBeenCalled(); - }); - }); -}); diff --git a/plugins/auth-backend/src/providers/oauth2-proxy/provider.ts b/plugins/auth-backend/src/providers/oauth2-proxy/provider.ts index 3ac7d09000..5d75167e84 100644 --- a/plugins/auth-backend/src/providers/oauth2-proxy/provider.ts +++ b/plugins/auth-backend/src/providers/oauth2-proxy/provider.ts @@ -14,164 +14,13 @@ * limitations under the License. */ -import express from 'express'; -import { AuthenticationError } from '@backstage/errors'; -import { getBearerTokenFromAuthorizationHeader } from '@backstage/plugin-auth-node'; -import { - AuthHandler, - SignInResolver, - AuthProviderRouteHandlers, - AuthResponse, - AuthResolverContext, - AuthHandlerResult, -} from '../types'; -import { decodeJwt } from 'jose'; -import { prepareBackstageIdentityResponse } from '../prepareBackstageIdentityResponse'; +import { createProxyAuthProviderFactory } from '@backstage/plugin-auth-node'; +import { AuthHandler, SignInResolver } from '../types'; import { createAuthProviderIntegration } from '../createAuthProviderIntegration'; -import { IncomingHttpHeaders } from 'http'; - -// NOTE: This may come in handy if you're doing work on this provider: -// -// plugins/auth-backend/examples/docker-compose.oauth2-proxy.yaml -// - -export const OAUTH2_PROXY_JWT_HEADER = 'X-OAUTH2-PROXY-ID-TOKEN'; - -/** - * JWT header extraction result, containing the raw value and the parsed JWT - * payload. - * - * @public - */ -export type OAuth2ProxyResult = { - /** - * The parsed payload of the `accessToken`. The token is only parsed, not verified. - * - * @deprecated Access through the `headers` instead. This will be removed in a future release. - */ - fullProfile: JWTPayload; - - /** - * The token received via the X-OAUTH2-PROXY-ID-TOKEN header. Will be an empty string - * if the header is not set. Note the this is typically an OpenID Connect token. - * - * @deprecated Access through the `headers` instead. This will be removed in a future release. - */ - accessToken: string; - - /** - * The headers of the incoming request from the OAuth2 proxy. This will include - * both the headers set by the client as well as the ones added by the OAuth2 proxy. - * You should only trust the headers that are injected by the OAuth2 proxy. - * - * Useful headers to use to complete the sign-in are for example `x-forwarded-user` - * and `x-forwarded-email`. See the OAuth2 proxy documentation for more information - * about the available headers and how to enable them. In particular it is possible - * to forward access and identity tokens, which can be user for additional verification - * and lookups. - */ - headers: IncomingHttpHeaders; - - /** - * Provides convenient access to the request headers. - * - * This call is simply forwarded to `req.get(name)`. - */ - getHeader(name: string): string | undefined; -}; - -interface Options { - resolverContext: AuthResolverContext; - signInResolver: SignInResolver>; - authHandler: AuthHandler>; -} - -export class Oauth2ProxyAuthProvider - implements AuthProviderRouteHandlers -{ - private readonly resolverContext: AuthResolverContext; - private readonly signInResolver: SignInResolver< - OAuth2ProxyResult - >; - private readonly authHandler: AuthHandler>; - - constructor(options: Options) { - this.resolverContext = options.resolverContext; - this.signInResolver = options.signInResolver; - this.authHandler = options.authHandler; - } - - frameHandler(): Promise { - return Promise.resolve(undefined); - } - - async refresh(req: express.Request, res: express.Response): Promise { - try { - // TODO(Rugvip): This parsing was deprecated in 1.2 and should be removed in a future release. - const authHeader = req.header(OAUTH2_PROXY_JWT_HEADER); - const jwt = getBearerTokenFromAuthorizationHeader(authHeader); - const decodedJWT = jwt && (decodeJwt(jwt) as unknown as JWTPayload); - - const result = { - fullProfile: decodedJWT || ({} as JWTPayload), - accessToken: jwt || '', - headers: req.headers, - getHeader(name: string) { - if (name.toLocaleLowerCase('en-US') === 'set-cookie') { - throw new Error('Access Set-Cookie via the headers object instead'); - } - return req.get(name); - }, - }; - - const response = await this.handleResult(result); - res.json(response); - } catch (e) { - throw new AuthenticationError('Refresh failed', e); - } - } - - start(): Promise { - return Promise.resolve(undefined); - } - - private async handleResult( - result: OAuth2ProxyResult, - ): Promise> { - const { profile } = await this.authHandler(result, this.resolverContext); - - const backstageSignInResult = await this.signInResolver( - { - result, - profile, - }, - this.resolverContext, - ); - - return { - providerInfo: { - accessToken: result.accessToken, - }, - backstageIdentity: prepareBackstageIdentityResponse( - backstageSignInResult, - ), - profile, - }; - } -} - -async function defaultAuthHandler( - result: OAuth2ProxyResult, -): Promise { - return { - profile: { - email: result.getHeader('x-forwarded-email'), - displayName: - result.getHeader('x-forwarded-preferred-username') || - result.getHeader('x-forwarded-user'), - }, - }; -} +import { + type OAuth2ProxyResult, + oauth2ProxyAuthenticator, +} from '@backstage/plugin-auth-backend-module-oauth2-proxy-provider'; /** * Auth provider integration for oauth2-proxy auth @@ -179,7 +28,7 @@ async function defaultAuthHandler( * @public */ export const oauth2Proxy = createAuthProviderIntegration({ - create(options: { + create(options: { /** * Configure an auth handler to generate a profile for the user. * @@ -187,7 +36,7 @@ export const oauth2Proxy = createAuthProviderIntegration({ * header as the display name, falling back to `X-Forwarded-User`, and the value of * the `X-Forwarded-Email` header as the email address. */ - authHandler?: AuthHandler>; + authHandler?: AuthHandler; /** * Configure sign-in for this provider, without it the provider can not be used to sign users in. @@ -196,17 +45,13 @@ export const oauth2Proxy = createAuthProviderIntegration({ /** * Maps an auth result to a Backstage identity for the user. */ - resolver: SignInResolver>; + resolver: SignInResolver; }; }) { - return ({ resolverContext }) => { - const signInResolver = options.signIn.resolver; - const authHandler = options.authHandler; - return new Oauth2ProxyAuthProvider({ - resolverContext, - signInResolver, - authHandler: authHandler ?? defaultAuthHandler, - }); - }; + return createProxyAuthProviderFactory({ + authenticator: oauth2ProxyAuthenticator, + profileTransform: options?.authHandler, + signInResolver: options?.signIn?.resolver, + }); }, }); diff --git a/plugins/auth-node/CHANGELOG.md b/plugins/auth-node/CHANGELOG.md index d585f8d1d4..8262a10dab 100644 --- a/plugins/auth-node/CHANGELOG.md +++ b/plugins/auth-node/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-auth-node +## 0.4.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.5.0-next.0 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + ## 0.4.2-next.0 ### Patch Changes diff --git a/plugins/auth-node/package.json b/plugins/auth-node/package.json index 5a18593ab6..ceb1a587ce 100644 --- a/plugins/auth-node/package.json +++ b/plugins/auth-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-node", - "version": "0.4.2-next.0", + "version": "0.4.2-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -41,7 +41,7 @@ "jose": "^4.6.0", "lodash": "^4.17.21", "node-fetch": "^2.6.7", - "passport": "^0.6.0", + "passport": "^0.7.0", "winston": "^3.2.1", "zod": "^3.21.4", "zod-to-json-schema": "^3.21.4" diff --git a/plugins/azure-devops-backend/CHANGELOG.md b/plugins/azure-devops-backend/CHANGELOG.md index 30b573b43f..0b4f978662 100644 --- a/plugins/azure-devops-backend/CHANGELOG.md +++ b/plugins/azure-devops-backend/CHANGELOG.md @@ -1,5 +1,26 @@ # @backstage/plugin-azure-devops-backend +## 0.5.0-next.1 + +### Minor Changes + +- 844969cd97: **BREAKING** New `fromConfig` static method must be used now when creating an instance of the `AzureDevOpsApi` + + Added support for using the `AzureDevOpsCredentialsProvider` + +### Patch Changes + +- 043b724c56: Introduced new `AzureDevOpsAnnotatorProcessor` that adds the needed annotations automatically. Also, moved constants to common package so they can be shared more easily +- Updated dependencies + - @backstage/plugin-azure-devops-common@0.3.2-next.0 + - @backstage/integration@1.8.0-next.1 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/plugin-catalog-common@1.0.18 + - @backstage/plugin-catalog-node@1.5.1-next.1 + ## 0.4.5-next.0 ### Patch Changes diff --git a/plugins/azure-devops-backend/README.md b/plugins/azure-devops-backend/README.md index e892b84dd1..6810112bdb 100644 --- a/plugins/azure-devops-backend/README.md +++ b/plugins/azure-devops-backend/README.md @@ -89,6 +89,29 @@ In your `packages/backend/src/index.ts` make the following changes: backend.start(); ``` +## Processor + +The Azure DevOps backend plugin includes the `AzureDevOpsAnnotatorProcessor` which will automatically add the needed annotations for you. Here's how to install it: + +```diff + import { CatalogBuilder } from '@backstage/plugin-catalog-backend'; + import { ScaffolderEntitiesProcessor } from '@backstage/plugin-catalog-backend-module-scaffolder-entity-model'; + import { Router } from 'express'; + import { PluginEnvironment } from '../types'; ++ import { AzureDevOpsAnnotatorProcessor } from '@backstage/plugin-azure-devops-backend'; + + export default async function createPlugin( + env: PluginEnvironment, + ): Promise { + const builder = await CatalogBuilder.create(env); + builder.addProcessor(new ScaffolderEntitiesProcessor()); ++ builder.addProcessor(AzureDevOpsAnnotatorProcessor.fromConfig(env.config)); + const { processingEngine, router } = await builder.build(); + await processingEngine.start(); + return router; + } +``` + ## Links - [Frontend part of the plugin](https://github.com/backstage/backstage/tree/master/plugins/azure-devops) diff --git a/plugins/azure-devops-backend/api-report.md b/plugins/azure-devops-backend/api-report.md index 8c916a1495..ac12f85131 100644 --- a/plugins/azure-devops-backend/api-report.md +++ b/plugins/azure-devops-backend/api-report.md @@ -7,24 +7,53 @@ import { BackendFeature } from '@backstage/backend-plugin-api'; import { Build } from 'azure-devops-node-api/interfaces/BuildInterfaces'; import { BuildDefinitionReference } from 'azure-devops-node-api/interfaces/BuildInterfaces'; import { BuildRun } from '@backstage/plugin-azure-devops-common'; +import { CatalogProcessor } from '@backstage/plugin-catalog-node'; import { Config } from '@backstage/config'; import { DashboardPullRequest } from '@backstage/plugin-azure-devops-common'; +import { Entity } from '@backstage/catalog-model'; import express from 'express'; import { GitRepository } from 'azure-devops-node-api/interfaces/GitInterfaces'; import { GitTag } from '@backstage/plugin-azure-devops-common'; +import { LocationSpec } from '@backstage/plugin-catalog-common'; import { Logger } from 'winston'; import { Project } from '@backstage/plugin-azure-devops-common'; import { PullRequest } from '@backstage/plugin-azure-devops-common'; import { PullRequestOptions } from '@backstage/plugin-azure-devops-common'; import { RepoBuild } from '@backstage/plugin-azure-devops-common'; +import { ScmIntegrationRegistry } from '@backstage/integration'; import { Team } from '@backstage/plugin-azure-devops-common'; import { TeamMember } from '@backstage/plugin-azure-devops-common'; import { UrlReader } from '@backstage/backend-common'; -import { WebApi } from 'azure-devops-node-api'; + +// @public (undocumented) +export class AzureDevOpsAnnotatorProcessor implements CatalogProcessor { + constructor(opts: { + scmIntegrationRegistry: ScmIntegrationRegistry; + kinds?: string[]; + }); + // (undocumented) + static fromConfig( + config: Config, + options?: { + kinds?: string[]; + }, + ): AzureDevOpsAnnotatorProcessor; + // (undocumented) + getProcessorName(): string; + // (undocumented) + preProcessEntity(entity: Entity, location: LocationSpec): Promise; +} // @public (undocumented) export class AzureDevOpsApi { - constructor(logger: Logger, webApi: WebApi, urlReader: UrlReader); + // (undocumented) + static fromConfig( + config: Config, + options: { + logger: Logger; + urlReader: UrlReader; + }, + ): AzureDevOpsApi; // (undocumented) getAllTeams(): Promise; // (undocumented) diff --git a/plugins/azure-devops-backend/config.d.ts b/plugins/azure-devops-backend/config.d.ts index 433e4df22f..86fefeeef9 100644 --- a/plugins/azure-devops-backend/config.d.ts +++ b/plugins/azure-devops-backend/config.d.ts @@ -15,7 +15,9 @@ */ export interface Config { - /** Configuration options for the azure-devops-backend plugin */ + /** + * Configuration options for the azure-devops-backend plugin + */ azureDevOps: { /** * The hostname of the given Azure instance diff --git a/plugins/azure-devops-backend/package.json b/plugins/azure-devops-backend/package.json index 5bf6b9b39f..6b223a26b2 100644 --- a/plugins/azure-devops-backend/package.json +++ b/plugins/azure-devops-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-azure-devops-backend", - "version": "0.4.5-next.0", + "version": "0.5.0-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -30,12 +30,17 @@ "dependencies": { "@backstage/backend-common": "workspace:^", "@backstage/backend-plugin-api": "workspace:^", + "@backstage/catalog-model": "workspace:^", "@backstage/config": "workspace:^", + "@backstage/integration": "workspace:^", "@backstage/plugin-azure-devops-common": "workspace:^", + "@backstage/plugin-catalog-common": "workspace:^", + "@backstage/plugin-catalog-node": "workspace:^", "@types/express": "^4.17.6", "azure-devops-node-api": "^11.0.1", "express": "^4.17.1", "express-promise-router": "^4.1.0", + "lodash": "^4.17.21", "mime-types": "^2.1.27", "p-limit": "^3.1.0", "winston": "^3.2.1", diff --git a/plugins/azure-devops-backend/src/api/AzureDevOpsApi.test.ts b/plugins/azure-devops-backend/src/api/AzureDevOpsApi.test.ts index dade60f1ed..174c5ff6cd 100644 --- a/plugins/azure-devops-backend/src/api/AzureDevOpsApi.test.ts +++ b/plugins/azure-devops-backend/src/api/AzureDevOpsApi.test.ts @@ -1,5 +1,5 @@ /* - * Copyright 2021 The Backstage Authors + * Copyright 2023 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,560 +14,751 @@ * limitations under the License. */ +jest.mock('azure-devops-node-api', () => ({ + WebApi: jest.fn(), + getHandlerFromToken: jest.fn().mockReturnValue(() => {}), +})); + +import { UrlReader, getVoidLogger } from '@backstage/backend-common'; +import { ConfigReader } from '@backstage/config'; +import { AzureDevOpsApi } from './AzureDevOpsApi'; +import { WebApi } from 'azure-devops-node-api'; +import { TeamProjectReference } from 'azure-devops-node-api/interfaces/CoreInterfaces'; +import { GitRepository } from 'azure-devops-node-api/interfaces/TfvcInterfaces'; import { Build, - DefinitionReference, -} from 'azure-devops-node-api/interfaces/BuildInterfaces'; -import { BuildResult, BuildStatus, - GitTag, - PullRequest, - PullRequestStatus, - RepoBuild, -} from '@backstage/plugin-azure-devops-common'; +} from 'azure-devops-node-api/interfaces/BuildInterfaces'; import { GitPullRequest, GitRef, - GitRepository, + PullRequestStatus, } from 'azure-devops-node-api/interfaces/GitInterfaces'; -import { - mappedBuildRun, - mappedGitTag, - mappedPullRequest, - mappedRepoBuild, -} from './AzureDevOpsApi'; - -import { IdentityRef } from 'azure-devops-node-api/interfaces/common/VSSInterfaces'; +import { PullRequestOptions } from '@backstage/plugin-azure-devops-common'; describe('AzureDevOpsApi', () => { - describe('mappedRepoBuild', () => { - describe('mappedRepoBuild happy path', () => { - it('should return RepoBuild from Build', () => { - const inputBuildDefinition: DefinitionReference = { - name: 'My Build Definition', - }; - - const inputLinks: any = { - web: { - href: 'https://host.com/myOrg/0bcc0c0d-2d02/_build/results?buildId=1', - }, - }; - - const inputIdentityRef: IdentityRef = { - displayName: 'Jane Doe', - uniqueName: 'DOMAIN\\jdoe', - }; - - const inputBuild: Build = { - id: 1, - buildNumber: 'Build-1', - status: BuildStatus.Completed, - result: BuildResult.Succeeded, - queueTime: new Date('2020-09-12T06:10:23.932Z'), - startTime: new Date('2020-09-12T06:15:23.932Z'), - finishTime: new Date('2020-09-12T06:20:23.932Z'), - sourceBranch: 'refs/heads/develop', - sourceVersion: 'f4f78b3100b2923982bdf60c89c57ce6fd2d9a1c', - definition: inputBuildDefinition, - _links: inputLinks, - requestedFor: inputIdentityRef, - }; - - const outputRepoBuild: RepoBuild = { - id: 1, - title: 'My Build Definition - Build-1', - link: 'https://host.com/myOrg/0bcc0c0d-2d02/_build/results?buildId=1', - status: BuildStatus.Completed, - result: BuildResult.Succeeded, - queueTime: '2020-09-12T06:10:23.932Z', - startTime: '2020-09-12T06:15:23.932Z', - finishTime: '2020-09-12T06:20:23.932Z', - source: 'refs/heads/develop (f4f78b31)', - uniqueName: 'DOMAIN\\jdoe', - }; - - expect(mappedRepoBuild(inputBuild)).toEqual(outputRepoBuild); - }); - }); - - describe('mappedRepoBuild with no Build definition name', () => { - it('should return RepoBuild with only Build Number for title', () => { - const inputLinks: any = { - web: { - href: 'https://host.com/myOrg/0bcc0c0d-2d02/_build/results?buildId=1', - }, - }; - - const inputIdentityRef: IdentityRef = { - displayName: 'Jane Doe', - uniqueName: 'DOMAIN\\jdoe', - }; - - const inputBuild: Build = { - id: 1, - buildNumber: 'Build-1', - status: BuildStatus.Completed, - result: BuildResult.Succeeded, - queueTime: new Date('2020-09-12T06:10:23.932Z'), - startTime: new Date('2020-09-12T06:15:23.932Z'), - finishTime: new Date('2020-09-12T06:20:23.932Z'), - sourceBranch: 'refs/heads/develop', - sourceVersion: 'f4f78b3100b2923982bdf60c89c57ce6fd2d9a1c', - definition: undefined, - _links: inputLinks, - requestedFor: inputIdentityRef, - }; - - const outputRepoBuild: RepoBuild = { - id: 1, - title: 'Build-1', - link: 'https://host.com/myOrg/0bcc0c0d-2d02/_build/results?buildId=1', - status: BuildStatus.Completed, - result: BuildResult.Succeeded, - queueTime: '2020-09-12T06:10:23.932Z', - startTime: '2020-09-12T06:15:23.932Z', - finishTime: '2020-09-12T06:20:23.932Z', - source: 'refs/heads/develop (f4f78b31)', - uniqueName: 'DOMAIN\\jdoe', - }; - - expect(mappedRepoBuild(inputBuild)).toEqual(outputRepoBuild); - }); - }); - - describe('mappedRepoBuild with undefined status', () => { - it('should return BuildStatus of None for status', () => { - const inputLinks: any = { - web: { - href: 'https://host.com/myOrg/0bcc0c0d-2d02/_build/results?buildId=1', - }, - }; - - const inputIdentityRef: IdentityRef = { - displayName: 'Jane Doe', - uniqueName: 'DOMAIN\\jdoe', - }; - - const inputBuild: Build = { - id: 1, - buildNumber: 'Build-1', - status: undefined, - result: BuildResult.Succeeded, - queueTime: new Date('2020-09-12T06:10:23.932Z'), - startTime: new Date('2020-09-12T06:15:23.932Z'), - finishTime: new Date('2020-09-12T06:20:23.932Z'), - sourceBranch: 'refs/heads/develop', - sourceVersion: 'f4f78b3100b2923982bdf60c89c57ce6fd2d9a1c', - definition: undefined, - _links: inputLinks, - requestedFor: inputIdentityRef, - }; - - const outputRepoBuild: RepoBuild = { - id: 1, - title: 'Build-1', - link: 'https://host.com/myOrg/0bcc0c0d-2d02/_build/results?buildId=1', - status: BuildStatus.None, - result: BuildResult.Succeeded, - queueTime: '2020-09-12T06:10:23.932Z', - startTime: '2020-09-12T06:15:23.932Z', - finishTime: '2020-09-12T06:20:23.932Z', - source: 'refs/heads/develop (f4f78b31)', - uniqueName: 'DOMAIN\\jdoe', - }; - - expect(mappedRepoBuild(inputBuild)).toEqual(outputRepoBuild); - }); - }); - - describe('mappedRepoBuild with undefined result', () => { - it('should return BuildResult of None for result', () => { - const inputLinks: any = { - web: { - href: 'https://host.com/myOrg/0bcc0c0d-2d02/_build/results?buildId=1', - }, - }; - - const inputIdentityRef: IdentityRef = { - displayName: 'Jane Doe', - uniqueName: 'DOMAIN\\jdoe', - }; - - const inputBuild: Build = { - id: 1, - buildNumber: 'Build-1', - status: BuildStatus.InProgress, - result: undefined, - queueTime: new Date('2020-09-12T06:10:23.932Z'), - startTime: new Date('2020-09-12T06:15:23.932Z'), - finishTime: new Date('2020-09-12T06:20:23.932Z'), - sourceBranch: 'refs/heads/develop', - sourceVersion: 'f4f78b3100b2923982bdf60c89c57ce6fd2d9a1c', - definition: undefined, - _links: inputLinks, - requestedFor: inputIdentityRef, - }; - - const outputRepoBuild: RepoBuild = { - id: 1, - title: 'Build-1', - link: 'https://host.com/myOrg/0bcc0c0d-2d02/_build/results?buildId=1', - status: BuildStatus.InProgress, - result: BuildResult.None, - queueTime: '2020-09-12T06:10:23.932Z', - startTime: '2020-09-12T06:15:23.932Z', - finishTime: '2020-09-12T06:20:23.932Z', - source: 'refs/heads/develop (f4f78b31)', - uniqueName: 'DOMAIN\\jdoe', - }; - - expect(mappedRepoBuild(inputBuild)).toEqual(outputRepoBuild); - }); - }); - - describe('mappedRepoBuild with undefined link', () => { - it('should return empty string for link', () => { - const inputIdentityRef: IdentityRef = { - displayName: 'Jane Doe', - uniqueName: 'DOMAIN\\jdoe', - }; - - const inputBuild: Build = { - id: 1, - buildNumber: 'Build-1', - status: BuildStatus.InProgress, - result: undefined, - queueTime: new Date('2020-09-12T06:10:23.932Z'), - startTime: new Date('2020-09-12T06:15:23.932Z'), - finishTime: new Date('2020-09-12T06:20:23.932Z'), - sourceBranch: 'refs/heads/develop', - sourceVersion: 'f4f78b3100b2923982bdf60c89c57ce6fd2d9a1c', - definition: undefined, - _links: undefined, - requestedFor: inputIdentityRef, - }; - - const outputRepoBuild: RepoBuild = { - id: 1, - title: 'Build-1', - link: '', - status: BuildStatus.InProgress, - result: BuildResult.None, - queueTime: '2020-09-12T06:10:23.932Z', - startTime: '2020-09-12T06:15:23.932Z', - finishTime: '2020-09-12T06:20:23.932Z', - source: 'refs/heads/develop (f4f78b31)', - uniqueName: 'DOMAIN\\jdoe', - }; - - expect(mappedRepoBuild(inputBuild)).toEqual(outputRepoBuild); - }); - }); + beforeEach(() => { + jest.clearAllMocks(); }); - describe('mappedGitTag', () => { - describe('mappedGitTag happy path', () => { - it('should return GitTag from GitRef', () => { - const inputIdentityRef: IdentityRef = { - displayName: 'Jane Doe', - }; - const inputGitRef: GitRef = { - name: 'refs/tags/v1.1.2', - creator: inputIdentityRef, - objectId: '1111aaaa2222bbbb3333cccc4444dddd5555eeee', - peeledObjectId: '1234567890abcdef1234567890abcdef12345678', - }; - const inputLinkBaseUrl = - 'https://host.com/myOrg/_git/super-feature-repo?version=GT'; - const inputCommitBaseUrl = - 'https://host.com/myOrg/_git/super-feature-repo/commit'; - const outputGitTag: GitTag = { - name: 'v1.1.2', - createdBy: 'Jane Doe', - commitLink: - 'https://host.com/myOrg/_git/super-feature-repo/commit/1234567890abcdef1234567890abcdef12345678', - objectId: '1111aaaa2222bbbb3333cccc4444dddd5555eeee', - peeledObjectId: '1234567890abcdef1234567890abcdef12345678', - link: 'https://host.com/myOrg/_git/super-feature-repo?version=GTv1.1.2', - }; - expect( - mappedGitTag(inputGitRef, inputLinkBaseUrl, inputCommitBaseUrl), - ).toEqual(outputGitTag); - }); - }); + const mockConfig = new ConfigReader({ + azureDevOps: { + host: 'dev.azure.com', + token: 'token', + organization: 'org', + }, + integrations: { + azure: [ + { + host: 'dev.azure.com', + credentials: [ + { + personalAccessToken: 'pat', + }, + ], + }, + ], + }, }); - describe('mappedPullRequest', () => { - describe('mappedPullRequest happy path', () => { - it('should return PullRequest from GitPullRequest', () => { - const inputGitRepository: GitRepository = { - name: 'super-feature-repo', - }; + const mockLogger = getVoidLogger(); - const inputIdentityRef: IdentityRef = { - displayName: 'Jane Doe', - uniqueName: 'DOMAIN\\jdoe', - }; + const mockUrlReader: UrlReader = { + readUrl: url => + Promise.resolve({ + buffer: async () => Buffer.from(url), + etag: 'buffer', + stream: jest.fn(), + }), + readTree: jest.fn(), + search: jest.fn(), + }; - const inputPullRequest: GitPullRequest = { - pullRequestId: 7181, - repository: inputGitRepository, - title: 'My Awesome New Feature', - createdBy: inputIdentityRef, - creationDate: new Date('2020-09-12T06:10:23.932Z'), - sourceRefName: 'refs/heads/topic/super-awesome-feature', - targetRefName: 'refs/heads/main', - status: PullRequestStatus.Active, - isDraft: false, - }; + it('should get projects', async () => { + const mockProjects: TeamProjectReference[] = [ + { + id: 'one', + name: 'one', + description: 'one', + }, + { + id: 'two', + name: 'two', + description: 'two', + }, + { + id: 'three', + name: 'three', + description: 'three', + }, + ]; - const inputBaseUrl = - 'https://host.com/myOrg/_git/super-feature-repo/pullrequest'; + const mockCoreApiClient = { + getProjects: jest.fn().mockResolvedValue(mockProjects), + }; - const outputPullRequest: PullRequest = { - pullRequestId: 7181, - repoName: 'super-feature-repo', - title: 'My Awesome New Feature', - uniqueName: 'DOMAIN\\jdoe', - createdBy: 'Jane Doe', - creationDate: '2020-09-12T06:10:23.932Z', - sourceRefName: 'refs/heads/topic/super-awesome-feature', - targetRefName: 'refs/heads/main', - status: PullRequestStatus.Active, - isDraft: false, - link: 'https://host.com/myOrg/_git/super-feature-repo/pullrequest/7181', - }; + const mockCoreApi = { + getCoreApi: jest.fn().mockResolvedValue(mockCoreApiClient), + }; - expect(mappedPullRequest(inputPullRequest, inputBaseUrl)).toEqual( - outputPullRequest, - ); - }); + (WebApi as unknown as jest.Mock).mockImplementation(() => mockCoreApi); + + const api = AzureDevOpsApi.fromConfig(mockConfig, { + logger: mockLogger, + urlReader: mockUrlReader, }); + + const result = await api.getProjects(); + + expect(result).toEqual([ + { + id: 'one', + name: 'one', + description: 'one', + }, + { + id: 'three', + name: 'three', + description: 'three', + }, + { + id: 'two', + name: 'two', + description: 'two', + }, + ]); }); - describe('mappedBuildRun', () => { - describe('mappedBuildRun happy path', () => { - it('should return RepoBuild from Build', () => { - const inputBuildDefinition: DefinitionReference = { - name: 'My Build Definition', - }; + it('should get git repository', async () => { + const mockGitRepository: GitRepository = { + id: 'repo', + }; - const inputLinks: any = { - web: { - href: 'https://host.com/myOrg/0bcc0c0d-2d02/_build/results?buildId=1', - }, - }; + const mockGitClient = { + getRepository: jest.fn().mockResolvedValue(mockGitRepository), + }; + const mockGitApi = { + getGitApi: jest.fn().mockReturnValue(mockGitClient), + }; - const inputIdentityRef: IdentityRef = { - displayName: 'Jane Doe', - uniqueName: 'DOMAIN\\jdoe', - }; + (WebApi as unknown as jest.Mock).mockImplementation(() => mockGitApi); - const inputBuild: Build = { - id: 1, - buildNumber: 'Build-1', - status: BuildStatus.Completed, - result: BuildResult.Succeeded, - queueTime: new Date('2020-09-12T06:10:23.932Z'), - startTime: new Date('2020-09-12T06:15:23.932Z'), - finishTime: new Date('2020-09-12T06:20:23.932Z'), - sourceBranch: 'refs/heads/develop', - sourceVersion: 'f4f78b3100b2923982bdf60c89c57ce6fd2d9a1c', - definition: inputBuildDefinition, - _links: inputLinks, - requestedFor: inputIdentityRef, - }; - - const outputRepoBuild: RepoBuild = { - id: 1, - title: 'My Build Definition - Build-1', - link: 'https://host.com/myOrg/0bcc0c0d-2d02/_build/results?buildId=1', - status: BuildStatus.Completed, - result: BuildResult.Succeeded, - queueTime: '2020-09-12T06:10:23.932Z', - startTime: '2020-09-12T06:15:23.932Z', - finishTime: '2020-09-12T06:20:23.932Z', - source: 'refs/heads/develop (f4f78b31)', - uniqueName: 'DOMAIN\\jdoe', - }; - - expect(mappedBuildRun(inputBuild)).toEqual(outputRepoBuild); - }); + const api = AzureDevOpsApi.fromConfig(mockConfig, { + logger: mockLogger, + urlReader: mockUrlReader, }); - describe('mappedBuildRun with no Build definition name', () => { - it('should return RepoBuild with only Build Number for title', () => { - const inputLinks: any = { - web: { - href: 'https://host.com/myOrg/0bcc0c0d-2d02/_build/results?buildId=1', - }, - }; + const result = await api.getGitRepository('project', 'repo'); - const inputIdentityRef: IdentityRef = { - displayName: 'Jane Doe', - uniqueName: 'DOMAIN\\jdoe', - }; + expect(result).toEqual({ id: 'repo' }); + }); - const inputBuild: Build = { - id: 1, - buildNumber: 'Build-1', - status: BuildStatus.Completed, - result: BuildResult.Succeeded, - queueTime: new Date('2020-09-12T06:10:23.932Z'), - startTime: new Date('2020-09-12T06:15:23.932Z'), - finishTime: new Date('2020-09-12T06:20:23.932Z'), - sourceBranch: 'refs/heads/develop', - sourceVersion: 'f4f78b3100b2923982bdf60c89c57ce6fd2d9a1c', - definition: undefined, - _links: inputLinks, - requestedFor: inputIdentityRef, - }; + it('should get build list', async () => { + const mockBuilds: Build[] = [ + { + id: 1, + }, + { + id: 2, + }, + { + id: 3, + }, + ]; - const outputRepoBuild: RepoBuild = { - id: 1, - title: 'Build-1', - link: 'https://host.com/myOrg/0bcc0c0d-2d02/_build/results?buildId=1', - status: BuildStatus.Completed, - result: BuildResult.Succeeded, - queueTime: '2020-09-12T06:10:23.932Z', - startTime: '2020-09-12T06:15:23.932Z', - finishTime: '2020-09-12T06:20:23.932Z', - source: 'refs/heads/develop (f4f78b31)', - uniqueName: 'DOMAIN\\jdoe', - }; + const mockBuildClient = { + getBuilds: jest.fn().mockResolvedValue(mockBuilds), + }; + const mockBuildApi = { + getBuildApi: jest.fn().mockReturnValue(mockBuildClient), + }; - expect(mappedBuildRun(inputBuild)).toEqual(outputRepoBuild); - }); + (WebApi as unknown as jest.Mock).mockImplementation(() => mockBuildApi); + + const api = AzureDevOpsApi.fromConfig(mockConfig, { + logger: mockLogger, + urlReader: mockUrlReader, }); - describe('mappedBuildRun with undefined status', () => { - it('should return BuildStatus of None for status', () => { - const inputLinks: any = { - web: { - href: 'https://host.com/myOrg/0bcc0c0d-2d02/_build/results?buildId=1', - }, - }; + const result = await api.getBuildList('project', 'repo', 10); - const inputIdentityRef: IdentityRef = { - displayName: 'Jane Doe', - uniqueName: 'DOMAIN\\jdoe', - }; + expect(result).toEqual([{ id: 1 }, { id: 2 }, { id: 3 }]); + }); - const inputBuild: Build = { - id: 1, - buildNumber: 'Build-1', - status: undefined, - result: BuildResult.Succeeded, - queueTime: new Date('2020-09-12T06:10:23.932Z'), - startTime: new Date('2020-09-12T06:15:23.932Z'), - finishTime: new Date('2020-09-12T06:20:23.932Z'), - sourceBranch: 'refs/heads/develop', - sourceVersion: 'f4f78b3100b2923982bdf60c89c57ce6fd2d9a1c', - definition: undefined, - _links: inputLinks, - requestedFor: inputIdentityRef, - }; + it('should get repo builds', async () => { + const mockGitRepository: GitRepository = { + id: 'repo', + }; - const outputRepoBuild: RepoBuild = { - id: 1, - title: 'Build-1', - link: 'https://host.com/myOrg/0bcc0c0d-2d02/_build/results?buildId=1', - status: BuildStatus.None, - result: BuildResult.Succeeded, - queueTime: '2020-09-12T06:10:23.932Z', - startTime: '2020-09-12T06:15:23.932Z', - finishTime: '2020-09-12T06:20:23.932Z', - source: 'refs/heads/develop (f4f78b31)', - uniqueName: 'DOMAIN\\jdoe', - }; + const mockBuilds: Build[] = [ + { + id: 1, + buildNumber: 'Build-1', + status: BuildStatus.Completed, + result: BuildResult.Succeeded, + queueTime: new Date('2020-09-12T06:10:23.932Z'), + startTime: new Date('2020-09-12T06:15:23.932Z'), + finishTime: new Date('2020-09-12T06:20:23.932Z'), + sourceBranch: 'main', + sourceVersion: 'abcd', + }, + { + id: 2, + buildNumber: 'Build-2', + status: BuildStatus.Completed, + result: BuildResult.Succeeded, + queueTime: new Date('2020-09-12T06:10:23.932Z'), + startTime: new Date('2020-09-12T06:15:23.932Z'), + finishTime: new Date('2020-09-12T06:20:23.932Z'), + sourceBranch: 'main', + sourceVersion: 'abcd', + }, + { + id: 3, + buildNumber: 'Build-3', + status: BuildStatus.Completed, + result: BuildResult.Succeeded, + queueTime: new Date('2020-09-12T06:10:23.932Z'), + startTime: new Date('2020-09-12T06:15:23.932Z'), + finishTime: new Date('2020-09-12T06:20:23.932Z'), + sourceBranch: 'main', + sourceVersion: 'abcd', + }, + ]; - expect(mappedBuildRun(inputBuild)).toEqual(outputRepoBuild); - }); + const mockBuildClient = { + getBuilds: jest.fn().mockResolvedValue(mockBuilds), + }; + const mockGitClient = { + getRepository: jest.fn().mockResolvedValue(mockGitRepository), + }; + const mockApi = { + getGitApi: jest.fn().mockReturnValue(mockGitClient), + getBuildApi: jest.fn().mockReturnValue(mockBuildClient), + }; + + (WebApi as unknown as jest.Mock).mockImplementation(() => mockApi); + + const api = AzureDevOpsApi.fromConfig(mockConfig, { + logger: mockLogger, + urlReader: mockUrlReader, }); - describe('mappedBuildRun with undefined result', () => { - it('should return BuildResult of None for result', () => { - const inputLinks: any = { - web: { - href: 'https://host.com/myOrg/0bcc0c0d-2d02/_build/results?buildId=1', - }, - }; + const result = await api.getRepoBuilds('project', 'repo', 10); - const inputIdentityRef: IdentityRef = { - displayName: 'Jane Doe', - uniqueName: 'DOMAIN\\jdoe', - }; + expect(result).toEqual([ + { + id: 1, + title: 'Build-1', + link: '', + status: BuildStatus.Completed, + result: BuildResult.Succeeded, + queueTime: '2020-09-12T06:10:23.932Z', + startTime: '2020-09-12T06:15:23.932Z', + finishTime: '2020-09-12T06:20:23.932Z', + source: 'main (abcd)', + uniqueName: 'N/A', + }, + { + id: 2, + title: 'Build-2', + link: '', + status: BuildStatus.Completed, + result: BuildResult.Succeeded, + queueTime: '2020-09-12T06:10:23.932Z', + startTime: '2020-09-12T06:15:23.932Z', + finishTime: '2020-09-12T06:20:23.932Z', + source: 'main (abcd)', + uniqueName: 'N/A', + }, + { + id: 3, + title: 'Build-3', + link: '', + status: BuildStatus.Completed, + result: BuildResult.Succeeded, + queueTime: '2020-09-12T06:10:23.932Z', + startTime: '2020-09-12T06:15:23.932Z', + finishTime: '2020-09-12T06:20:23.932Z', + source: 'main (abcd)', + uniqueName: 'N/A', + }, + ]); + }); - const inputBuild: Build = { - id: 1, - buildNumber: 'Build-1', - status: BuildStatus.InProgress, - result: undefined, - queueTime: new Date('2020-09-12T06:10:23.932Z'), - startTime: new Date('2020-09-12T06:15:23.932Z'), - finishTime: new Date('2020-09-12T06:20:23.932Z'), - sourceBranch: 'refs/heads/develop', - sourceVersion: 'f4f78b3100b2923982bdf60c89c57ce6fd2d9a1c', - definition: undefined, - _links: inputLinks, - requestedFor: inputIdentityRef, - }; + it('should get git tags', async () => { + const mockGitRepository: GitRepository = { + id: 'repo', + }; - const outputRepoBuild: RepoBuild = { - id: 1, - title: 'Build-1', - link: 'https://host.com/myOrg/0bcc0c0d-2d02/_build/results?buildId=1', - status: BuildStatus.InProgress, - result: BuildResult.None, - queueTime: '2020-09-12T06:10:23.932Z', - startTime: '2020-09-12T06:15:23.932Z', - finishTime: '2020-09-12T06:20:23.932Z', - source: 'refs/heads/develop (f4f78b31)', - uniqueName: 'DOMAIN\\jdoe', - }; + const mockGitRefs: GitRef[] = [ + { + objectId: '1.0', + peeledObjectId: '1.0', + name: 'v1.0', + }, + { + objectId: '2.0', + peeledObjectId: '2.0', + name: 'v2.0', + }, + { + objectId: '3.0', + peeledObjectId: '3.0', + name: 'v3.0', + }, + ]; - expect(mappedBuildRun(inputBuild)).toEqual(outputRepoBuild); - }); + const mockGitClient = { + getRepository: jest.fn().mockResolvedValue(mockGitRepository), + getRefs: jest.fn().mockResolvedValue(mockGitRefs), + }; + const mockApi = { + getGitApi: jest.fn().mockReturnValue(mockGitClient), + serverUrl: 'serverUrl', + }; + + (WebApi as unknown as jest.Mock).mockImplementation(() => mockApi); + + const api = AzureDevOpsApi.fromConfig(mockConfig, { + logger: mockLogger, + urlReader: mockUrlReader, }); - describe('mappedBuildRun with undefined link', () => { - it('should return empty string for link', () => { - const inputIdentityRef: IdentityRef = { - displayName: 'Jane Doe', - uniqueName: 'DOMAIN\\jdoe', - }; + const result = await api.getGitTags('project', 'repo'); - const inputBuild: Build = { - id: 1, - buildNumber: 'Build-1', - status: BuildStatus.InProgress, - result: undefined, - queueTime: new Date('2020-09-12T06:10:23.932Z'), - startTime: new Date('2020-09-12T06:15:23.932Z'), - finishTime: new Date('2020-09-12T06:20:23.932Z'), - sourceBranch: 'refs/heads/develop', - sourceVersion: 'f4f78b3100b2923982bdf60c89c57ce6fd2d9a1c', - definition: undefined, - _links: undefined, - requestedFor: inputIdentityRef, - }; + expect(result).toEqual([ + { + objectId: '1.0', + peeledObjectId: '1.0', + name: 'v1.0', + commitLink: 'serverUrl/project/_git/repo/commit/1.0', + createdBy: 'N/A', + link: 'serverUrl/project/_git/repo?version=GTv1.0', + }, + { + objectId: '2.0', + peeledObjectId: '2.0', + name: 'v2.0', + commitLink: 'serverUrl/project/_git/repo/commit/2.0', + createdBy: 'N/A', + link: 'serverUrl/project/_git/repo?version=GTv2.0', + }, + { + objectId: '3.0', + peeledObjectId: '3.0', + name: 'v3.0', + commitLink: 'serverUrl/project/_git/repo/commit/3.0', + createdBy: 'N/A', + link: 'serverUrl/project/_git/repo?version=GTv3.0', + }, + ]); + }); - const outputRepoBuild: RepoBuild = { - id: 1, - title: 'Build-1', - link: '', - status: BuildStatus.InProgress, - result: BuildResult.None, - queueTime: '2020-09-12T06:10:23.932Z', - startTime: '2020-09-12T06:15:23.932Z', - finishTime: '2020-09-12T06:20:23.932Z', - source: 'refs/heads/develop (f4f78b31)', - uniqueName: 'DOMAIN\\jdoe', - }; + it('should get pull requests', async () => { + const mockGitRepository: GitRepository = { + id: 'repo', + name: 'repo', + }; - expect(mappedBuildRun(inputBuild)).toEqual(outputRepoBuild); - }); + const mockGitPullRequests: GitPullRequest[] = [ + { + pullRequestId: 1, + repository: mockGitRepository, + title: 'PR1', + creationDate: new Date('2020-09-12T06:10:23.932Z'), + sourceRefName: 'refs/heads/topic/pr1', + targetRefName: 'refs/heads/main', + status: PullRequestStatus.Active, + isDraft: false, + }, + { + pullRequestId: 2, + repository: mockGitRepository, + title: 'PR2', + creationDate: new Date('2020-09-12T06:10:23.932Z'), + sourceRefName: 'refs/heads/topic/pr2', + targetRefName: 'refs/heads/main', + status: PullRequestStatus.Active, + isDraft: false, + }, + { + pullRequestId: 3, + repository: mockGitRepository, + title: 'PR3', + creationDate: new Date('2020-09-12T06:10:23.932Z'), + sourceRefName: 'refs/heads/topic/pr3', + targetRefName: 'refs/heads/main', + status: PullRequestStatus.Active, + isDraft: false, + }, + ]; + + const mockGitClient = { + getRepository: jest.fn().mockResolvedValue(mockGitRepository), + getPullRequests: jest.fn().mockResolvedValue(mockGitPullRequests), + }; + const mockApi = { + getGitApi: jest.fn().mockReturnValue(mockGitClient), + serverUrl: 'serverUrl', + }; + + (WebApi as unknown as jest.Mock).mockImplementation(() => mockApi); + + const api = AzureDevOpsApi.fromConfig(mockConfig, { + logger: mockLogger, + urlReader: mockUrlReader, }); + + const pullRequestOptions: PullRequestOptions = { + top: 10, + status: PullRequestStatus.Active, + }; + + const result = await api.getPullRequests( + 'project', + 'repo', + pullRequestOptions, + ); + + expect(result).toEqual([ + { + pullRequestId: 1, + repoName: 'repo', + title: 'PR1', + uniqueName: 'N/A', + createdBy: 'N/A', + creationDate: '2020-09-12T06:10:23.932Z', + sourceRefName: 'refs/heads/topic/pr1', + targetRefName: 'refs/heads/main', + status: PullRequestStatus.Active, + isDraft: false, + link: 'serverUrl/project/_git/repo/pullrequest/1', + }, + { + pullRequestId: 2, + repoName: 'repo', + title: 'PR2', + uniqueName: 'N/A', + createdBy: 'N/A', + creationDate: '2020-09-12T06:10:23.932Z', + sourceRefName: 'refs/heads/topic/pr2', + targetRefName: 'refs/heads/main', + status: PullRequestStatus.Active, + isDraft: false, + link: 'serverUrl/project/_git/repo/pullrequest/2', + }, + { + pullRequestId: 3, + repoName: 'repo', + title: 'PR3', + uniqueName: 'N/A', + createdBy: 'N/A', + creationDate: '2020-09-12T06:10:23.932Z', + sourceRefName: 'refs/heads/topic/pr3', + targetRefName: 'refs/heads/main', + status: PullRequestStatus.Active, + isDraft: false, + link: 'serverUrl/project/_git/repo/pullrequest/3', + }, + ]); + }); + + it('should get build definitions', async () => { + const mockBuilds: Build[] = [ + { + id: 1, + }, + { + id: 2, + }, + { + id: 3, + }, + ]; + + const mockBuildClient = { + getDefinitions: jest.fn().mockResolvedValue(mockBuilds), + }; + const mockBuildApi = { + getBuildApi: jest.fn().mockReturnValue(mockBuildClient), + }; + + (WebApi as unknown as jest.Mock).mockImplementation(() => mockBuildApi); + + const api = AzureDevOpsApi.fromConfig(mockConfig, { + logger: mockLogger, + urlReader: mockUrlReader, + }); + + const result = await api.getBuildDefinitions('project', 'definition'); + + expect(result).toEqual([{ id: 1 }, { id: 2 }, { id: 3 }]); + }); + + it('should get build builds with repoId', async () => { + const mockBuilds: Build[] = [ + { + id: 1, + }, + { + id: 2, + }, + { + id: 3, + }, + ]; + + const mockBuildClient = { + getBuilds: jest.fn().mockResolvedValue(mockBuilds), + }; + const mockBuildApi = { + getBuildApi: jest.fn().mockReturnValue(mockBuildClient), + }; + + (WebApi as unknown as jest.Mock).mockImplementation(() => mockBuildApi); + + const api = AzureDevOpsApi.fromConfig(mockConfig, { + logger: mockLogger, + urlReader: mockUrlReader, + }); + + const result = await api.getBuilds('project', 10, 'repo', undefined); + + expect(result).toEqual([{ id: 1 }, { id: 2 }, { id: 3 }]); + }); + + it('should get build builds with definitions', async () => { + const mockBuilds: Build[] = [ + { + id: 1, + }, + { + id: 2, + }, + { + id: 3, + }, + ]; + + const mockBuildClient = { + getBuilds: jest.fn().mockResolvedValue(mockBuilds), + }; + const mockBuildApi = { + getBuildApi: jest.fn().mockReturnValue(mockBuildClient), + }; + + (WebApi as unknown as jest.Mock).mockImplementation(() => mockBuildApi); + + const api = AzureDevOpsApi.fromConfig(mockConfig, { + logger: mockLogger, + urlReader: mockUrlReader, + }); + + const result = await api.getBuilds('project', 10, undefined, [1, 2, 3]); + + expect(result).toEqual([{ id: 1 }, { id: 2 }, { id: 3 }]); + }); + + it('should get build runs with repoName', async () => { + const mockGitRepository: GitRepository = { + id: 'repo', + }; + + const mockBuilds: Build[] = [ + { + id: 1, + buildNumber: 'Build-1', + status: BuildStatus.Completed, + result: BuildResult.Succeeded, + queueTime: new Date('2020-09-12T06:10:23.932Z'), + startTime: new Date('2020-09-12T06:15:23.932Z'), + finishTime: new Date('2020-09-12T06:20:23.932Z'), + sourceBranch: 'main', + sourceVersion: 'abcd', + }, + { + id: 2, + buildNumber: 'Build-2', + status: BuildStatus.Completed, + result: BuildResult.Succeeded, + queueTime: new Date('2020-09-12T06:10:23.932Z'), + startTime: new Date('2020-09-12T06:15:23.932Z'), + finishTime: new Date('2020-09-12T06:20:23.932Z'), + sourceBranch: 'main', + sourceVersion: 'abcd', + }, + { + id: 3, + buildNumber: 'Build-3', + status: BuildStatus.Completed, + result: BuildResult.Succeeded, + queueTime: new Date('2020-09-12T06:10:23.932Z'), + startTime: new Date('2020-09-12T06:15:23.932Z'), + finishTime: new Date('2020-09-12T06:20:23.932Z'), + sourceBranch: 'main', + sourceVersion: 'abcd', + }, + ]; + + const mockBuildClient = { + getBuilds: jest.fn().mockResolvedValue(mockBuilds), + }; + const mockGitClient = { + getRepository: jest.fn().mockResolvedValue(mockGitRepository), + }; + const mockApi = { + getGitApi: jest.fn().mockReturnValue(mockGitClient), + getBuildApi: jest.fn().mockReturnValue(mockBuildClient), + }; + + (WebApi as unknown as jest.Mock).mockImplementation(() => mockApi); + + const api = AzureDevOpsApi.fromConfig(mockConfig, { + logger: mockLogger, + urlReader: mockUrlReader, + }); + + const result = await api.getBuildRuns('project', 10, 'repo', undefined); + + expect(result).toEqual([ + { + id: 1, + title: 'Build-1', + link: '', + status: BuildStatus.Completed, + result: BuildResult.Succeeded, + queueTime: '2020-09-12T06:10:23.932Z', + startTime: '2020-09-12T06:15:23.932Z', + finishTime: '2020-09-12T06:20:23.932Z', + source: 'main (abcd)', + uniqueName: 'N/A', + }, + { + id: 2, + title: 'Build-2', + link: '', + status: BuildStatus.Completed, + result: BuildResult.Succeeded, + queueTime: '2020-09-12T06:10:23.932Z', + startTime: '2020-09-12T06:15:23.932Z', + finishTime: '2020-09-12T06:20:23.932Z', + source: 'main (abcd)', + uniqueName: 'N/A', + }, + { + id: 3, + title: 'Build-3', + link: '', + status: BuildStatus.Completed, + result: BuildResult.Succeeded, + queueTime: '2020-09-12T06:10:23.932Z', + startTime: '2020-09-12T06:15:23.932Z', + finishTime: '2020-09-12T06:20:23.932Z', + source: 'main (abcd)', + uniqueName: 'N/A', + }, + ]); + }); + + it('should get build runs with definitionName', async () => { + const mockBuilds: Build[] = [ + { + id: 1, + buildNumber: 'Build-1', + status: BuildStatus.Completed, + result: BuildResult.Succeeded, + queueTime: new Date('2020-09-12T06:10:23.932Z'), + startTime: new Date('2020-09-12T06:15:23.932Z'), + finishTime: new Date('2020-09-12T06:20:23.932Z'), + sourceBranch: 'main', + sourceVersion: 'abcd', + }, + { + id: 2, + buildNumber: 'Build-2', + status: BuildStatus.Completed, + result: BuildResult.Succeeded, + queueTime: new Date('2020-09-12T06:10:23.932Z'), + startTime: new Date('2020-09-12T06:15:23.932Z'), + finishTime: new Date('2020-09-12T06:20:23.932Z'), + sourceBranch: 'main', + sourceVersion: 'abcd', + }, + { + id: 3, + buildNumber: 'Build-3', + status: BuildStatus.Completed, + result: BuildResult.Succeeded, + queueTime: new Date('2020-09-12T06:10:23.932Z'), + startTime: new Date('2020-09-12T06:15:23.932Z'), + finishTime: new Date('2020-09-12T06:20:23.932Z'), + sourceBranch: 'main', + sourceVersion: 'abcd', + }, + ]; + + const mockBuildClient = { + getBuilds: jest.fn().mockResolvedValue(mockBuilds), + getDefinitions: jest.fn().mockResolvedValue(mockBuilds), + }; + + const mockApi = { + getBuildApi: jest.fn().mockReturnValue(mockBuildClient), + }; + + (WebApi as unknown as jest.Mock).mockImplementation(() => mockApi); + + const api = AzureDevOpsApi.fromConfig(mockConfig, { + logger: mockLogger, + urlReader: mockUrlReader, + }); + + const result = await api.getBuildRuns( + 'project', + 10, + undefined, + 'definition', + ); + + expect(result).toEqual([ + { + id: 1, + title: 'Build-1', + link: '', + status: BuildStatus.Completed, + result: BuildResult.Succeeded, + queueTime: '2020-09-12T06:10:23.932Z', + startTime: '2020-09-12T06:15:23.932Z', + finishTime: '2020-09-12T06:20:23.932Z', + source: 'main (abcd)', + uniqueName: 'N/A', + }, + { + id: 2, + title: 'Build-2', + link: '', + status: BuildStatus.Completed, + result: BuildResult.Succeeded, + queueTime: '2020-09-12T06:10:23.932Z', + startTime: '2020-09-12T06:15:23.932Z', + finishTime: '2020-09-12T06:20:23.932Z', + source: 'main (abcd)', + uniqueName: 'N/A', + }, + { + id: 3, + title: 'Build-3', + link: '', + status: BuildStatus.Completed, + result: BuildResult.Succeeded, + queueTime: '2020-09-12T06:10:23.932Z', + startTime: '2020-09-12T06:15:23.932Z', + finishTime: '2020-09-12T06:20:23.932Z', + source: 'main (abcd)', + uniqueName: 'N/A', + }, + ]); }); }); diff --git a/plugins/azure-devops-backend/src/api/AzureDevOpsApi.ts b/plugins/azure-devops-backend/src/api/AzureDevOpsApi.ts index 87ded2f0e9..6765b54c05 100644 --- a/plugins/azure-devops-backend/src/api/AzureDevOpsApi.ts +++ b/plugins/azure-devops-backend/src/api/AzureDevOpsApi.ts @@ -19,9 +19,7 @@ import { BuildDefinitionReference, } from 'azure-devops-node-api/interfaces/BuildInterfaces'; import { - BuildResult, BuildRun, - BuildStatus, DashboardPullRequest, GitTag, Policy, @@ -49,23 +47,92 @@ import { import { TeamMember as AdoTeamMember } from 'azure-devops-node-api/interfaces/common/VSSInterfaces'; import { Logger } from 'winston'; import { PolicyEvaluationRecord } from 'azure-devops-node-api/interfaces/PolicyInterfaces'; -import { WebApi } from 'azure-devops-node-api'; +import { + WebApi, + getHandlerFromToken, + getPersonalAccessTokenHandler, +} from 'azure-devops-node-api'; import { TeamProjectReference, WebApiTeam, } from 'azure-devops-node-api/interfaces/CoreInterfaces'; import { UrlReader } from '@backstage/backend-common'; +import { Config } from '@backstage/config'; +import { + AzureDevOpsCredentialsProvider, + DefaultAzureDevOpsCredentialsProvider, + ScmIntegrations, +} from '@backstage/integration'; +import { + mappedBuildRun, + mappedGitTag, + mappedPullRequest, + mappedRepoBuild, +} from './mappers'; /** @public */ export class AzureDevOpsApi { - public constructor( - private readonly logger: Logger, - private readonly webApi: WebApi, - private readonly urlReader: UrlReader, - ) {} + private readonly logger: Logger; + private readonly urlReader: UrlReader; + private readonly config: Config; + private readonly credentialsProvider: AzureDevOpsCredentialsProvider; + + private constructor( + logger: Logger, + urlReader: UrlReader, + config: Config, + credentialsProvider: AzureDevOpsCredentialsProvider, + ) { + this.logger = logger; + this.urlReader = urlReader; + this.config = config; + this.credentialsProvider = credentialsProvider; + } + + static fromConfig( + config: Config, + options: { logger: Logger; urlReader: UrlReader }, + ) { + const scmIntegrations = ScmIntegrations.fromConfig(config); + const credentialsProvider = + DefaultAzureDevOpsCredentialsProvider.fromIntegrations(scmIntegrations); + return new AzureDevOpsApi( + options.logger, + options.urlReader, + config, + credentialsProvider, + ); + } + + private async getWebApi(host?: string, org?: string): Promise { + // If no host or org is provided we fall back to the values from the `azureDevOps` config section + // these may have been setup in the `integrations.azure` config section + // which is why use them here and not just falling back on them entirely + const validHost = host ?? this.config.getString('azureDevOps.host'); + const validOrg = org ?? this.config.getString('azureDevOps.organization'); + const url = `https://${validHost}/${encodeURI(validOrg)}`; + + const credentials = await this.credentialsProvider.getCredentials({ + url, + }); + + let authHandler; + if (!credentials) { + // No credentials found for the provided host and org in the `integrations.azure` config section + // use the fall back personal access token from `azureDevOps.token` + const token = this.config.getString('azureDevOps.token'); + authHandler = getPersonalAccessTokenHandler(token); + } else { + authHandler = getHandlerFromToken(credentials.token); + } + + const webApi = new WebApi(url, authHandler); + return webApi; + } public async getProjects(): Promise { - const client = await this.webApi.getCoreApi(); + const webApi = await this.getWebApi(); + const client = await webApi.getCoreApi(); const projectList: TeamProjectReference[] = await client.getProjects(); const projects: Project[] = projectList.map(project => ({ @@ -87,7 +154,8 @@ export class AzureDevOpsApi { `Calling Azure DevOps REST API, getting Repository ${repoName} for Project ${projectName}`, ); - const client = await this.webApi.getGitApi(); + const webApi = await this.getWebApi(); + const client = await webApi.getGitApi(); return client.getRepository(repoName, projectName); } @@ -100,7 +168,8 @@ export class AzureDevOpsApi { `Calling Azure DevOps REST API, getting up to ${top} Builds for Repository Id ${repoId} for Project ${projectName}`, ); - const client = await this.webApi.getBuildApi(); + const webApi = await this.getWebApi(); + const client = await webApi.getBuildApi(); return client.getBuilds( projectName, undefined, @@ -158,7 +227,8 @@ export class AzureDevOpsApi { ); const gitRepository = await this.getGitRepository(projectName, repoName); - const client = await this.webApi.getGitApi(); + const webApi = await this.getWebApi(); + const client = await webApi.getGitApi(); const tagRefs: GitRef[] = await client.getRefs( gitRepository.id as string, projectName, @@ -169,10 +239,10 @@ export class AzureDevOpsApi { false, true, ); - const linkBaseUrl = `${this.webApi.serverUrl}/${encodeURIComponent( + const linkBaseUrl = `${webApi.serverUrl}/${encodeURIComponent( projectName, )}/_git/${encodeURIComponent(repoName)}?version=GT`; - const commitBaseUrl = `${this.webApi.serverUrl}/${encodeURIComponent( + const commitBaseUrl = `${webApi.serverUrl}/${encodeURIComponent( projectName, )}/_git/${encodeURIComponent(repoName)}/commit`; const gitTags: GitTag[] = tagRefs.map(tagRef => { @@ -192,7 +262,8 @@ export class AzureDevOpsApi { ); const gitRepository = await this.getGitRepository(projectName, repoName); - const client = await this.webApi.getGitApi(); + const webApi = await this.getWebApi(); + const client = await webApi.getGitApi(); const searchCriteria: GitPullRequestSearchCriteria = { status: options.status, }; @@ -204,7 +275,7 @@ export class AzureDevOpsApi { undefined, options.top, ); - const linkBaseUrl = `${this.webApi.serverUrl}/${encodeURIComponent( + const linkBaseUrl = `${webApi.serverUrl}/${encodeURIComponent( projectName, )}/_git/${encodeURIComponent(repoName)}/pullrequest`; const pullRequests: PullRequest[] = gitPullRequests.map(gitPullRequest => { @@ -222,7 +293,8 @@ export class AzureDevOpsApi { `Getting dashboard pull requests for project '${projectName}'.`, ); - const client = await this.webApi.getGitApi(); + const webApi = await this.getWebApi(); + const client = await webApi.getGitApi(); const searchCriteria: GitPullRequestSearchCriteria = { status: options.status, @@ -254,7 +326,7 @@ export class AzureDevOpsApi { return convertDashboardPullRequest( gitPullRequest, - this.webApi.serverUrl, + webApi.serverUrl, policies, ); }), @@ -270,7 +342,8 @@ export class AzureDevOpsApi { `Getting pull request policies for pull request id '${pullRequestId}'.`, ); - const client = await this.webApi.getPolicyApi(); + const webApi = await this.getWebApi(); + const client = await webApi.getPolicyApi(); const artifactId = getArtifactId(projectId, pullRequestId); @@ -285,7 +358,8 @@ export class AzureDevOpsApi { public async getAllTeams(): Promise { this.logger?.debug('Getting all teams.'); - const client = await this.webApi.getCoreApi(); + const webApi = await this.getWebApi(); + const client = await webApi.getCoreApi(); const webApiTeams: WebApiTeam[] = await client.getAllTeams(); const teams: Team[] = webApiTeams.map(team => ({ @@ -307,7 +381,8 @@ export class AzureDevOpsApi { const { projectId, teamId } = options; this.logger?.debug(`Getting team member ids for team '${teamId}'.`); - const client = await this.webApi.getCoreApi(); + const webApi = await this.getWebApi(); + const client = await webApi.getCoreApi(); const teamMembers: AdoTeamMember[] = await client.getTeamMembersWithExtendedProperties(projectId, teamId); @@ -327,7 +402,8 @@ export class AzureDevOpsApi { `Calling Azure DevOps REST API, getting Build Definitions for ${definitionName} in Project ${projectName}`, ); - const client = await this.webApi.getBuildApi(); + const webApi = await this.getWebApi(); + const client = await webApi.getBuildApi(); return client.getDefinitions(projectName, definitionName); } @@ -341,7 +417,8 @@ export class AzureDevOpsApi { `Calling Azure DevOps REST API, getting up to ${top} Builds for Repository Id ${repoId} for Project ${projectName}`, ); - const client = await this.webApi.getBuildApi(); + const webApi = await this.getWebApi(); + const client = await webApi.getBuildApi(); return client.getBuilds( projectName, definitions, @@ -421,75 +498,3 @@ export class AzureDevOpsApi { return { url, content }; } } - -export function mappedRepoBuild(build: Build): RepoBuild { - return { - id: build.id, - title: [build.definition?.name, build.buildNumber] - .filter(Boolean) - .join(' - '), - link: build._links?.web.href ?? '', - status: build.status ?? BuildStatus.None, - result: build.result ?? BuildResult.None, - queueTime: build.queueTime?.toISOString(), - startTime: build.startTime?.toISOString(), - finishTime: build.finishTime?.toISOString(), - source: `${build.sourceBranch} (${build.sourceVersion?.slice(0, 8)})`, - uniqueName: build.requestedFor?.uniqueName ?? 'N/A', - }; -} - -export function mappedGitTag( - gitRef: GitRef, - linkBaseUrl: string, - commitBaseUrl: string, -): GitTag { - return { - objectId: gitRef.objectId, - peeledObjectId: gitRef.peeledObjectId, - name: gitRef.name?.replace('refs/tags/', ''), - createdBy: gitRef.creator?.displayName ?? 'N/A', - link: `${linkBaseUrl}${encodeURIComponent( - gitRef.name?.replace('refs/tags/', '') ?? '', - )}`, - commitLink: `${commitBaseUrl}/${encodeURIComponent( - gitRef.peeledObjectId ?? '', - )}`, - }; -} - -export function mappedPullRequest( - pullRequest: GitPullRequest, - linkBaseUrl: string, -): PullRequest { - return { - pullRequestId: pullRequest.pullRequestId, - repoName: pullRequest.repository?.name, - title: pullRequest.title, - uniqueName: pullRequest.createdBy?.uniqueName ?? 'N/A', - createdBy: pullRequest.createdBy?.displayName ?? 'N/A', - creationDate: pullRequest.creationDate?.toISOString(), - sourceRefName: pullRequest.sourceRefName, - targetRefName: pullRequest.targetRefName, - status: pullRequest.status, - isDraft: pullRequest.isDraft, - link: `${linkBaseUrl}/${pullRequest.pullRequestId}`, - }; -} - -export function mappedBuildRun(build: Build): BuildRun { - return { - id: build.id, - title: [build.definition?.name, build.buildNumber] - .filter(Boolean) - .join(' - '), - link: build._links?.web.href ?? '', - status: build.status ?? BuildStatus.None, - result: build.result ?? BuildResult.None, - queueTime: build.queueTime?.toISOString(), - startTime: build.startTime?.toISOString(), - finishTime: build.finishTime?.toISOString(), - source: `${build.sourceBranch} (${build.sourceVersion?.slice(0, 8)})`, - uniqueName: build.requestedFor?.uniqueName ?? 'N/A', - }; -} diff --git a/plugins/azure-devops-backend/src/api/mappers.test.ts b/plugins/azure-devops-backend/src/api/mappers.test.ts new file mode 100644 index 0000000000..5f7fe5eb28 --- /dev/null +++ b/plugins/azure-devops-backend/src/api/mappers.test.ts @@ -0,0 +1,573 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + Build, + DefinitionReference, +} from 'azure-devops-node-api/interfaces/BuildInterfaces'; +import { + BuildResult, + BuildStatus, + GitTag, + PullRequest, + PullRequestStatus, + RepoBuild, +} from '@backstage/plugin-azure-devops-common'; +import { + GitPullRequest, + GitRef, + GitRepository, +} from 'azure-devops-node-api/interfaces/GitInterfaces'; +import { + mappedBuildRun, + mappedGitTag, + mappedPullRequest, + mappedRepoBuild, +} from './mappers'; + +import { IdentityRef } from 'azure-devops-node-api/interfaces/common/VSSInterfaces'; + +describe('mappers', () => { + describe('mappedRepoBuild', () => { + describe('mappedRepoBuild happy path', () => { + it('should return RepoBuild from Build', () => { + const inputBuildDefinition: DefinitionReference = { + name: 'My Build Definition', + }; + + const inputLinks: any = { + web: { + href: 'https://host.com/myOrg/0bcc0c0d-2d02/_build/results?buildId=1', + }, + }; + + const inputIdentityRef: IdentityRef = { + displayName: 'Jane Doe', + uniqueName: 'DOMAIN\\jdoe', + }; + + const inputBuild: Build = { + id: 1, + buildNumber: 'Build-1', + status: BuildStatus.Completed, + result: BuildResult.Succeeded, + queueTime: new Date('2020-09-12T06:10:23.932Z'), + startTime: new Date('2020-09-12T06:15:23.932Z'), + finishTime: new Date('2020-09-12T06:20:23.932Z'), + sourceBranch: 'refs/heads/develop', + sourceVersion: 'f4f78b3100b2923982bdf60c89c57ce6fd2d9a1c', + definition: inputBuildDefinition, + _links: inputLinks, + requestedFor: inputIdentityRef, + }; + + const outputRepoBuild: RepoBuild = { + id: 1, + title: 'My Build Definition - Build-1', + link: 'https://host.com/myOrg/0bcc0c0d-2d02/_build/results?buildId=1', + status: BuildStatus.Completed, + result: BuildResult.Succeeded, + queueTime: '2020-09-12T06:10:23.932Z', + startTime: '2020-09-12T06:15:23.932Z', + finishTime: '2020-09-12T06:20:23.932Z', + source: 'refs/heads/develop (f4f78b31)', + uniqueName: 'DOMAIN\\jdoe', + }; + + expect(mappedRepoBuild(inputBuild)).toEqual(outputRepoBuild); + }); + }); + + describe('mappedRepoBuild with no Build definition name', () => { + it('should return RepoBuild with only Build Number for title', () => { + const inputLinks: any = { + web: { + href: 'https://host.com/myOrg/0bcc0c0d-2d02/_build/results?buildId=1', + }, + }; + + const inputIdentityRef: IdentityRef = { + displayName: 'Jane Doe', + uniqueName: 'DOMAIN\\jdoe', + }; + + const inputBuild: Build = { + id: 1, + buildNumber: 'Build-1', + status: BuildStatus.Completed, + result: BuildResult.Succeeded, + queueTime: new Date('2020-09-12T06:10:23.932Z'), + startTime: new Date('2020-09-12T06:15:23.932Z'), + finishTime: new Date('2020-09-12T06:20:23.932Z'), + sourceBranch: 'refs/heads/develop', + sourceVersion: 'f4f78b3100b2923982bdf60c89c57ce6fd2d9a1c', + definition: undefined, + _links: inputLinks, + requestedFor: inputIdentityRef, + }; + + const outputRepoBuild: RepoBuild = { + id: 1, + title: 'Build-1', + link: 'https://host.com/myOrg/0bcc0c0d-2d02/_build/results?buildId=1', + status: BuildStatus.Completed, + result: BuildResult.Succeeded, + queueTime: '2020-09-12T06:10:23.932Z', + startTime: '2020-09-12T06:15:23.932Z', + finishTime: '2020-09-12T06:20:23.932Z', + source: 'refs/heads/develop (f4f78b31)', + uniqueName: 'DOMAIN\\jdoe', + }; + + expect(mappedRepoBuild(inputBuild)).toEqual(outputRepoBuild); + }); + }); + + describe('mappedRepoBuild with undefined status', () => { + it('should return BuildStatus of None for status', () => { + const inputLinks: any = { + web: { + href: 'https://host.com/myOrg/0bcc0c0d-2d02/_build/results?buildId=1', + }, + }; + + const inputIdentityRef: IdentityRef = { + displayName: 'Jane Doe', + uniqueName: 'DOMAIN\\jdoe', + }; + + const inputBuild: Build = { + id: 1, + buildNumber: 'Build-1', + status: undefined, + result: BuildResult.Succeeded, + queueTime: new Date('2020-09-12T06:10:23.932Z'), + startTime: new Date('2020-09-12T06:15:23.932Z'), + finishTime: new Date('2020-09-12T06:20:23.932Z'), + sourceBranch: 'refs/heads/develop', + sourceVersion: 'f4f78b3100b2923982bdf60c89c57ce6fd2d9a1c', + definition: undefined, + _links: inputLinks, + requestedFor: inputIdentityRef, + }; + + const outputRepoBuild: RepoBuild = { + id: 1, + title: 'Build-1', + link: 'https://host.com/myOrg/0bcc0c0d-2d02/_build/results?buildId=1', + status: BuildStatus.None, + result: BuildResult.Succeeded, + queueTime: '2020-09-12T06:10:23.932Z', + startTime: '2020-09-12T06:15:23.932Z', + finishTime: '2020-09-12T06:20:23.932Z', + source: 'refs/heads/develop (f4f78b31)', + uniqueName: 'DOMAIN\\jdoe', + }; + + expect(mappedRepoBuild(inputBuild)).toEqual(outputRepoBuild); + }); + }); + + describe('mappedRepoBuild with undefined result', () => { + it('should return BuildResult of None for result', () => { + const inputLinks: any = { + web: { + href: 'https://host.com/myOrg/0bcc0c0d-2d02/_build/results?buildId=1', + }, + }; + + const inputIdentityRef: IdentityRef = { + displayName: 'Jane Doe', + uniqueName: 'DOMAIN\\jdoe', + }; + + const inputBuild: Build = { + id: 1, + buildNumber: 'Build-1', + status: BuildStatus.InProgress, + result: undefined, + queueTime: new Date('2020-09-12T06:10:23.932Z'), + startTime: new Date('2020-09-12T06:15:23.932Z'), + finishTime: new Date('2020-09-12T06:20:23.932Z'), + sourceBranch: 'refs/heads/develop', + sourceVersion: 'f4f78b3100b2923982bdf60c89c57ce6fd2d9a1c', + definition: undefined, + _links: inputLinks, + requestedFor: inputIdentityRef, + }; + + const outputRepoBuild: RepoBuild = { + id: 1, + title: 'Build-1', + link: 'https://host.com/myOrg/0bcc0c0d-2d02/_build/results?buildId=1', + status: BuildStatus.InProgress, + result: BuildResult.None, + queueTime: '2020-09-12T06:10:23.932Z', + startTime: '2020-09-12T06:15:23.932Z', + finishTime: '2020-09-12T06:20:23.932Z', + source: 'refs/heads/develop (f4f78b31)', + uniqueName: 'DOMAIN\\jdoe', + }; + + expect(mappedRepoBuild(inputBuild)).toEqual(outputRepoBuild); + }); + }); + + describe('mappedRepoBuild with undefined link', () => { + it('should return empty string for link', () => { + const inputIdentityRef: IdentityRef = { + displayName: 'Jane Doe', + uniqueName: 'DOMAIN\\jdoe', + }; + + const inputBuild: Build = { + id: 1, + buildNumber: 'Build-1', + status: BuildStatus.InProgress, + result: undefined, + queueTime: new Date('2020-09-12T06:10:23.932Z'), + startTime: new Date('2020-09-12T06:15:23.932Z'), + finishTime: new Date('2020-09-12T06:20:23.932Z'), + sourceBranch: 'refs/heads/develop', + sourceVersion: 'f4f78b3100b2923982bdf60c89c57ce6fd2d9a1c', + definition: undefined, + _links: undefined, + requestedFor: inputIdentityRef, + }; + + const outputRepoBuild: RepoBuild = { + id: 1, + title: 'Build-1', + link: '', + status: BuildStatus.InProgress, + result: BuildResult.None, + queueTime: '2020-09-12T06:10:23.932Z', + startTime: '2020-09-12T06:15:23.932Z', + finishTime: '2020-09-12T06:20:23.932Z', + source: 'refs/heads/develop (f4f78b31)', + uniqueName: 'DOMAIN\\jdoe', + }; + + expect(mappedRepoBuild(inputBuild)).toEqual(outputRepoBuild); + }); + }); + }); + + describe('mappedGitTag', () => { + describe('mappedGitTag happy path', () => { + it('should return GitTag from GitRef', () => { + const inputIdentityRef: IdentityRef = { + displayName: 'Jane Doe', + }; + const inputGitRef: GitRef = { + name: 'refs/tags/v1.1.2', + creator: inputIdentityRef, + objectId: '1111aaaa2222bbbb3333cccc4444dddd5555eeee', + peeledObjectId: '1234567890abcdef1234567890abcdef12345678', + }; + const inputLinkBaseUrl = + 'https://host.com/myOrg/_git/super-feature-repo?version=GT'; + const inputCommitBaseUrl = + 'https://host.com/myOrg/_git/super-feature-repo/commit'; + const outputGitTag: GitTag = { + name: 'v1.1.2', + createdBy: 'Jane Doe', + commitLink: + 'https://host.com/myOrg/_git/super-feature-repo/commit/1234567890abcdef1234567890abcdef12345678', + objectId: '1111aaaa2222bbbb3333cccc4444dddd5555eeee', + peeledObjectId: '1234567890abcdef1234567890abcdef12345678', + link: 'https://host.com/myOrg/_git/super-feature-repo?version=GTv1.1.2', + }; + expect( + mappedGitTag(inputGitRef, inputLinkBaseUrl, inputCommitBaseUrl), + ).toEqual(outputGitTag); + }); + }); + }); + + describe('mappedPullRequest', () => { + describe('mappedPullRequest happy path', () => { + it('should return PullRequest from GitPullRequest', () => { + const inputGitRepository: GitRepository = { + name: 'super-feature-repo', + }; + + const inputIdentityRef: IdentityRef = { + displayName: 'Jane Doe', + uniqueName: 'DOMAIN\\jdoe', + }; + + const inputPullRequest: GitPullRequest = { + pullRequestId: 7181, + repository: inputGitRepository, + title: 'My Awesome New Feature', + createdBy: inputIdentityRef, + creationDate: new Date('2020-09-12T06:10:23.932Z'), + sourceRefName: 'refs/heads/topic/super-awesome-feature', + targetRefName: 'refs/heads/main', + status: PullRequestStatus.Active, + isDraft: false, + }; + + const inputBaseUrl = + 'https://host.com/myOrg/_git/super-feature-repo/pullrequest'; + + const outputPullRequest: PullRequest = { + pullRequestId: 7181, + repoName: 'super-feature-repo', + title: 'My Awesome New Feature', + uniqueName: 'DOMAIN\\jdoe', + createdBy: 'Jane Doe', + creationDate: '2020-09-12T06:10:23.932Z', + sourceRefName: 'refs/heads/topic/super-awesome-feature', + targetRefName: 'refs/heads/main', + status: PullRequestStatus.Active, + isDraft: false, + link: 'https://host.com/myOrg/_git/super-feature-repo/pullrequest/7181', + }; + + expect(mappedPullRequest(inputPullRequest, inputBaseUrl)).toEqual( + outputPullRequest, + ); + }); + }); + }); + + describe('mappedBuildRun', () => { + describe('mappedBuildRun happy path', () => { + it('should return RepoBuild from Build', () => { + const inputBuildDefinition: DefinitionReference = { + name: 'My Build Definition', + }; + + const inputLinks: any = { + web: { + href: 'https://host.com/myOrg/0bcc0c0d-2d02/_build/results?buildId=1', + }, + }; + + const inputIdentityRef: IdentityRef = { + displayName: 'Jane Doe', + uniqueName: 'DOMAIN\\jdoe', + }; + + const inputBuild: Build = { + id: 1, + buildNumber: 'Build-1', + status: BuildStatus.Completed, + result: BuildResult.Succeeded, + queueTime: new Date('2020-09-12T06:10:23.932Z'), + startTime: new Date('2020-09-12T06:15:23.932Z'), + finishTime: new Date('2020-09-12T06:20:23.932Z'), + sourceBranch: 'refs/heads/develop', + sourceVersion: 'f4f78b3100b2923982bdf60c89c57ce6fd2d9a1c', + definition: inputBuildDefinition, + _links: inputLinks, + requestedFor: inputIdentityRef, + }; + + const outputRepoBuild: RepoBuild = { + id: 1, + title: 'My Build Definition - Build-1', + link: 'https://host.com/myOrg/0bcc0c0d-2d02/_build/results?buildId=1', + status: BuildStatus.Completed, + result: BuildResult.Succeeded, + queueTime: '2020-09-12T06:10:23.932Z', + startTime: '2020-09-12T06:15:23.932Z', + finishTime: '2020-09-12T06:20:23.932Z', + source: 'refs/heads/develop (f4f78b31)', + uniqueName: 'DOMAIN\\jdoe', + }; + + expect(mappedBuildRun(inputBuild)).toEqual(outputRepoBuild); + }); + }); + + describe('mappedBuildRun with no Build definition name', () => { + it('should return RepoBuild with only Build Number for title', () => { + const inputLinks: any = { + web: { + href: 'https://host.com/myOrg/0bcc0c0d-2d02/_build/results?buildId=1', + }, + }; + + const inputIdentityRef: IdentityRef = { + displayName: 'Jane Doe', + uniqueName: 'DOMAIN\\jdoe', + }; + + const inputBuild: Build = { + id: 1, + buildNumber: 'Build-1', + status: BuildStatus.Completed, + result: BuildResult.Succeeded, + queueTime: new Date('2020-09-12T06:10:23.932Z'), + startTime: new Date('2020-09-12T06:15:23.932Z'), + finishTime: new Date('2020-09-12T06:20:23.932Z'), + sourceBranch: 'refs/heads/develop', + sourceVersion: 'f4f78b3100b2923982bdf60c89c57ce6fd2d9a1c', + definition: undefined, + _links: inputLinks, + requestedFor: inputIdentityRef, + }; + + const outputRepoBuild: RepoBuild = { + id: 1, + title: 'Build-1', + link: 'https://host.com/myOrg/0bcc0c0d-2d02/_build/results?buildId=1', + status: BuildStatus.Completed, + result: BuildResult.Succeeded, + queueTime: '2020-09-12T06:10:23.932Z', + startTime: '2020-09-12T06:15:23.932Z', + finishTime: '2020-09-12T06:20:23.932Z', + source: 'refs/heads/develop (f4f78b31)', + uniqueName: 'DOMAIN\\jdoe', + }; + + expect(mappedBuildRun(inputBuild)).toEqual(outputRepoBuild); + }); + }); + + describe('mappedBuildRun with undefined status', () => { + it('should return BuildStatus of None for status', () => { + const inputLinks: any = { + web: { + href: 'https://host.com/myOrg/0bcc0c0d-2d02/_build/results?buildId=1', + }, + }; + + const inputIdentityRef: IdentityRef = { + displayName: 'Jane Doe', + uniqueName: 'DOMAIN\\jdoe', + }; + + const inputBuild: Build = { + id: 1, + buildNumber: 'Build-1', + status: undefined, + result: BuildResult.Succeeded, + queueTime: new Date('2020-09-12T06:10:23.932Z'), + startTime: new Date('2020-09-12T06:15:23.932Z'), + finishTime: new Date('2020-09-12T06:20:23.932Z'), + sourceBranch: 'refs/heads/develop', + sourceVersion: 'f4f78b3100b2923982bdf60c89c57ce6fd2d9a1c', + definition: undefined, + _links: inputLinks, + requestedFor: inputIdentityRef, + }; + + const outputRepoBuild: RepoBuild = { + id: 1, + title: 'Build-1', + link: 'https://host.com/myOrg/0bcc0c0d-2d02/_build/results?buildId=1', + status: BuildStatus.None, + result: BuildResult.Succeeded, + queueTime: '2020-09-12T06:10:23.932Z', + startTime: '2020-09-12T06:15:23.932Z', + finishTime: '2020-09-12T06:20:23.932Z', + source: 'refs/heads/develop (f4f78b31)', + uniqueName: 'DOMAIN\\jdoe', + }; + + expect(mappedBuildRun(inputBuild)).toEqual(outputRepoBuild); + }); + }); + + describe('mappedBuildRun with undefined result', () => { + it('should return BuildResult of None for result', () => { + const inputLinks: any = { + web: { + href: 'https://host.com/myOrg/0bcc0c0d-2d02/_build/results?buildId=1', + }, + }; + + const inputIdentityRef: IdentityRef = { + displayName: 'Jane Doe', + uniqueName: 'DOMAIN\\jdoe', + }; + + const inputBuild: Build = { + id: 1, + buildNumber: 'Build-1', + status: BuildStatus.InProgress, + result: undefined, + queueTime: new Date('2020-09-12T06:10:23.932Z'), + startTime: new Date('2020-09-12T06:15:23.932Z'), + finishTime: new Date('2020-09-12T06:20:23.932Z'), + sourceBranch: 'refs/heads/develop', + sourceVersion: 'f4f78b3100b2923982bdf60c89c57ce6fd2d9a1c', + definition: undefined, + _links: inputLinks, + requestedFor: inputIdentityRef, + }; + + const outputRepoBuild: RepoBuild = { + id: 1, + title: 'Build-1', + link: 'https://host.com/myOrg/0bcc0c0d-2d02/_build/results?buildId=1', + status: BuildStatus.InProgress, + result: BuildResult.None, + queueTime: '2020-09-12T06:10:23.932Z', + startTime: '2020-09-12T06:15:23.932Z', + finishTime: '2020-09-12T06:20:23.932Z', + source: 'refs/heads/develop (f4f78b31)', + uniqueName: 'DOMAIN\\jdoe', + }; + + expect(mappedBuildRun(inputBuild)).toEqual(outputRepoBuild); + }); + }); + + describe('mappedBuildRun with undefined link', () => { + it('should return empty string for link', () => { + const inputIdentityRef: IdentityRef = { + displayName: 'Jane Doe', + uniqueName: 'DOMAIN\\jdoe', + }; + + const inputBuild: Build = { + id: 1, + buildNumber: 'Build-1', + status: BuildStatus.InProgress, + result: undefined, + queueTime: new Date('2020-09-12T06:10:23.932Z'), + startTime: new Date('2020-09-12T06:15:23.932Z'), + finishTime: new Date('2020-09-12T06:20:23.932Z'), + sourceBranch: 'refs/heads/develop', + sourceVersion: 'f4f78b3100b2923982bdf60c89c57ce6fd2d9a1c', + definition: undefined, + _links: undefined, + requestedFor: inputIdentityRef, + }; + + const outputRepoBuild: RepoBuild = { + id: 1, + title: 'Build-1', + link: '', + status: BuildStatus.InProgress, + result: BuildResult.None, + queueTime: '2020-09-12T06:10:23.932Z', + startTime: '2020-09-12T06:15:23.932Z', + finishTime: '2020-09-12T06:20:23.932Z', + source: 'refs/heads/develop (f4f78b31)', + uniqueName: 'DOMAIN\\jdoe', + }; + + expect(mappedBuildRun(inputBuild)).toEqual(outputRepoBuild); + }); + }); + }); +}); diff --git a/plugins/azure-devops-backend/src/api/mappers.ts b/plugins/azure-devops-backend/src/api/mappers.ts new file mode 100644 index 0000000000..72097a25b8 --- /dev/null +++ b/plugins/azure-devops-backend/src/api/mappers.ts @@ -0,0 +1,101 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + RepoBuild, + BuildStatus, + BuildResult, + GitTag, + PullRequest, + BuildRun, +} from '@backstage/plugin-azure-devops-common'; +import { Build } from 'azure-devops-node-api/interfaces/BuildInterfaces'; +import { + GitRef, + GitPullRequest, +} from 'azure-devops-node-api/interfaces/GitInterfaces'; + +export function mappedRepoBuild(build: Build): RepoBuild { + return { + id: build.id, + title: [build.definition?.name, build.buildNumber] + .filter(Boolean) + .join(' - '), + link: build._links?.web.href ?? '', + status: build.status ?? BuildStatus.None, + result: build.result ?? BuildResult.None, + queueTime: build.queueTime?.toISOString(), + startTime: build.startTime?.toISOString(), + finishTime: build.finishTime?.toISOString(), + source: `${build.sourceBranch} (${build.sourceVersion?.slice(0, 8)})`, + uniqueName: build.requestedFor?.uniqueName ?? 'N/A', + }; +} + +export function mappedGitTag( + gitRef: GitRef, + linkBaseUrl: string, + commitBaseUrl: string, +): GitTag { + return { + objectId: gitRef.objectId, + peeledObjectId: gitRef.peeledObjectId, + name: gitRef.name?.replace('refs/tags/', ''), + createdBy: gitRef.creator?.displayName ?? 'N/A', + link: `${linkBaseUrl}${encodeURIComponent( + gitRef.name?.replace('refs/tags/', '') ?? '', + )}`, + commitLink: `${commitBaseUrl}/${encodeURIComponent( + gitRef.peeledObjectId ?? '', + )}`, + }; +} + +export function mappedPullRequest( + pullRequest: GitPullRequest, + linkBaseUrl: string, +): PullRequest { + return { + pullRequestId: pullRequest.pullRequestId, + repoName: pullRequest.repository?.name, + title: pullRequest.title, + uniqueName: pullRequest.createdBy?.uniqueName ?? 'N/A', + createdBy: pullRequest.createdBy?.displayName ?? 'N/A', + creationDate: pullRequest.creationDate?.toISOString(), + sourceRefName: pullRequest.sourceRefName, + targetRefName: pullRequest.targetRefName, + status: pullRequest.status, + isDraft: pullRequest.isDraft, + link: `${linkBaseUrl}/${pullRequest.pullRequestId}`, + }; +} + +export function mappedBuildRun(build: Build): BuildRun { + return { + id: build.id, + title: [build.definition?.name, build.buildNumber] + .filter(Boolean) + .join(' - '), + link: build._links?.web.href ?? '', + status: build.status ?? BuildStatus.None, + result: build.result ?? BuildResult.None, + queueTime: build.queueTime?.toISOString(), + startTime: build.startTime?.toISOString(), + finishTime: build.finishTime?.toISOString(), + source: `${build.sourceBranch} (${build.sourceVersion?.slice(0, 8)})`, + uniqueName: build.requestedFor?.uniqueName ?? 'N/A', + }; +} diff --git a/plugins/azure-devops-backend/src/index.ts b/plugins/azure-devops-backend/src/index.ts index 3c802e7144..61cf7c7a6f 100644 --- a/plugins/azure-devops-backend/src/index.ts +++ b/plugins/azure-devops-backend/src/index.ts @@ -23,3 +23,4 @@ export { AzureDevOpsApi } from './api'; export * from './service/router'; export { azureDevOpsPlugin as default } from './plugin'; +export { AzureDevOpsAnnotatorProcessor } from './processor'; diff --git a/plugins/azure-devops-backend/src/processor/AzureDevOpsAnnotatorProcessor.test.ts b/plugins/azure-devops-backend/src/processor/AzureDevOpsAnnotatorProcessor.test.ts new file mode 100644 index 0000000000..dd83c56f70 --- /dev/null +++ b/plugins/azure-devops-backend/src/processor/AzureDevOpsAnnotatorProcessor.test.ts @@ -0,0 +1,271 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { Entity } from '@backstage/catalog-model'; +import { ConfigReader } from '@backstage/config'; +import { AzureDevOpsAnnotatorProcessor } from './AzureDevOpsAnnotatorProcessor'; +import { LocationSpec } from '@backstage/plugin-catalog-common'; + +describe('AzureDevOpsAnnotatorProcessor', () => { + it('adds annotation', async () => { + const entity: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'my-component', + }, + }; + const location: LocationSpec = { + type: 'url', + target: + 'https://dev.azure.com/organization/project/_git/repository?path=%2Fcatalog-info.yaml', + }; + + const processor = AzureDevOpsAnnotatorProcessor.fromConfig( + new ConfigReader({}), + ); + + expect(await processor.preProcessEntity(entity, location)).toEqual({ + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'my-component', + annotations: { + 'dev.azure.com/host-org': 'dev.azure.com/organization', + 'dev.azure.com/project-repo': 'project/repository', + }, + }, + }); + }); + + it('adds annotation for Azure DevOps Server url', async () => { + const entity: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'my-component', + }, + }; + const location: LocationSpec = { + type: 'url', + target: + 'https://example.com/organization/project/_git/repository?path=%2Fcatalog-info.yaml', + }; + + const processor = AzureDevOpsAnnotatorProcessor.fromConfig( + new ConfigReader({ + integrations: { + azure: [ + { + host: 'example.com', + credentials: [ + { + personalAccessToken: 'pat', + }, + ], + }, + ], + }, + }), + ); + + expect(await processor.preProcessEntity(entity, location)).toEqual({ + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'my-component', + annotations: { + 'dev.azure.com/host-org': 'example.com/organization', + 'dev.azure.com/project-repo': 'project/repository', + }, + }, + }); + }); + + it('adds annotation for TFS subpath url', async () => { + const entity: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'my-component', + }, + }; + const location: LocationSpec = { + type: 'url', + target: + 'https://example.com/tfs/organization/project/_git/repository?path=%2Fcatalog-info.yaml', + }; + + const processor = AzureDevOpsAnnotatorProcessor.fromConfig( + new ConfigReader({ + integrations: { + azure: [ + { + host: 'example.com', + credentials: [ + { + personalAccessToken: 'pat', + }, + ], + }, + ], + }, + }), + ); + + expect(await processor.preProcessEntity(entity, location)).toEqual({ + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'my-component', + annotations: { + 'dev.azure.com/host-org': 'example.com/tfs/organization', + 'dev.azure.com/project-repo': 'project/repository', + }, + }, + }); + }); + + it('does not override existing annotation', async () => { + const entity: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'my-component', + annotations: { + 'dev.azure.com/host-org': 'myhost/myorg', + 'dev.azure.com/project-repo': 'myproj/myrepo', + }, + }, + }; + const location: LocationSpec = { + type: 'url', + target: + 'https://dev.azure.com/organization/project/_git/repository?path=%2Fcatalog-info.yaml', + }; + + const processor = AzureDevOpsAnnotatorProcessor.fromConfig( + new ConfigReader({}), + ); + + expect(await processor.preProcessEntity(entity, location)).toEqual({ + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'my-component', + annotations: { + 'dev.azure.com/host-org': 'myhost/myorg', + 'dev.azure.com/project-repo': 'myproj/myrepo', + }, + }, + }); + }); + + it('should not add annotation for other providers', async () => { + const entity: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'my-component', + }, + }; + const location: LocationSpec = { + type: 'url', + target: + 'https://not-in-mock-config.example.com/backstage/backstage/-/blob/master/catalog-info.yaml', + }; + + const processor = AzureDevOpsAnnotatorProcessor.fromConfig( + new ConfigReader({}), + ); + + expect(await processor.preProcessEntity(entity, location)).toEqual({ + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'my-component', + }, + }); + }); + + it('should only process applicable kinds', async () => { + const component: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'my-component', + }, + }; + + const api: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'API', + metadata: { + name: 'my-component', + }, + }; + + const system: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'System', + metadata: { + name: 'my-component', + }, + }; + + const location: LocationSpec = { + type: 'url', + target: + 'https://dev.azure.com/organization/project/_git/repository?path=%2Fcatalog-info.yaml', + }; + + const processor = AzureDevOpsAnnotatorProcessor.fromConfig( + new ConfigReader({}), + { kinds: ['API', 'Component'] }, + ); + + expect(await processor.preProcessEntity(component, location)).toEqual({ + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'my-component', + annotations: { + 'dev.azure.com/host-org': 'dev.azure.com/organization', + 'dev.azure.com/project-repo': 'project/repository', + }, + }, + }); + + expect(await processor.preProcessEntity(api, location)).toEqual({ + apiVersion: 'backstage.io/v1alpha1', + kind: 'API', + metadata: { + name: 'my-component', + annotations: { + 'dev.azure.com/host-org': 'dev.azure.com/organization', + 'dev.azure.com/project-repo': 'project/repository', + }, + }, + }); + + expect(await processor.preProcessEntity(system, location)).toEqual({ + apiVersion: 'backstage.io/v1alpha1', + kind: 'System', + metadata: { + name: 'my-component', + }, + }); + }); +}); diff --git a/plugins/azure-devops-backend/src/processor/AzureDevOpsAnnotatorProcessor.ts b/plugins/azure-devops-backend/src/processor/AzureDevOpsAnnotatorProcessor.ts new file mode 100644 index 0000000000..c27195b8ac --- /dev/null +++ b/plugins/azure-devops-backend/src/processor/AzureDevOpsAnnotatorProcessor.ts @@ -0,0 +1,127 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Entity } from '@backstage/catalog-model'; +import { Config } from '@backstage/config'; +import { + ScmIntegrationRegistry, + ScmIntegrations, +} from '@backstage/integration'; +import { identity, merge, pickBy } from 'lodash'; +import { LocationSpec } from '@backstage/plugin-catalog-common'; +import { CatalogProcessor } from '@backstage/plugin-catalog-node'; +import { + AZURE_DEVOPS_HOST_ORG_ANNOTATION, + AZURE_DEVOPS_REPO_ANNOTATION, +} from '@backstage/plugin-azure-devops-common'; +import { parseAzureDevOpsUrl } from '../utils'; + +/** @public */ +export class AzureDevOpsAnnotatorProcessor implements CatalogProcessor { + constructor( + private readonly opts: { + scmIntegrationRegistry: ScmIntegrationRegistry; + kinds?: string[]; + }, + ) {} + + getProcessorName(): string { + return 'AzureDevOpsAnnotatorProcessor'; + } + + static fromConfig( + config: Config, + options?: { kinds?: string[] }, + ): AzureDevOpsAnnotatorProcessor { + return new AzureDevOpsAnnotatorProcessor({ + scmIntegrationRegistry: ScmIntegrations.fromConfig(config), + kinds: options?.kinds, + }); + } + + async preProcessEntity( + entity: Entity, + location: LocationSpec, + ): Promise { + const applicableKinds = (this.opts.kinds ?? ['Component']).map(k => + k.toLocaleLowerCase('en-US'), + ); + if ( + !applicableKinds.includes(entity.kind.toLocaleLowerCase('en-US')) || + location.type !== 'url' + ) { + return entity; + } + + const scmIntegration = this.opts.scmIntegrationRegistry.byUrl( + location.target, + ); + + if (!scmIntegration) { + return entity; + } + + if (scmIntegration.type !== 'azure') { + return entity; + } + + const { host, org, project, repo } = parseAzureDevOpsUrl(location.target); + + if (!org || !project || !repo) { + return entity; + } + + const hostOrgAnnotation = AZURE_DEVOPS_HOST_ORG_ANNOTATION; + let hostOrgValue = entity.metadata.annotations?.[hostOrgAnnotation]; + if (!hostOrgValue) { + hostOrgValue = `${host}/${org}`; + } + + const projectRepoAnnotation = AZURE_DEVOPS_REPO_ANNOTATION; + let projectRepoValue = entity.metadata.annotations?.[projectRepoAnnotation]; + if (!projectRepoValue) { + projectRepoValue = `${project}/${repo}`; + } + + const result = merge( + { + metadata: { + annotations: pickBy( + { + [hostOrgAnnotation]: hostOrgValue, + }, + identity, + ), + }, + }, + entity, + ); + + return merge( + { + metadata: { + annotations: pickBy( + { + [projectRepoAnnotation]: projectRepoValue, + }, + identity, + ), + }, + }, + result, + ); + } +} diff --git a/plugins/azure-devops-backend/src/processor/index.ts b/plugins/azure-devops-backend/src/processor/index.ts new file mode 100644 index 0000000000..46747c4802 --- /dev/null +++ b/plugins/azure-devops-backend/src/processor/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { AzureDevOpsAnnotatorProcessor } from './AzureDevOpsAnnotatorProcessor'; diff --git a/plugins/azure-devops-backend/src/service/router.ts b/plugins/azure-devops-backend/src/service/router.ts index b6b8a6fdea..9fc762f1a7 100644 --- a/plugins/azure-devops-backend/src/service/router.ts +++ b/plugins/azure-devops-backend/src/service/router.ts @@ -19,7 +19,6 @@ import { PullRequestOptions, PullRequestStatus, } from '@backstage/plugin-azure-devops-common'; -import { WebApi, getPersonalAccessTokenHandler } from 'azure-devops-node-api'; import { AzureDevOpsApi } from '../api'; import { Config } from '@backstage/config'; @@ -43,18 +42,11 @@ export interface RouterOptions { export async function createRouter( options: RouterOptions, ): Promise { - const { logger, reader } = options; - const config = options.config.getConfig('azureDevOps'); - - const token = config.getString('token'); - const host = config.getString('host'); - const organization = config.getString('organization'); - - const authHandler = getPersonalAccessTokenHandler(token); - const webApi = new WebApi(`https://${host}/${organization}`, authHandler); + const { logger, reader, config } = options; const azureDevOpsApi = - options.azureDevOpsApi || new AzureDevOpsApi(logger, webApi, reader); + options.azureDevOpsApi || + AzureDevOpsApi.fromConfig(config, { logger, urlReader: reader }); const pullRequestsDashboardProvider = await PullRequestsDashboardProvider.create(logger, azureDevOpsApi); @@ -195,6 +187,8 @@ export async function createRouter( }); router.get('/readme/:projectName/:repoName', async (req, res) => { + const host = config.getString('azureDevOps.host'); + const organization = config.getString('azureDevOps.organization'); const { projectName, repoName } = req.params; const readme = await azureDevOpsApi.getReadme( host, diff --git a/plugins/azure-devops-backend/src/utils/azure-devops-utils.test.ts b/plugins/azure-devops-backend/src/utils/azure-devops-utils.test.ts index 427980be14..86a06b7b0b 100644 --- a/plugins/azure-devops-backend/src/utils/azure-devops-utils.test.ts +++ b/plugins/azure-devops-backend/src/utils/azure-devops-utils.test.ts @@ -28,6 +28,7 @@ import { getPullRequestLink, replaceReadme, buildEncodedUrl, + parseAzureDevOpsUrl, } from './azure-devops-utils'; import { GitPullRequest } from 'azure-devops-node-api/interfaces/GitInterfaces'; import { UrlReader } from '@backstage/backend-common'; @@ -295,3 +296,38 @@ describe('buildEncodedUrl', () => { ); }); }); + +describe('parseAzureDevOpsUrl', () => { + it('parses Azure DevOps Cloud url', async () => { + const result = parseAzureDevOpsUrl( + 'https://dev.azure.com/organization/project/_git/repository?path=%2Fcatalog-info.yaml', + ); + + expect(result.host).toEqual('dev.azure.com'); + expect(result.org).toEqual('organization'); + expect(result.project).toEqual('project'); + expect(result.repo).toEqual('repository'); + }); + + it('parses Azure DevOps Server url', async () => { + const result = parseAzureDevOpsUrl( + 'https://server.com/organization/project/_git/repository?path=%2Fcatalog-info.yaml', + ); + + expect(result.host).toEqual('server.com'); + expect(result.org).toEqual('organization'); + expect(result.project).toEqual('project'); + expect(result.repo).toEqual('repository'); + }); + + it('parses TFS subpath Url', async () => { + const result = parseAzureDevOpsUrl( + 'https://server.com/tfs/organization/project/_git/repository?path=%2Fcatalog-info.yaml', + ); + + expect(result.host).toEqual('server.com/tfs'); + expect(result.org).toEqual('organization'); + expect(result.project).toEqual('project'); + expect(result.repo).toEqual('repository'); + }); +}); diff --git a/plugins/azure-devops-backend/src/utils/azure-devops-utils.ts b/plugins/azure-devops-backend/src/utils/azure-devops-utils.ts index aa946e7a74..7f5bf39ac4 100644 --- a/plugins/azure-devops-backend/src/utils/azure-devops-utils.ts +++ b/plugins/azure-devops-backend/src/utils/azure-devops-utils.ts @@ -328,3 +328,29 @@ export function extractPartsFromAsset(content: string): { path: path.startsWith('.') ? path.substring(1, path.length) : path, }; } + +export function parseAzureDevOpsUrl(sourceUrl: string) { + const url = new URL(sourceUrl); + + let host = url.host; + let org; + let project; + let repo; + + const parts = url.pathname.split('/').map(part => decodeURIComponent(part)); + if (parts[2] === '_git') { + org = parts[1]; + project = repo = parts[3]; + } else if (parts[3] === '_git') { + org = parts[1]; + project = parts[2]; + repo = parts[4]; + } else if (parts[4] === '_git') { + host = `${host}/${parts[1]}`; + org = parts[2]; + project = parts[3]; + repo = parts[5]; + } + + return { host, org, project, repo }; +} diff --git a/plugins/azure-devops-common/CHANGELOG.md b/plugins/azure-devops-common/CHANGELOG.md index e5637a9ff4..5ad70d8832 100644 --- a/plugins/azure-devops-common/CHANGELOG.md +++ b/plugins/azure-devops-common/CHANGELOG.md @@ -1,5 +1,11 @@ # @backstage/plugin-azure-devops-common +## 0.3.2-next.0 + +### Patch Changes + +- 043b724c56: Introduced new `AzureDevOpsAnnotatorProcessor` that adds the needed annotations automatically. Also, moved constants to common package so they can be shared more easily + ## 0.3.1 ### Patch Changes diff --git a/plugins/azure-devops-common/api-report.md b/plugins/azure-devops-common/api-report.md index ea9f7130c0..262d5c8136 100644 --- a/plugins/azure-devops-common/api-report.md +++ b/plugins/azure-devops-common/api-report.md @@ -3,6 +3,22 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts +// @public (undocumented) +export const AZURE_DEVOPS_BUILD_DEFINITION_ANNOTATION = + 'dev.azure.com/build-definition'; + +// @public (undocumented) +export const AZURE_DEVOPS_DEFAULT_TOP: number; + +// @public (undocumented) +export const AZURE_DEVOPS_HOST_ORG_ANNOTATION = 'dev.azure.com/host-org'; + +// @public (undocumented) +export const AZURE_DEVOPS_PROJECT_ANNOTATION = 'dev.azure.com/project'; + +// @public (undocumented) +export const AZURE_DEVOPS_REPO_ANNOTATION = 'dev.azure.com/project-repo'; + // @public (undocumented) export enum BuildResult { Canceled = 32, diff --git a/plugins/azure-devops-common/package.json b/plugins/azure-devops-common/package.json index 7648e39539..f2bbe449ff 100644 --- a/plugins/azure-devops-common/package.json +++ b/plugins/azure-devops-common/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-azure-devops-common", - "version": "0.3.1", + "version": "0.3.2-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/azure-devops/src/constants.ts b/plugins/azure-devops-common/src/constants.ts similarity index 92% rename from plugins/azure-devops/src/constants.ts rename to plugins/azure-devops-common/src/constants.ts index aba0ec093a..5cc6b54592 100644 --- a/plugins/azure-devops/src/constants.ts +++ b/plugins/azure-devops-common/src/constants.ts @@ -14,9 +14,14 @@ * limitations under the License. */ +/** @public */ export const AZURE_DEVOPS_BUILD_DEFINITION_ANNOTATION = 'dev.azure.com/build-definition'; +/** @public */ export const AZURE_DEVOPS_HOST_ORG_ANNOTATION = 'dev.azure.com/host-org'; +/** @public */ export const AZURE_DEVOPS_PROJECT_ANNOTATION = 'dev.azure.com/project'; +/** @public */ export const AZURE_DEVOPS_REPO_ANNOTATION = 'dev.azure.com/project-repo'; +/** @public */ export const AZURE_DEVOPS_DEFAULT_TOP: number = 10; diff --git a/plugins/azure-devops-common/src/index.ts b/plugins/azure-devops-common/src/index.ts index 26a854de89..9a4a007317 100644 --- a/plugins/azure-devops-common/src/index.ts +++ b/plugins/azure-devops-common/src/index.ts @@ -15,3 +15,4 @@ */ export * from './types'; +export * from './constants'; diff --git a/plugins/azure-devops/CHANGELOG.md b/plugins/azure-devops/CHANGELOG.md index 919b4b7a25..3cd6209099 100644 --- a/plugins/azure-devops/CHANGELOG.md +++ b/plugins/azure-devops/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-azure-devops +## 0.3.10-next.1 + +### Patch Changes + +- 043b724c56: Introduced new `AzureDevOpsAnnotatorProcessor` that adds the needed annotations automatically. Also, moved constants to common package so they can be shared more easily +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/plugin-azure-devops-common@0.3.2-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + ## 0.3.10-next.0 ### Patch Changes diff --git a/plugins/azure-devops/package.json b/plugins/azure-devops/package.json index 9040d51e8d..39b14b8be2 100644 --- a/plugins/azure-devops/package.json +++ b/plugins/azure-devops/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-azure-devops", - "version": "0.3.10-next.0", + "version": "0.3.10-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/azure-devops/src/hooks/useBuildRuns.ts b/plugins/azure-devops/src/hooks/useBuildRuns.ts index 2b70ea63a5..0c10dc8db8 100644 --- a/plugins/azure-devops/src/hooks/useBuildRuns.ts +++ b/plugins/azure-devops/src/hooks/useBuildRuns.ts @@ -15,11 +15,11 @@ */ import { + AZURE_DEVOPS_DEFAULT_TOP, BuildRun, BuildRunOptions, } from '@backstage/plugin-azure-devops-common'; -import { AZURE_DEVOPS_DEFAULT_TOP } from '../constants'; import { azureDevOpsApiRef } from '../api'; import { useApi } from '@backstage/core-plugin-api'; import useAsync from 'react-use/lib/useAsync'; diff --git a/plugins/azure-devops/src/hooks/usePullRequests.ts b/plugins/azure-devops/src/hooks/usePullRequests.ts index de886919eb..1ac6afae24 100644 --- a/plugins/azure-devops/src/hooks/usePullRequests.ts +++ b/plugins/azure-devops/src/hooks/usePullRequests.ts @@ -15,12 +15,12 @@ */ import { + AZURE_DEVOPS_DEFAULT_TOP, PullRequest, PullRequestOptions, PullRequestStatus, } from '@backstage/plugin-azure-devops-common'; -import { AZURE_DEVOPS_DEFAULT_TOP } from '../constants'; import { Entity } from '@backstage/catalog-model'; import { azureDevOpsApiRef } from '../api'; import { useApi } from '@backstage/core-plugin-api'; diff --git a/plugins/azure-devops/src/plugin.ts b/plugins/azure-devops/src/plugin.ts index 4dd423f35a..1e76c9e53a 100644 --- a/plugins/azure-devops/src/plugin.ts +++ b/plugins/azure-devops/src/plugin.ts @@ -14,11 +14,6 @@ * limitations under the License. */ -import { - AZURE_DEVOPS_BUILD_DEFINITION_ANNOTATION, - AZURE_DEVOPS_PROJECT_ANNOTATION, - AZURE_DEVOPS_REPO_ANNOTATION, -} from './constants'; import { azurePipelinesEntityContentRouteRef, azurePullRequestDashboardRouteRef, @@ -37,6 +32,11 @@ import { import { AzureDevOpsClient } from './api/AzureDevOpsClient'; import { Entity } from '@backstage/catalog-model'; import { azureDevOpsApiRef } from './api/AzureDevOpsApi'; +import { + AZURE_DEVOPS_REPO_ANNOTATION, + AZURE_DEVOPS_PROJECT_ANNOTATION, + AZURE_DEVOPS_BUILD_DEFINITION_ANNOTATION, +} from '@backstage/plugin-azure-devops-common'; /** @public */ export const isAzureDevOpsAvailable = (entity: Entity) => diff --git a/plugins/azure-devops/src/utils/getAnnotationValuesFromEntity.ts b/plugins/azure-devops/src/utils/getAnnotationValuesFromEntity.ts index 9feec8bf95..dec140706c 100644 --- a/plugins/azure-devops/src/utils/getAnnotationValuesFromEntity.ts +++ b/plugins/azure-devops/src/utils/getAnnotationValuesFromEntity.ts @@ -14,14 +14,13 @@ * limitations under the License. */ -import { - AZURE_DEVOPS_BUILD_DEFINITION_ANNOTATION, - AZURE_DEVOPS_HOST_ORG_ANNOTATION, - AZURE_DEVOPS_PROJECT_ANNOTATION, - AZURE_DEVOPS_REPO_ANNOTATION, -} from '../constants'; - import { Entity } from '@backstage/catalog-model'; +import { + AZURE_DEVOPS_PROJECT_ANNOTATION, + AZURE_DEVOPS_BUILD_DEFINITION_ANNOTATION, + AZURE_DEVOPS_REPO_ANNOTATION, + AZURE_DEVOPS_HOST_ORG_ANNOTATION, +} from '@backstage/plugin-azure-devops-common'; export function getAnnotationValuesFromEntity(entity: Entity): { project: string; diff --git a/plugins/azure-sites-backend/CHANGELOG.md b/plugins/azure-sites-backend/CHANGELOG.md index 75cf7ac7a5..19953f496a 100644 --- a/plugins/azure-sites-backend/CHANGELOG.md +++ b/plugins/azure-sites-backend/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-azure-sites-backend +## 0.1.18-next.1 + +### Patch Changes + +- 99fb54183b: Updated dependency `@azure/identity` to `^4.0.0`. +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/config@1.1.1 + - @backstage/plugin-azure-sites-common@0.1.1 + ## 0.1.18-next.0 ### Patch Changes diff --git a/plugins/azure-sites-backend/package.json b/plugins/azure-sites-backend/package.json index bf1cc6a144..1a62fead17 100644 --- a/plugins/azure-sites-backend/package.json +++ b/plugins/azure-sites-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-azure-sites-backend", - "version": "0.1.18-next.0", + "version": "0.1.18-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/azure-sites/CHANGELOG.md b/plugins/azure-sites/CHANGELOG.md index 3c36d729f3..95d7c4491e 100644 --- a/plugins/azure-sites/CHANGELOG.md +++ b/plugins/azure-sites/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-azure-sites +## 0.1.17-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/theme@0.5.0-next.0 + - @backstage/plugin-azure-sites-common@0.1.1 + ## 0.1.17-next.0 ### Patch Changes diff --git a/plugins/azure-sites/package.json b/plugins/azure-sites/package.json index 631d368bc8..e8a21a2a55 100644 --- a/plugins/azure-sites/package.json +++ b/plugins/azure-sites/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-azure-sites", - "version": "0.1.17-next.0", + "version": "0.1.17-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/badges-backend/CHANGELOG.md b/plugins/badges-backend/CHANGELOG.md index aab01f0504..f424b1b04e 100644 --- a/plugins/badges-backend/CHANGELOG.md +++ b/plugins/badges-backend/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-badges-backend +## 0.3.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.5.0-next.0 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-auth-node@0.4.2-next.1 + ## 0.3.5-next.0 ### Patch Changes diff --git a/plugins/badges-backend/package.json b/plugins/badges-backend/package.json index 657ad01b09..6aaae4d8ea 100644 --- a/plugins/badges-backend/package.json +++ b/plugins/badges-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-badges-backend", "description": "A Backstage backend plugin that generates README badges for your entities", - "version": "0.3.5-next.0", + "version": "0.3.5-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/badges/CHANGELOG.md b/plugins/badges/CHANGELOG.md index 0d1a0856c9..f7330a5a9b 100644 --- a/plugins/badges/CHANGELOG.md +++ b/plugins/badges/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-badges +## 0.2.52-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + ## 0.2.52-next.0 ### Patch Changes diff --git a/plugins/badges/package.json b/plugins/badges/package.json index ff09805301..627af40a6b 100644 --- a/plugins/badges/package.json +++ b/plugins/badges/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-badges", "description": "A Backstage plugin that generates README badges for your entities", - "version": "0.2.52-next.0", + "version": "0.2.52-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/bazaar-backend/CHANGELOG.md b/plugins/bazaar-backend/CHANGELOG.md index ca50ba2d48..0fc8f9323d 100644 --- a/plugins/bazaar-backend/CHANGELOG.md +++ b/plugins/bazaar-backend/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-bazaar-backend +## 0.3.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-auth-node@0.4.2-next.1 + ## 0.3.6-next.0 ### Patch Changes diff --git a/plugins/bazaar-backend/alpha-api-report.md b/plugins/bazaar-backend/api-report-alpha.md similarity index 100% rename from plugins/bazaar-backend/alpha-api-report.md rename to plugins/bazaar-backend/api-report-alpha.md diff --git a/plugins/bazaar-backend/package.json b/plugins/bazaar-backend/package.json index f1deae24d8..5823ebe418 100644 --- a/plugins/bazaar-backend/package.json +++ b/plugins/bazaar-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-bazaar-backend", - "version": "0.3.6-next.0", + "version": "0.3.6-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/bazaar/CHANGELOG.md b/plugins/bazaar/CHANGELOG.md index 8dff109d9f..d89b8fa06b 100644 --- a/plugins/bazaar/CHANGELOG.md +++ b/plugins/bazaar/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-bazaar +## 0.2.20-next.2 + +### Patch Changes + +- 5d796829bb: Internalize 'AboutField' to break catalog dependency +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-client@1.5.0-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + ## 0.2.20-next.1 ### Patch Changes diff --git a/plugins/bazaar/package.json b/plugins/bazaar/package.json index a4b59ec57e..fcf5245a4a 100644 --- a/plugins/bazaar/package.json +++ b/plugins/bazaar/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-bazaar", - "version": "0.2.20-next.1", + "version": "0.2.20-next.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/bitbucket-cloud-common/CHANGELOG.md b/plugins/bitbucket-cloud-common/CHANGELOG.md index 58bfdcea30..12d38ed830 100644 --- a/plugins/bitbucket-cloud-common/CHANGELOG.md +++ b/plugins/bitbucket-cloud-common/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-bitbucket-cloud-common +## 0.2.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.8.0-next.1 + ## 0.2.15-next.0 ### Patch Changes diff --git a/plugins/bitbucket-cloud-common/package.json b/plugins/bitbucket-cloud-common/package.json index 0aa9d3550e..5525510d5e 100644 --- a/plugins/bitbucket-cloud-common/package.json +++ b/plugins/bitbucket-cloud-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-bitbucket-cloud-common", "description": "Common functionalities for bitbucket-cloud plugins", - "version": "0.2.15-next.0", + "version": "0.2.15-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/bitrise/CHANGELOG.md b/plugins/bitrise/CHANGELOG.md index 3b180f797e..46f9c95da7 100644 --- a/plugins/bitrise/CHANGELOG.md +++ b/plugins/bitrise/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-bitrise +## 0.1.55-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/theme@0.5.0-next.0 + ## 0.1.55-next.0 ### Patch Changes diff --git a/plugins/bitrise/package.json b/plugins/bitrise/package.json index 5f4df1022f..47d35cbe58 100644 --- a/plugins/bitrise/package.json +++ b/plugins/bitrise/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-bitrise", "description": "A Backstage plugin that integrates towards Bitrise", - "version": "0.1.55-next.0", + "version": "0.1.55-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-aws/CHANGELOG.md b/plugins/catalog-backend-module-aws/CHANGELOG.md index 4447c07186..94d8f6913c 100644 --- a/plugins/catalog-backend-module-aws/CHANGELOG.md +++ b/plugins/catalog-backend-module-aws/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-catalog-backend-module-aws +## 0.3.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.8.0-next.1 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/plugin-kubernetes-common@0.7.2-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/backend-tasks@0.5.13-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/integration-aws-node@0.1.8 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.18 + - @backstage/plugin-catalog-node@1.5.1-next.1 + ## 0.3.2-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-aws/alpha-api-report.md b/plugins/catalog-backend-module-aws/api-report-alpha.md similarity index 100% rename from plugins/catalog-backend-module-aws/alpha-api-report.md rename to plugins/catalog-backend-module-aws/api-report-alpha.md diff --git a/plugins/catalog-backend-module-aws/package.json b/plugins/catalog-backend-module-aws/package.json index a4ae3b395f..7dc89cd13e 100644 --- a/plugins/catalog-backend-module-aws/package.json +++ b/plugins/catalog-backend-module-aws/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-aws", "description": "A Backstage catalog backend module that helps integrate towards AWS", - "version": "0.3.2-next.0", + "version": "0.3.2-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-aws/src/module/catalogModuleAwsS3EntityProvider.ts b/plugins/catalog-backend-module-aws/src/module/catalogModuleAwsS3EntityProvider.ts index 521d61d3d8..e7816ae12f 100644 --- a/plugins/catalog-backend-module-aws/src/module/catalogModuleAwsS3EntityProvider.ts +++ b/plugins/catalog-backend-module-aws/src/module/catalogModuleAwsS3EntityProvider.ts @@ -29,7 +29,7 @@ import { AwsS3EntityProvider } from '../providers'; */ export const catalogModuleAwsS3EntityProvider = createBackendModule({ pluginId: 'catalog', - moduleId: 'awsS3EntityProvider', + moduleId: 'aws-s3-entity-provider', register(env) { env.registerInit({ deps: { diff --git a/plugins/catalog-backend-module-azure/CHANGELOG.md b/plugins/catalog-backend-module-azure/CHANGELOG.md index 04d6dcd33b..e34657c198 100644 --- a/plugins/catalog-backend-module-azure/CHANGELOG.md +++ b/plugins/catalog-backend-module-azure/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-catalog-backend-module-azure +## 0.1.27-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.8.0-next.1 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/backend-tasks@0.5.13-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.18 + - @backstage/plugin-catalog-node@1.5.1-next.1 + ## 0.1.27-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-azure/alpha-api-report.md b/plugins/catalog-backend-module-azure/api-report-alpha.md similarity index 100% rename from plugins/catalog-backend-module-azure/alpha-api-report.md rename to plugins/catalog-backend-module-azure/api-report-alpha.md diff --git a/plugins/catalog-backend-module-azure/package.json b/plugins/catalog-backend-module-azure/package.json index 1ff6c4cc78..8425b22331 100644 --- a/plugins/catalog-backend-module-azure/package.json +++ b/plugins/catalog-backend-module-azure/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-azure", "description": "A Backstage catalog backend module that helps integrate towards Azure", - "version": "0.1.27-next.0", + "version": "0.1.27-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-azure/src/module/catalogModuleAzureDevOpsEntityProvider.ts b/plugins/catalog-backend-module-azure/src/module/catalogModuleAzureDevOpsEntityProvider.ts index 804c0d4712..b9c55041b3 100644 --- a/plugins/catalog-backend-module-azure/src/module/catalogModuleAzureDevOpsEntityProvider.ts +++ b/plugins/catalog-backend-module-azure/src/module/catalogModuleAzureDevOpsEntityProvider.ts @@ -29,7 +29,7 @@ import { AzureDevOpsEntityProvider } from '../providers'; */ export const catalogModuleAzureDevOpsEntityProvider = createBackendModule({ pluginId: 'catalog', - moduleId: 'azureDevOpsEntityProvider', + moduleId: 'azure-dev-ops-entity-provider', register(env) { env.registerInit({ deps: { diff --git a/plugins/catalog-backend-module-backstage-openapi/CHANGELOG.md b/plugins/catalog-backend-module-backstage-openapi/CHANGELOG.md index cf6015342e..db59c77fc4 100644 --- a/plugins/catalog-backend-module-backstage-openapi/CHANGELOG.md +++ b/plugins/catalog-backend-module-backstage-openapi/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-catalog-backend-module-backstage-openapi +## 0.1.1-next.1 + +### Patch Changes + +- eb44e92898: Support authenticated backends +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-openapi-utils@0.1.1-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/backend-tasks@0.5.13-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-catalog-node@1.5.1-next.1 + ## 0.1.1-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-backstage-openapi/package.json b/plugins/catalog-backend-module-backstage-openapi/package.json index 57611b6920..dc3fc37508 100644 --- a/plugins/catalog-backend-module-backstage-openapi/package.json +++ b/plugins/catalog-backend-module-backstage-openapi/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-backstage-openapi", - "version": "0.1.1-next.0", + "version": "0.1.1-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md b/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md index d0824e1fe6..4f54bad476 100644 --- a/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md +++ b/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-catalog-backend-module-bitbucket-cloud +## 0.1.23-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.5.0-next.0 + - @backstage/integration@1.8.0-next.1 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/backend-tasks@0.5.13-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/plugin-bitbucket-cloud-common@0.2.15-next.1 + - @backstage/plugin-catalog-common@1.0.18 + - @backstage/plugin-catalog-node@1.5.1-next.1 + - @backstage/plugin-events-node@0.2.17-next.1 + ## 0.1.23-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-bitbucket-cloud/alpha-api-report.md b/plugins/catalog-backend-module-bitbucket-cloud/api-report-alpha.md similarity index 100% rename from plugins/catalog-backend-module-bitbucket-cloud/alpha-api-report.md rename to plugins/catalog-backend-module-bitbucket-cloud/api-report-alpha.md diff --git a/plugins/catalog-backend-module-bitbucket-cloud/package.json b/plugins/catalog-backend-module-bitbucket-cloud/package.json index fd991c00a6..6848617058 100644 --- a/plugins/catalog-backend-module-bitbucket-cloud/package.json +++ b/plugins/catalog-backend-module-bitbucket-cloud/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-bitbucket-cloud", "description": "A Backstage catalog backend module that helps integrate towards Bitbucket Cloud", - "version": "0.1.23-next.0", + "version": "0.1.23-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-bitbucket-cloud/src/module/catalogModuleBitbucketCloudEntityProvider.ts b/plugins/catalog-backend-module-bitbucket-cloud/src/module/catalogModuleBitbucketCloudEntityProvider.ts index e5e1ad1609..39265dc1a5 100644 --- a/plugins/catalog-backend-module-bitbucket-cloud/src/module/catalogModuleBitbucketCloudEntityProvider.ts +++ b/plugins/catalog-backend-module-bitbucket-cloud/src/module/catalogModuleBitbucketCloudEntityProvider.ts @@ -31,7 +31,7 @@ import { BitbucketCloudEntityProvider } from '../providers/BitbucketCloudEntityP */ export const catalogModuleBitbucketCloudEntityProvider = createBackendModule({ pluginId: 'catalog', - moduleId: 'bitbucketCloudEntityProvider', + moduleId: 'bitbucket-cloud-entity-provider', register(env) { env.registerInit({ deps: { diff --git a/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md b/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md index f96a8ee235..71e47ddb6c 100644 --- a/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md +++ b/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-catalog-backend-module-bitbucket-server +## 0.1.21-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.8.0-next.1 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/backend-tasks@0.5.13-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-catalog-node@1.5.1-next.1 + ## 0.1.21-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-bitbucket-server/alpha-api-report.md b/plugins/catalog-backend-module-bitbucket-server/api-report-alpha.md similarity index 100% rename from plugins/catalog-backend-module-bitbucket-server/alpha-api-report.md rename to plugins/catalog-backend-module-bitbucket-server/api-report-alpha.md diff --git a/plugins/catalog-backend-module-bitbucket-server/package.json b/plugins/catalog-backend-module-bitbucket-server/package.json index acba1bc02a..bf2130422b 100644 --- a/plugins/catalog-backend-module-bitbucket-server/package.json +++ b/plugins/catalog-backend-module-bitbucket-server/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-bitbucket-server", - "version": "0.1.21-next.0", + "version": "0.1.21-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-bitbucket-server/src/module/catalogModuleBitbucketServerEntityProvider.ts b/plugins/catalog-backend-module-bitbucket-server/src/module/catalogModuleBitbucketServerEntityProvider.ts index ef71df9b06..dd2a423cdc 100644 --- a/plugins/catalog-backend-module-bitbucket-server/src/module/catalogModuleBitbucketServerEntityProvider.ts +++ b/plugins/catalog-backend-module-bitbucket-server/src/module/catalogModuleBitbucketServerEntityProvider.ts @@ -27,7 +27,7 @@ import { BitbucketServerEntityProvider } from '../providers'; */ export const catalogModuleBitbucketServerEntityProvider = createBackendModule({ pluginId: 'catalog', - moduleId: 'bitbucketServerEntityProvider', + moduleId: 'bitbucket-server-entity-provider', register(env) { env.registerInit({ deps: { diff --git a/plugins/catalog-backend-module-bitbucket/CHANGELOG.md b/plugins/catalog-backend-module-bitbucket/CHANGELOG.md index a611562cfe..3ceec86c77 100644 --- a/plugins/catalog-backend-module-bitbucket/CHANGELOG.md +++ b/plugins/catalog-backend-module-bitbucket/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-catalog-backend-module-bitbucket +## 0.2.23-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.8.0-next.1 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-bitbucket-cloud-common@0.2.15-next.1 + - @backstage/plugin-catalog-node@1.5.1-next.1 + ## 0.2.23-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-bitbucket/package.json b/plugins/catalog-backend-module-bitbucket/package.json index cc0d27df6b..75eb671f30 100644 --- a/plugins/catalog-backend-module-bitbucket/package.json +++ b/plugins/catalog-backend-module-bitbucket/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-bitbucket", "description": "A Backstage catalog backend module that helps integrate towards Bitbucket", - "version": "0.2.23-next.0", + "version": "0.2.23-next.1", "deprecated": true, "main": "src/index.ts", "types": "src/index.ts", diff --git a/plugins/catalog-backend-module-gcp/CHANGELOG.md b/plugins/catalog-backend-module-gcp/CHANGELOG.md index a438d1dc65..3af2589342 100644 --- a/plugins/catalog-backend-module-gcp/CHANGELOG.md +++ b/plugins/catalog-backend-module-gcp/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-catalog-backend-module-gcp +## 0.1.8-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/plugin-kubernetes-common@0.7.2-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/backend-tasks@0.5.13-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/plugin-catalog-node@1.5.1-next.1 + ## 0.1.8-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-gcp/alpha-api-report.md b/plugins/catalog-backend-module-gcp/api-report-alpha.md similarity index 100% rename from plugins/catalog-backend-module-gcp/alpha-api-report.md rename to plugins/catalog-backend-module-gcp/api-report-alpha.md diff --git a/plugins/catalog-backend-module-gcp/package.json b/plugins/catalog-backend-module-gcp/package.json index d33ffa9d47..e68aeaec66 100644 --- a/plugins/catalog-backend-module-gcp/package.json +++ b/plugins/catalog-backend-module-gcp/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-gcp", "description": "A Backstage catalog backend module that helps integrate towards GCP", - "version": "0.1.8-next.0", + "version": "0.1.8-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-gcp/src/module/catalogModuleGcpGkeEntityProvider.ts b/plugins/catalog-backend-module-gcp/src/module/catalogModuleGcpGkeEntityProvider.ts index 7f93bddbfe..acc216e1f6 100644 --- a/plugins/catalog-backend-module-gcp/src/module/catalogModuleGcpGkeEntityProvider.ts +++ b/plugins/catalog-backend-module-gcp/src/module/catalogModuleGcpGkeEntityProvider.ts @@ -29,7 +29,7 @@ import { GkeEntityProvider } from '../providers/GkeEntityProvider'; */ export const catalogModuleGcpGkeEntityProvider = createBackendModule({ pluginId: 'catalog', - moduleId: 'gcpGkeEntityProvider', + moduleId: 'gcp-gke-entity-provider', register(env) { env.registerInit({ deps: { diff --git a/plugins/catalog-backend-module-gerrit/CHANGELOG.md b/plugins/catalog-backend-module-gerrit/CHANGELOG.md index b0f9d60904..6251a978b4 100644 --- a/plugins/catalog-backend-module-gerrit/CHANGELOG.md +++ b/plugins/catalog-backend-module-gerrit/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-catalog-backend-module-gerrit +## 0.1.24-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.8.0-next.1 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/backend-tasks@0.5.13-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-catalog-node@1.5.1-next.1 + ## 0.1.24-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-gerrit/alpha-api-report.md b/plugins/catalog-backend-module-gerrit/api-report-alpha.md similarity index 100% rename from plugins/catalog-backend-module-gerrit/alpha-api-report.md rename to plugins/catalog-backend-module-gerrit/api-report-alpha.md diff --git a/plugins/catalog-backend-module-gerrit/package.json b/plugins/catalog-backend-module-gerrit/package.json index 04e3457389..2bccaa62c2 100644 --- a/plugins/catalog-backend-module-gerrit/package.json +++ b/plugins/catalog-backend-module-gerrit/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-gerrit", - "version": "0.1.24-next.0", + "version": "0.1.24-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-gerrit/src/module/catalogModuleGerritEntityProvider.ts b/plugins/catalog-backend-module-gerrit/src/module/catalogModuleGerritEntityProvider.ts index bb29ce6eef..af870d095f 100644 --- a/plugins/catalog-backend-module-gerrit/src/module/catalogModuleGerritEntityProvider.ts +++ b/plugins/catalog-backend-module-gerrit/src/module/catalogModuleGerritEntityProvider.ts @@ -27,7 +27,7 @@ import { GerritEntityProvider } from '../providers/GerritEntityProvider'; */ export const catalogModuleGerritEntityProvider = createBackendModule({ pluginId: 'catalog', - moduleId: 'gerritEntityProvider', + moduleId: 'gerrit-entity-provider', register(env) { env.registerInit({ deps: { diff --git a/plugins/catalog-backend-module-github-org/CHANGELOG.md b/plugins/catalog-backend-module-github-org/CHANGELOG.md index cd9731e46c..dd857af17c 100644 --- a/plugins/catalog-backend-module-github-org/CHANGELOG.md +++ b/plugins/catalog-backend-module-github-org/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-catalog-backend-module-github-org +## 0.1.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/backend-tasks@0.5.13-next.1 + - @backstage/config@1.1.1 + - @backstage/plugin-catalog-backend-module-github@0.4.6-next.1 + - @backstage/plugin-catalog-node@1.5.1-next.1 + ## 0.1.2-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-github-org/package.json b/plugins/catalog-backend-module-github-org/package.json index a1abbd3207..b2d2904bc8 100644 --- a/plugins/catalog-backend-module-github-org/package.json +++ b/plugins/catalog-backend-module-github-org/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-github-org", "description": "The github-org backend module for the catalog plugin.", - "version": "0.1.2-next.0", + "version": "0.1.2-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-github-org/src/module.ts b/plugins/catalog-backend-module-github-org/src/module.ts index e510464bb7..d666f78bde 100644 --- a/plugins/catalog-backend-module-github-org/src/module.ts +++ b/plugins/catalog-backend-module-github-org/src/module.ts @@ -68,7 +68,7 @@ export const githubOrgEntityProviderTransformsExtensionPoint = */ export const catalogModuleGithubOrgEntityProvider = createBackendModule({ pluginId: 'catalog', - moduleId: 'githubOrgEntityProvider', + moduleId: 'github-org-entity-provider', register(env) { let userTransformer: UserTransformer | undefined; let teamTransformer: TeamTransformer | undefined; diff --git a/plugins/catalog-backend-module-github/CHANGELOG.md b/plugins/catalog-backend-module-github/CHANGELOG.md index 535430f03b..780e095568 100644 --- a/plugins/catalog-backend-module-github/CHANGELOG.md +++ b/plugins/catalog-backend-module-github/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-catalog-backend-module-github +## 0.4.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.15.1-next.1 + - @backstage/catalog-client@1.5.0-next.0 + - @backstage/integration@1.8.0-next.1 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/backend-tasks@0.5.13-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/plugin-catalog-common@1.0.18 + - @backstage/plugin-catalog-node@1.5.1-next.1 + - @backstage/plugin-events-node@0.2.17-next.1 + ## 0.4.6-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-github/alpha-api-report.md b/plugins/catalog-backend-module-github/api-report-alpha.md similarity index 100% rename from plugins/catalog-backend-module-github/alpha-api-report.md rename to plugins/catalog-backend-module-github/api-report-alpha.md diff --git a/plugins/catalog-backend-module-github/package.json b/plugins/catalog-backend-module-github/package.json index e94b98e427..08f86b7feb 100644 --- a/plugins/catalog-backend-module-github/package.json +++ b/plugins/catalog-backend-module-github/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-github", "description": "A Backstage catalog backend module that helps integrate towards GitHub", - "version": "0.4.6-next.0", + "version": "0.4.6-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-github/src/module/catalogModuleGithubEntityProvider.ts b/plugins/catalog-backend-module-github/src/module/catalogModuleGithubEntityProvider.ts index 8e052bd0a0..bfa8cf2a4e 100644 --- a/plugins/catalog-backend-module-github/src/module/catalogModuleGithubEntityProvider.ts +++ b/plugins/catalog-backend-module-github/src/module/catalogModuleGithubEntityProvider.ts @@ -29,7 +29,7 @@ import { GithubEntityProvider } from '../providers/GithubEntityProvider'; */ export const catalogModuleGithubEntityProvider = createBackendModule({ pluginId: 'catalog', - moduleId: 'githubEntityProvider', + moduleId: 'github-entity-provider', register(env) { env.registerInit({ deps: { diff --git a/plugins/catalog-backend-module-gitlab/CHANGELOG.md b/plugins/catalog-backend-module-gitlab/CHANGELOG.md index 5d98bdd8c2..816002bd55 100644 --- a/plugins/catalog-backend-module-gitlab/CHANGELOG.md +++ b/plugins/catalog-backend-module-gitlab/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-catalog-backend-module-gitlab +## 0.3.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.8.0-next.1 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/backend-tasks@0.5.13-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/plugin-catalog-node@1.5.1-next.1 + ## 0.3.5-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-gitlab/alpha-api-report.md b/plugins/catalog-backend-module-gitlab/api-report-alpha.md similarity index 100% rename from plugins/catalog-backend-module-gitlab/alpha-api-report.md rename to plugins/catalog-backend-module-gitlab/api-report-alpha.md diff --git a/plugins/catalog-backend-module-gitlab/package.json b/plugins/catalog-backend-module-gitlab/package.json index d42961b665..0a9728756c 100644 --- a/plugins/catalog-backend-module-gitlab/package.json +++ b/plugins/catalog-backend-module-gitlab/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-gitlab", "description": "A Backstage catalog backend module that helps integrate towards GitLab", - "version": "0.3.5-next.0", + "version": "0.3.5-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-gitlab/src/module/catalogModuleGitlabDiscoveryEntityProvider.ts b/plugins/catalog-backend-module-gitlab/src/module/catalogModuleGitlabDiscoveryEntityProvider.ts index d192e8f3a5..9979d0862e 100644 --- a/plugins/catalog-backend-module-gitlab/src/module/catalogModuleGitlabDiscoveryEntityProvider.ts +++ b/plugins/catalog-backend-module-gitlab/src/module/catalogModuleGitlabDiscoveryEntityProvider.ts @@ -29,7 +29,7 @@ import { GitlabDiscoveryEntityProvider } from '../providers'; */ export const catalogModuleGitlabDiscoveryEntityProvider = createBackendModule({ pluginId: 'catalog', - moduleId: 'gitlabDiscoveryEntityProvider', + moduleId: 'gitlab-discovery-entity-provider', register(env) { env.registerInit({ deps: { diff --git a/plugins/catalog-backend-module-incremental-ingestion/CHANGELOG.md b/plugins/catalog-backend-module-incremental-ingestion/CHANGELOG.md index f2b342aae6..d62732c7c2 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.4.12-next.1 + +### Patch Changes + +- 43b2eb8f70: Ensure that cursors always come back as JSON on sqlite too +- Updated dependencies + - @backstage/plugin-catalog-backend@1.15.1-next.1 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/backend-tasks@0.5.13-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-catalog-node@1.5.1-next.1 + - @backstage/plugin-events-node@0.2.17-next.1 + - @backstage/plugin-permission-common@0.7.10 + ## 0.4.12-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-incremental-ingestion/alpha-api-report.md b/plugins/catalog-backend-module-incremental-ingestion/api-report-alpha.md similarity index 100% rename from plugins/catalog-backend-module-incremental-ingestion/alpha-api-report.md rename to plugins/catalog-backend-module-incremental-ingestion/api-report-alpha.md diff --git a/plugins/catalog-backend-module-incremental-ingestion/package.json b/plugins/catalog-backend-module-incremental-ingestion/package.json index 53677fdec2..003ee93d6a 100644 --- a/plugins/catalog-backend-module-incremental-ingestion/package.json +++ b/plugins/catalog-backend-module-incremental-ingestion/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-incremental-ingestion", "description": "An entity provider for streaming large asset sources into the catalog", - "version": "0.4.12-next.0", + "version": "0.4.12-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-incremental-ingestion/src/database/IncrementalIngestionDatabaseManager.test.ts b/plugins/catalog-backend-module-incremental-ingestion/src/database/IncrementalIngestionDatabaseManager.test.ts new file mode 100644 index 0000000000..a8862cfc2a --- /dev/null +++ b/plugins/catalog-backend-module-incremental-ingestion/src/database/IncrementalIngestionDatabaseManager.test.ts @@ -0,0 +1,79 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { TestDatabases } from '@backstage/backend-test-utils'; +import { IncrementalIngestionDatabaseManager } from './IncrementalIngestionDatabaseManager'; +import { v4 as uuid } from 'uuid'; + +const migrationsDir = `${__dirname}/../../migrations`; + +jest.setTimeout(60_000); + +describe('IncrementalIngestionDatabaseManager', () => { + const databases = TestDatabases.create({ + ids: ['POSTGRES_13', 'POSTGRES_9', 'SQLITE_3'], + }); + + it.each(databases.eachSupportedId())( + 'stores and retrieves marks, %p', + async databaseId => { + const knex = await databases.init(databaseId); + await knex.migrate.latest({ directory: migrationsDir }); + + const manager = new IncrementalIngestionDatabaseManager({ client: knex }); + const { ingestionId } = (await manager.createProviderIngestionRecord( + 'myProvider', + ))!; + + const cursorId = uuid(); + + await manager.createMark({ + record: { + id: cursorId, + ingestion_id: ingestionId, + sequence: 1, + cursor: { data: 1 }, + }, + }); + + await expect(manager.getFirstMark(ingestionId)).resolves.toEqual({ + created_at: expect.anything(), + cursor: { data: 1 }, + id: cursorId, + ingestion_id: ingestionId, + sequence: 1, + }); + + await expect(manager.getLastMark(ingestionId)).resolves.toEqual({ + created_at: expect.anything(), + cursor: { data: 1 }, + id: cursorId, + ingestion_id: ingestionId, + sequence: 1, + }); + + await expect(manager.getAllMarks(ingestionId)).resolves.toEqual([ + { + created_at: expect.anything(), + cursor: { data: 1 }, + id: cursorId, + ingestion_id: ingestionId, + sequence: 1, + }, + ]); + }, + ); +}); diff --git a/plugins/catalog-backend-module-incremental-ingestion/src/database/IncrementalIngestionDatabaseManager.ts b/plugins/catalog-backend-module-incremental-ingestion/src/database/IncrementalIngestionDatabaseManager.ts index 527d345706..e47954f47e 100644 --- a/plugins/catalog-backend-module-incremental-ingestion/src/database/IncrementalIngestionDatabaseManager.ts +++ b/plugins/catalog-backend-module-incremental-ingestion/src/database/IncrementalIngestionDatabaseManager.ts @@ -542,7 +542,7 @@ export class IncrementalIngestionDatabaseManager { .where('ingestion_id', ingestionId) .orderBy('sequence', 'desc') .first(); - return mark; + return this.#decodeMark(this.client, mark); }); } @@ -557,7 +557,7 @@ export class IncrementalIngestionDatabaseManager { .where('ingestion_id', ingestionId) .orderBy('sequence', 'asc') .first(); - return mark; + return this.#decodeMark(this.client, mark); }); } @@ -566,7 +566,7 @@ export class IncrementalIngestionDatabaseManager { const marks = await tx('ingestion_marks') .where('ingestion_id', ingestionId) .orderBy('sequence', 'desc'); - return marks; + return marks.map(m => this.#decodeMark(this.client, m)); }); } @@ -580,6 +580,19 @@ export class IncrementalIngestionDatabaseManager { await tx('ingestion_marks').insert(record); }); } + + // Handles the fact that sqlite does not support json columns; they just + // persist the stringified data instead + #decodeMark(knex: Knex, record: T): T { + if (record && knex.client.config.client.includes('sqlite3')) { + return { + ...record, + cursor: JSON.parse(record.cursor as string), + }; + } + return record; + } + /** * Performs an upsert to the `ingestion_mark_entities` table for all deferred entities. * @param markId - string diff --git a/plugins/catalog-backend-module-incremental-ingestion/src/database/tables.ts b/plugins/catalog-backend-module-incremental-ingestion/src/database/tables.ts index 00d40fb4a1..e1a47c040c 100644 --- a/plugins/catalog-backend-module-incremental-ingestion/src/database/tables.ts +++ b/plugins/catalog-backend-module-incremental-ingestion/src/database/tables.ts @@ -89,7 +89,7 @@ export interface MarkRecord { id: string; sequence: number; ingestion_id: string; - cursor: string; + cursor: unknown; created_at: string; } diff --git a/plugins/catalog-backend-module-incremental-ingestion/src/module/catalogModuleIncrementalIngestionEntityProvider.test.ts b/plugins/catalog-backend-module-incremental-ingestion/src/module/catalogModuleIncrementalIngestionEntityProvider.test.ts index 7d76ddf480..316a4be7eb 100644 --- a/plugins/catalog-backend-module-incremental-ingestion/src/module/catalogModuleIncrementalIngestionEntityProvider.test.ts +++ b/plugins/catalog-backend-module-incremental-ingestion/src/module/catalogModuleIncrementalIngestionEntityProvider.test.ts @@ -48,7 +48,7 @@ describe('catalogModuleIncrementalIngestionEntityProvider', () => { catalogModuleIncrementalIngestionEntityProvider(), createBackendModule({ pluginId: 'catalog', - moduleId: 'incrementalTest', + moduleId: 'incremental-test', register(env) { env.registerInit({ deps: { extension: incrementalIngestionProvidersExtensionPoint }, diff --git a/plugins/catalog-backend-module-incremental-ingestion/src/module/catalogModuleIncrementalIngestionEntityProvider.ts b/plugins/catalog-backend-module-incremental-ingestion/src/module/catalogModuleIncrementalIngestionEntityProvider.ts index 4e94088ef6..adb5ab6a04 100644 --- a/plugins/catalog-backend-module-incremental-ingestion/src/module/catalogModuleIncrementalIngestionEntityProvider.ts +++ b/plugins/catalog-backend-module-incremental-ingestion/src/module/catalogModuleIncrementalIngestionEntityProvider.ts @@ -49,7 +49,7 @@ export interface IncrementalIngestionProviderExtensionPoint { * ```ts * backend.add(createBackendModule({ * pluginId: 'catalog', - * moduleId: 'myIncrementalProvider', + * moduleId: 'my-incremental-provider', * register(env) { * env.registerInit({ * deps: { @@ -85,7 +85,7 @@ export const incrementalIngestionProvidersExtensionPoint = export const catalogModuleIncrementalIngestionEntityProvider = createBackendModule({ pluginId: 'catalog', - moduleId: 'incrementalIngestionEntityProvider', + moduleId: 'incremental-ingestion-entity-provider', register(env) { const addedProviders = new Array<{ provider: IncrementalEntityProvider; diff --git a/plugins/catalog-backend-module-incremental-ingestion/src/run.ts b/plugins/catalog-backend-module-incremental-ingestion/src/run.ts index c24deb6adb..5af31482a6 100644 --- a/plugins/catalog-backend-module-incremental-ingestion/src/run.ts +++ b/plugins/catalog-backend-module-incremental-ingestion/src/run.ts @@ -67,7 +67,7 @@ async function main() { backend.add( createBackendModule({ pluginId: 'catalog', - moduleId: 'incrementalTestProvider', + moduleId: 'incremental-test-provider', register(reg) { reg.registerInit({ deps: { extension: incrementalIngestionProvidersExtensionPoint }, diff --git a/plugins/catalog-backend-module-ldap/CHANGELOG.md b/plugins/catalog-backend-module-ldap/CHANGELOG.md index 987005f0a3..29a73c7239 100644 --- a/plugins/catalog-backend-module-ldap/CHANGELOG.md +++ b/plugins/catalog-backend-module-ldap/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-catalog-backend-module-ldap +## 0.5.23-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-tasks@0.5.13-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.18 + - @backstage/plugin-catalog-node@1.5.1-next.1 + ## 0.5.23-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-ldap/package.json b/plugins/catalog-backend-module-ldap/package.json index fc1325c991..1fcf31f572 100644 --- a/plugins/catalog-backend-module-ldap/package.json +++ b/plugins/catalog-backend-module-ldap/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-ldap", "description": "A Backstage catalog backend module that helps integrate towards LDAP", - "version": "0.5.23-next.0", + "version": "0.5.23-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-msgraph/CHANGELOG.md b/plugins/catalog-backend-module-msgraph/CHANGELOG.md index e4d73b6745..ecdff69a6b 100644 --- a/plugins/catalog-backend-module-msgraph/CHANGELOG.md +++ b/plugins/catalog-backend-module-msgraph/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-catalog-backend-module-msgraph +## 0.5.15-next.1 + +### Patch Changes + +- 99fb54183b: Updated dependency `@azure/identity` to `^4.0.0`. +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/backend-tasks@0.5.13-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/plugin-catalog-common@1.0.18 + - @backstage/plugin-catalog-node@1.5.1-next.1 + ## 0.5.15-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-msgraph/alpha-api-report.md b/plugins/catalog-backend-module-msgraph/api-report-alpha.md similarity index 100% rename from plugins/catalog-backend-module-msgraph/alpha-api-report.md rename to plugins/catalog-backend-module-msgraph/api-report-alpha.md diff --git a/plugins/catalog-backend-module-msgraph/package.json b/plugins/catalog-backend-module-msgraph/package.json index 52ff917585..e5767dbf10 100644 --- a/plugins/catalog-backend-module-msgraph/package.json +++ b/plugins/catalog-backend-module-msgraph/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-msgraph", "description": "A Backstage catalog backend module that helps integrate towards Microsoft Graph", - "version": "0.5.15-next.0", + "version": "0.5.15-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-openapi/CHANGELOG.md b/plugins/catalog-backend-module-openapi/CHANGELOG.md index 8b7743c39f..297b1d9393 100644 --- a/plugins/catalog-backend-module-openapi/CHANGELOG.md +++ b/plugins/catalog-backend-module-openapi/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-catalog-backend-module-openapi +## 0.1.25-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.15.1-next.1 + - @backstage/integration@1.8.0-next.1 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.18 + - @backstage/plugin-catalog-node@1.5.1-next.1 + ## 0.1.25-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-openapi/package.json b/plugins/catalog-backend-module-openapi/package.json index 85e8cedb06..d1c3d7b605 100644 --- a/plugins/catalog-backend-module-openapi/package.json +++ b/plugins/catalog-backend-module-openapi/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-openapi", "description": "A Backstage catalog backend module that helps with OpenAPI specifications", - "version": "0.1.25-next.0", + "version": "0.1.25-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-puppetdb/CHANGELOG.md b/plugins/catalog-backend-module-puppetdb/CHANGELOG.md index 1a2febb565..0679c1f6e5 100644 --- a/plugins/catalog-backend-module-puppetdb/CHANGELOG.md +++ b/plugins/catalog-backend-module-puppetdb/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-catalog-backend-module-puppetdb +## 0.1.13-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/backend-tasks@0.5.13-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-node@1.5.1-next.1 + ## 0.1.13-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-puppetdb/alpha-api-report.md b/plugins/catalog-backend-module-puppetdb/api-report-alpha.md similarity index 100% rename from plugins/catalog-backend-module-puppetdb/alpha-api-report.md rename to plugins/catalog-backend-module-puppetdb/api-report-alpha.md diff --git a/plugins/catalog-backend-module-puppetdb/package.json b/plugins/catalog-backend-module-puppetdb/package.json index 751c37c16c..e908a1806a 100644 --- a/plugins/catalog-backend-module-puppetdb/package.json +++ b/plugins/catalog-backend-module-puppetdb/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-puppetdb", "description": "A Backstage catalog backend module that helps integrate towards PuppetDB", - "version": "0.1.13-next.0", + "version": "0.1.13-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-puppetdb/src/module/catalogModulePuppetDbEntityProvider.ts b/plugins/catalog-backend-module-puppetdb/src/module/catalogModulePuppetDbEntityProvider.ts index 5546b5283b..aad28eb47a 100644 --- a/plugins/catalog-backend-module-puppetdb/src/module/catalogModulePuppetDbEntityProvider.ts +++ b/plugins/catalog-backend-module-puppetdb/src/module/catalogModulePuppetDbEntityProvider.ts @@ -29,7 +29,7 @@ import { PuppetDbEntityProvider } from '../providers/PuppetDbEntityProvider'; */ export const catalogModulePuppetDbEntityProvider = createBackendModule({ pluginId: 'catalog', - moduleId: 'puppetDbEntityProvider', + moduleId: 'puppetdb-entity-provider', register(env) { env.registerInit({ deps: { diff --git a/plugins/catalog-backend-module-scaffolder-entity-model/CHANGELOG.md b/plugins/catalog-backend-module-scaffolder-entity-model/CHANGELOG.md index f122b093e0..56edf10313 100644 --- a/plugins/catalog-backend-module-scaffolder-entity-model/CHANGELOG.md +++ b/plugins/catalog-backend-module-scaffolder-entity-model/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-catalog-backend-module-scaffolder-entity-model +## 0.1.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/plugin-catalog-common@1.0.18 + - @backstage/plugin-catalog-node@1.5.1-next.1 + - @backstage/plugin-scaffolder-common@1.4.3 + ## 0.1.5-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-scaffolder-entity-model/package.json b/plugins/catalog-backend-module-scaffolder-entity-model/package.json index 638aa6d104..ed1bf21a26 100644 --- a/plugins/catalog-backend-module-scaffolder-entity-model/package.json +++ b/plugins/catalog-backend-module-scaffolder-entity-model/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-scaffolder-entity-model", "description": "Adds support for the scaffolder specific entity model (e.g. the Template kind) to the catalog backend plugin.", - "version": "0.1.5-next.0", + "version": "0.1.5-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-scaffolder-entity-model/src/module.ts b/plugins/catalog-backend-module-scaffolder-entity-model/src/module.ts index 8ed763d32c..74048dea12 100644 --- a/plugins/catalog-backend-module-scaffolder-entity-model/src/module.ts +++ b/plugins/catalog-backend-module-scaffolder-entity-model/src/module.ts @@ -26,7 +26,7 @@ import { ScaffolderEntitiesProcessor } from '@backstage/plugin-catalog-backend-m */ export const catalogModuleScaffolderEntityModel = createBackendModule({ pluginId: 'catalog', - moduleId: 'scaffolderEntityModel', + moduleId: 'scaffolder-entity-model', register(env) { env.registerInit({ deps: { diff --git a/plugins/catalog-backend-module-unprocessed/CHANGELOG.md b/plugins/catalog-backend-module-unprocessed/CHANGELOG.md index 45dbc74091..3bf8cf6245 100644 --- a/plugins/catalog-backend-module-unprocessed/CHANGELOG.md +++ b/plugins/catalog-backend-module-unprocessed/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-catalog-backend-module-unprocessed +## 0.3.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/plugin-auth-node@0.4.2-next.1 + ## 0.3.5-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-unprocessed/package.json b/plugins/catalog-backend-module-unprocessed/package.json index 9e0f29285e..3e4efb5715 100644 --- a/plugins/catalog-backend-module-unprocessed/package.json +++ b/plugins/catalog-backend-module-unprocessed/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-unprocessed", "description": "Backstage Catalog module to view unprocessed entities", - "version": "0.3.5-next.0", + "version": "0.3.5-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-unprocessed/src/module.ts b/plugins/catalog-backend-module-unprocessed/src/module.ts index 1db4fd70b0..8c724d432d 100644 --- a/plugins/catalog-backend-module-unprocessed/src/module.ts +++ b/plugins/catalog-backend-module-unprocessed/src/module.ts @@ -27,7 +27,7 @@ import { UnprocessedEntitiesModule } from './UnprocessedEntitiesModule'; */ export const catalogModuleUnprocessedEntities = createBackendModule({ pluginId: 'catalog', - moduleId: 'catalogModuleUnprocessedEntities', + moduleId: 'catalog-module-unprocessed-entities', register(env) { env.registerInit({ deps: { diff --git a/plugins/catalog-backend/CHANGELOG.md b/plugins/catalog-backend/CHANGELOG.md index cb721208a3..c689547470 100644 --- a/plugins/catalog-backend/CHANGELOG.md +++ b/plugins/catalog-backend/CHANGELOG.md @@ -1,5 +1,30 @@ # @backstage/plugin-catalog-backend +## 1.15.1-next.1 + +### Patch Changes + +- 38340678c3: Update the OpenAPI spec to support the use of `openapi-generator`. +- 7123c58b3d: Updated dependency `@types/glob` to `^8.0.0`. +- Updated dependencies + - @backstage/catalog-client@1.5.0-next.0 + - @backstage/integration@1.8.0-next.1 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-openapi-utils@0.1.1-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/backend-tasks@0.5.13-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.2-next.1 + - @backstage/plugin-catalog-common@1.0.18 + - @backstage/plugin-catalog-node@1.5.1-next.1 + - @backstage/plugin-events-node@0.2.17-next.1 + - @backstage/plugin-permission-common@0.7.10 + - @backstage/plugin-permission-node@0.7.19-next.1 + - @backstage/plugin-search-backend-module-catalog@0.1.12-next.1 + ## 1.15.1-next.0 ### Patch Changes diff --git a/plugins/catalog-backend/alpha-api-report.md b/plugins/catalog-backend/api-report-alpha.md similarity index 97% rename from plugins/catalog-backend/alpha-api-report.md rename to plugins/catalog-backend/api-report-alpha.md index 1682d1cbae..51a2ec6114 100644 --- a/plugins/catalog-backend/alpha-api-report.md +++ b/plugins/catalog-backend/api-report-alpha.md @@ -6,6 +6,7 @@ import { BackendFeature } from '@backstage/backend-plugin-api'; import { ConditionalPolicyDecision } from '@backstage/plugin-permission-common'; import { Conditions } from '@backstage/plugin-permission-node'; +import { EntitiesSearchFilter } from '@backstage/plugin-catalog-node'; import { Entity } from '@backstage/catalog-model'; import { PermissionCondition } from '@backstage/plugin-permission-common'; import { PermissionCriteria } from '@backstage/plugin-permission-common'; @@ -90,12 +91,6 @@ export const createCatalogPermissionRule: < rule: PermissionRule, ) => PermissionRule; -// @public -export type EntitiesSearchFilter = { - key: string; - values?: string[]; -}; - // @alpha export const permissionRules: { hasAnnotation: PermissionRule< diff --git a/plugins/catalog-backend/api-report.md b/plugins/catalog-backend/api-report.md index 1aed0ea8b0..8b7565bd1f 100644 --- a/plugins/catalog-backend/api-report.md +++ b/plugins/catalog-backend/api-report.md @@ -28,7 +28,9 @@ import { Config } from '@backstage/config'; import { DefaultCatalogCollatorFactory as DefaultCatalogCollatorFactory_2 } from '@backstage/plugin-search-backend-module-catalog'; import type { DefaultCatalogCollatorFactoryOptions as DefaultCatalogCollatorFactoryOptions_2 } from '@backstage/plugin-search-backend-module-catalog'; import { DeferredEntity as DeferredEntity_2 } from '@backstage/plugin-catalog-node'; +import { EntitiesSearchFilter as EntitiesSearchFilter_2 } from '@backstage/plugin-catalog-node'; import { Entity } from '@backstage/catalog-model'; +import { EntityFilter as EntityFilter_2 } from '@backstage/plugin-catalog-node'; import { EntityPolicy } from '@backstage/catalog-model'; import { EntityProvider as EntityProvider_2 } from '@backstage/plugin-catalog-node'; import { EntityProviderConnection as EntityProviderConnection_2 } from '@backstage/plugin-catalog-node'; @@ -191,7 +193,7 @@ export type CatalogEnvironment = { // @public export type CatalogPermissionRuleInput< TParams extends PermissionRuleParams = PermissionRuleParams, -> = PermissionRule; +> = PermissionRule; // @public export interface CatalogProcessingEngine { @@ -313,24 +315,11 @@ export type DefaultCatalogCollatorFactoryOptions = // @public @deprecated (undocumented) export type DeferredEntity = DeferredEntity_2; -// @public -export type EntitiesSearchFilter = { - key: string; - values?: string[]; -}; +// @public @deprecated (undocumented) +export type EntitiesSearchFilter = EntitiesSearchFilter_2; -// @public -export type EntityFilter = - | { - allOf: EntityFilter[]; - } - | { - anyOf: EntityFilter[]; - } - | { - not: EntityFilter; - } - | EntitiesSearchFilter; +// @public @deprecated (undocumented) +export type EntityFilter = EntityFilter_2; // @public @deprecated (undocumented) export type EntityProvider = EntityProvider_2; diff --git a/plugins/catalog-backend/package.json b/plugins/catalog-backend/package.json index f20cbd86a9..cd83510bdc 100644 --- a/plugins/catalog-backend/package.json +++ b/plugins/catalog-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend", "description": "The Backstage backend plugin that provides the Backstage catalog", - "version": "1.15.1-next.0", + "version": "1.15.1-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -90,7 +90,7 @@ "@backstage/plugin-permission-common": "workspace:^", "@types/core-js": "^2.5.4", "@types/git-url-parse": "^9.0.0", - "@types/glob": "^7.1.1", + "@types/glob": "^8.0.0", "@types/lodash": "^4.14.151", "@types/supertest": "^2.0.8", "@types/uuid": "^8.0.0", diff --git a/plugins/catalog-backend/src/alpha.ts b/plugins/catalog-backend/src/alpha.ts index 06c9571bfa..f85ef02c80 100644 --- a/plugins/catalog-backend/src/alpha.ts +++ b/plugins/catalog-backend/src/alpha.ts @@ -14,11 +14,5 @@ * limitations under the License. */ -// TODO(Rugvip): Re-exported for alpha types as the API report will otherwise -// produce warnings due to the indirect dependency. Would be nice to avoid. -import type { EntitiesSearchFilter } from './catalog/types'; - -export type { /** @alpha */ EntitiesSearchFilter }; - export * from './permissions'; export { catalogPlugin as default } from './service/CatalogPlugin'; diff --git a/plugins/catalog-backend/src/catalog/types.ts b/plugins/catalog-backend/src/catalog/types.ts index 156b8aa64d..e9f5f154c2 100644 --- a/plugins/catalog-backend/src/catalog/types.ts +++ b/plugins/catalog-backend/src/catalog/types.ts @@ -15,19 +15,7 @@ */ import { Entity } from '@backstage/catalog-model'; - -/** - * A filter expression for entities. - * - * Any (at least one) of the outer sets must match, within which all of the - * individual filters must match. - * @public - */ -export type EntityFilter = - | { allOf: EntityFilter[] } - | { anyOf: EntityFilter[] } - | { not: EntityFilter } - | EntitiesSearchFilter; +import { EntityFilter } from '@backstage/plugin-catalog-node'; /** * A pagination rule for entities. @@ -46,27 +34,6 @@ export type EntityOrder = { order: 'asc' | 'desc'; }; -/** - * Matches rows in the search table. - * @public - */ -export type EntitiesSearchFilter = { - /** - * The key to match on. - * - * Matches are always case insensitive. - */ - key: string; - - /** - * Match on plain equality of values. - * - * Match on values that are equal to any of the given array items. Matches are - * always case insensitive. - */ - values?: string[]; -}; - export type PageInfo = | { hasNextPage: false; diff --git a/plugins/catalog-backend/src/deprecated.ts b/plugins/catalog-backend/src/deprecated.ts index f3c199cbd3..2201bbbab4 100644 --- a/plugins/catalog-backend/src/deprecated.ts +++ b/plugins/catalog-backend/src/deprecated.ts @@ -18,6 +18,8 @@ import { locationSpecToMetadataName as _locationSpecToMetadataName, locationSpecToLocationEntity as _locationSpecToLocationEntity, processingResult as _processingResult, + type EntitiesSearchFilter as _EntitiesSearchFilter, + type EntityFilter as _EntityFilter, type DeferredEntity as _DeferredEntity, type EntityRelationSpec as _EntityRelationSpec, type CatalogProcessor as _CatalogProcessor, @@ -53,6 +55,16 @@ export const locationSpecToLocationEntity = _locationSpecToLocationEntity; * @deprecated import from `@backstage/plugin-catalog-node` instead */ export const processingResult = _processingResult; +/** + * @public + * @deprecated import from `@backstage/plugin-catalog-node` instead + */ +export type EntitiesSearchFilter = _EntitiesSearchFilter; +/** + * @public + * @deprecated import from `@backstage/plugin-catalog-node` instead + */ +export type EntityFilter = _EntityFilter; /** * @public * @deprecated import from `@backstage/plugin-catalog-node` instead diff --git a/plugins/catalog-backend/src/index.ts b/plugins/catalog-backend/src/index.ts index 55c1c672d3..d2f20d24d8 100644 --- a/plugins/catalog-backend/src/index.ts +++ b/plugins/catalog-backend/src/index.ts @@ -20,7 +20,6 @@ * @packageDocumentation */ -export * from './catalog'; export * from './ingestion'; export * from './modules'; export * from './processing'; diff --git a/plugins/catalog-backend/src/permissions/rules/isEntityKind.ts b/plugins/catalog-backend/src/permissions/rules/isEntityKind.ts index 8ebad5521c..568aae4838 100644 --- a/plugins/catalog-backend/src/permissions/rules/isEntityKind.ts +++ b/plugins/catalog-backend/src/permissions/rules/isEntityKind.ts @@ -13,9 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import { RESOURCE_TYPE_CATALOG_ENTITY } from '@backstage/plugin-catalog-common/alpha'; import { z } from 'zod'; -import { EntitiesSearchFilter } from '../../catalog/types'; import { createCatalogPermissionRule } from './util'; /** @@ -36,7 +36,7 @@ export const isEntityKind = createCatalogPermissionRule({ const resourceKind = resource.kind.toLocaleLowerCase('en-US'); return kinds.some(kind => kind.toLocaleLowerCase('en-US') === resourceKind); }, - toQuery({ kinds }): EntitiesSearchFilter { + toQuery({ kinds }) { return { key: 'kind', values: kinds.map(kind => kind.toLocaleLowerCase('en-US')), diff --git a/plugins/catalog-backend/src/permissions/rules/util.ts b/plugins/catalog-backend/src/permissions/rules/util.ts index 466f5ce70b..c3e642490c 100644 --- a/plugins/catalog-backend/src/permissions/rules/util.ts +++ b/plugins/catalog-backend/src/permissions/rules/util.ts @@ -16,12 +16,12 @@ import { Entity } from '@backstage/catalog-model'; import { RESOURCE_TYPE_CATALOG_ENTITY } from '@backstage/plugin-catalog-common/alpha'; +import { EntitiesSearchFilter } from '@backstage/plugin-catalog-node'; import { PermissionRuleParams } from '@backstage/plugin-permission-common'; import { makeCreatePermissionRule, PermissionRule, } from '@backstage/plugin-permission-node'; -import { EntitiesSearchFilter } from '../../catalog/types'; /** * Convenience type for {@link @backstage/plugin-permission-node#PermissionRule} diff --git a/plugins/catalog-backend/src/processing/TaskPipeline.ts b/plugins/catalog-backend/src/processing/TaskPipeline.ts index 44030be9e0..b808e41ce4 100644 --- a/plugins/catalog-backend/src/processing/TaskPipeline.ts +++ b/plugins/catalog-backend/src/processing/TaskPipeline.ts @@ -14,7 +14,11 @@ * limitations under the License. */ +import { TRACER_ID, withActiveSpan } from '../util/opentelemetry'; +import { trace } from '@opentelemetry/api'; + const DEFAULT_POLLING_INTERVAL_MS = 1000; +const tracer = trace.getTracer(TRACER_ID); type Options = { /** @@ -86,33 +90,35 @@ export function startTaskPipeline(options: Options) { async function pipelineLoop() { while (!abortSignal.aborted) { if (state.inFlightCount <= lowWatermark) { - const loadCount = highWatermark - state.inFlightCount; - const loadedItems = await Promise.resolve() - .then(() => loadTasks(loadCount)) - .catch(() => { - // Silently swallow errors and go back to sleep to try again; we - // delegate to the loadTasks function itself to catch errors and log - // if it so desires - return []; - }); - if (loadedItems.length && !abortSignal.aborted) { - state.inFlightCount += loadedItems.length; - for (const item of loadedItems) { - Promise.resolve() - .then(() => processTask(item)) - .catch(() => { - // Silently swallow errors and go back to sleep to try again; we - // delegate to the processTask function itself to catch errors - // and log if it so desires - }) - .finally(() => { - state.inFlightCount -= 1; - barrier.release(); - }); + await withActiveSpan(tracer, 'TaskPipelineLoop', async span => { + const loadCount = highWatermark - state.inFlightCount; + const loadedItems = await Promise.resolve() + .then(() => loadTasks(loadCount)) + .catch(() => { + // Silently swallow errors and go back to sleep to try again; we + // delegate to the loadTasks function itself to catch errors and log + // if it so desires + return []; + }); + span.setAttribute('itemCount', loadedItems.length); + if (loadedItems.length && !abortSignal.aborted) { + state.inFlightCount += loadedItems.length; + for (const item of loadedItems) { + Promise.resolve() + .then(() => processTask(item)) + .catch(() => { + // Silently swallow errors and go back to sleep to try again; we + // delegate to the processTask function itself to catch errors + // and log if it so desires + }) + .finally(() => { + state.inFlightCount -= 1; + barrier.release(); + }); + } } - } + }); } - await barrier.wait(); } } diff --git a/plugins/catalog-backend/src/schema/openapi.generated.ts b/plugins/catalog-backend/src/schema/openapi.generated.ts index a31615dfc6..fe64c75373 100644 --- a/plugins/catalog-backend/src/schema/openapi.generated.ts +++ b/plugins/catalog-backend/src/schema/openapi.generated.ts @@ -36,6 +36,9 @@ export const spec = { { url: '/', }, + { + url: 'catalog', + }, ], components: { examples: {}, @@ -105,6 +108,7 @@ export const spec = { description: 'Restrict to just these fields in the response.', required: false, allowReserved: true, + explode: false, schema: { type: 'array', items: { @@ -248,12 +252,13 @@ export const spec = { }, }, required: ['error', 'response'], + additionalProperties: {}, }, JsonObject: { type: 'object', properties: {}, description: 'A type representing all allowed JSON object values.', - additionalProperties: true, + additionalProperties: {}, }, MapStringString: { type: 'object', @@ -291,70 +296,62 @@ export const spec = { additionalProperties: false, }, EntityMeta: { - allOf: [ - { - $ref: '#/components/schemas/JsonObject', - }, - { - type: 'object', - properties: { - links: { - type: 'array', - items: { - $ref: '#/components/schemas/EntityLink', - }, - description: - 'A list of external hyperlinks related to the entity.', - }, - tags: { - type: 'array', - items: { - type: 'string', - }, - description: - 'A list of single-valued strings, to for example classify catalog entities in\nvarious ways.', - }, - annotations: { - $ref: '#/components/schemas/MapStringString', - }, - labels: { - $ref: '#/components/schemas/MapStringString', - }, - description: { - type: 'string', - description: - 'A short (typically relatively few words, on one line) description of the\nentity.', - }, - title: { - type: 'string', - description: - 'A display name of the entity, to be presented in user interfaces instead\nof the `name` property above, when available.\nThis field is sometimes useful when the `name` is cumbersome or ends up\nbeing perceived as overly technical. The title generally does not have\nas stringent format requirements on it, so it may contain special\ncharacters and be more explanatory. Do keep it very short though, and\navoid situations where a title can be confused with the name of another\nentity, or where two entities share a title.\nNote that this is only for display purposes, and may be ignored by some\nparts of the code. Entity references still always make use of the `name`\nproperty, not the title.', - }, - namespace: { - type: 'string', - description: 'The namespace that the entity belongs to.', - }, - name: { - type: 'string', - description: - 'The name of the entity.\nMust be unique within the catalog at any given point in time, for any\ngiven namespace + kind pair. This value is part of the technical\nidentifier of the entity, and as such it will appear in URLs, database\ntables, entity references, and similar. It is subject to restrictions\nregarding what characters are allowed.\nIf you want to use a different, more human readable string with fewer\nrestrictions on it in user interfaces, see the `title` field below.', - }, - etag: { - type: 'string', - description: - 'An opaque string that changes for each update operation to any part of\nthe entity, including metadata.\nThis field can not be set by the user at creation time, and the server\nwill reject an attempt to do so. The field will be populated in read\noperations. The field can (optionally) be specified when performing\nupdate or delete operations, and the server will then reject the\noperation if it does not match the current stored value.', - }, - uid: { - type: 'string', - description: - 'A globally unique ID for the entity.\nThis field can not be set by the user at creation time, and the server\nwill reject an attempt to do so. The field will be populated in read\noperations. The field can (optionally) be specified when performing\nupdate or delete operations, but the server is free to reject requests\nthat do so in such a way that it breaks semantics.', - }, + type: 'object', + properties: { + links: { + type: 'array', + items: { + $ref: '#/components/schemas/EntityLink', }, - required: ['name'], + description: 'A list of external hyperlinks related to the entity.', }, - ], + tags: { + type: 'array', + items: { + type: 'string', + }, + description: + 'A list of single-valued strings, to for example classify catalog entities in\nvarious ways.', + }, + annotations: { + $ref: '#/components/schemas/MapStringString', + }, + labels: { + $ref: '#/components/schemas/MapStringString', + }, + description: { + type: 'string', + description: + 'A short (typically relatively few words, on one line) description of the\nentity.', + }, + title: { + type: 'string', + description: + 'A display name of the entity, to be presented in user interfaces instead\nof the `name` property above, when available.\nThis field is sometimes useful when the `name` is cumbersome or ends up\nbeing perceived as overly technical. The title generally does not have\nas stringent format requirements on it, so it may contain special\ncharacters and be more explanatory. Do keep it very short though, and\navoid situations where a title can be confused with the name of another\nentity, or where two entities share a title.\nNote that this is only for display purposes, and may be ignored by some\nparts of the code. Entity references still always make use of the `name`\nproperty, not the title.', + }, + namespace: { + type: 'string', + description: 'The namespace that the entity belongs to.', + }, + name: { + type: 'string', + description: + 'The name of the entity.\nMust be unique within the catalog at any given point in time, for any\ngiven namespace + kind pair. This value is part of the technical\nidentifier of the entity, and as such it will appear in URLs, database\ntables, entity references, and similar. It is subject to restrictions\nregarding what characters are allowed.\nIf you want to use a different, more human readable string with fewer\nrestrictions on it in user interfaces, see the `title` field below.', + }, + etag: { + type: 'string', + description: + 'An opaque string that changes for each update operation to any part of\nthe entity, including metadata.\nThis field can not be set by the user at creation time, and the server\nwill reject an attempt to do so. The field will be populated in read\noperations. The field can (optionally) be specified when performing\nupdate or delete operations, and the server will then reject the\noperation if it does not match the current stored value.', + }, + uid: { + type: 'string', + description: + 'A globally unique ID for the entity.\nThis field can not be set by the user at creation time, and the server\nwill reject an attempt to do so. The field will be populated in read\noperations. The field can (optionally) be specified when performing\nupdate or delete operations, but the server is free to reject requests\nthat do so in such a way that it breaks semantics.', + }, + }, + required: ['name'], description: 'Metadata fields common to all versions/kinds of entity.', - additionalProperties: true, + additionalProperties: {}, }, EntityRelation: { type: 'object', @@ -403,7 +400,6 @@ export const spec = { required: ['metadata', 'kind', 'apiVersion'], description: "The parts of the format that's common to all versions/kinds of entity.", - additionalProperties: true, }, NullableEntity: { type: 'object', @@ -435,7 +431,6 @@ export const spec = { required: ['metadata', 'kind', 'apiVersion'], description: "The parts of the format that's common to all versions/kinds of entity.", - additionalProperties: true, nullable: true, }, EntityAncestryResponse: { @@ -472,11 +467,7 @@ export const spec = { items: { type: 'array', items: { - anyOf: [ - { - $ref: '#/components/schemas/NullableEntity', - }, - ], + $ref: '#/components/schemas/NullableEntity', }, description: 'The list of entities, in the same order as the refs in the request. Entries\nthat are null signify that no entity existed with that ref.', @@ -495,6 +486,7 @@ export const spec = { type: 'number', }, }, + required: ['value', 'count'], additionalProperties: false, }, EntityFacetsResponse: { @@ -782,6 +774,7 @@ export const spec = { }, }, }, + required: ['items', 'totalItems', 'pageInfo'], additionalProperties: false, }, }, @@ -1076,11 +1069,6 @@ export const spec = { JWT: [], }, ], - parameters: [ - { - $ref: '#/components/parameters/fields', - }, - ], requestBody: { required: false, content: { @@ -1257,8 +1245,8 @@ export const spec = { operationId: 'CreateLocation', description: 'Create a location for a given target.', responses: { - '200': { - description: 'Ok', + '201': { + description: 'Created', content: { 'application/json': { schema: { @@ -1282,38 +1270,6 @@ export const spec = { }, }, }, - '201': { - description: '201 response', - content: { - 'application/json': { - schema: { - type: 'object', - properties: { - location: { - type: 'object', - properties: { - id: { - type: 'string', - }, - type: { - type: 'string', - }, - target: { - type: 'string', - }, - }, - required: ['id', 'type', 'target'], - }, - entities: { - type: 'array', - items: {}, - }, - }, - required: ['location', 'entities'], - }, - }, - }, - }, '400': { $ref: '#/components/responses/ErrorResponse', }, @@ -1524,7 +1480,7 @@ export const spec = { description: 'Ok', }, '400': { - description: '400 response', + description: 'Validation errors.', content: { 'application/json; charset=utf-8': { schema: { @@ -1543,6 +1499,7 @@ export const spec = { }, }, required: ['name', 'message'], + additionalProperties: {}, }, }, }, @@ -1571,7 +1528,7 @@ export const spec = { }, entity: { type: 'object', - additionalProperties: true, + additionalProperties: {}, }, }, required: ['location', 'entity'], diff --git a/plugins/catalog-backend/src/schema/openapi.yaml b/plugins/catalog-backend/src/schema/openapi.yaml index 3375f7fe15..d3ef718286 100644 --- a/plugins/catalog-backend/src/schema/openapi.yaml +++ b/plugins/catalog-backend/src/schema/openapi.yaml @@ -9,6 +9,7 @@ info: contact: {} servers: - url: / + - url: catalog components: examples: {} headers: {} @@ -65,6 +66,7 @@ components: description: Restrict to just these fields in the response. required: false allowReserved: true + explode: false schema: type: array items: @@ -180,11 +182,12 @@ components: required: - error - response + additionalProperties: {} JsonObject: type: object properties: {} description: A type representing all allowed JSON object values. - additionalProperties: true + additionalProperties: {} MapStringString: type: object properties: {} @@ -211,82 +214,80 @@ components: description: A link to external information that is related to the entity. additionalProperties: false EntityMeta: - allOf: - - $ref: '#/components/schemas/JsonObject' - - type: object - properties: - links: - type: array - items: - $ref: '#/components/schemas/EntityLink' - description: A list of external hyperlinks related to the entity. - tags: - type: array - items: - type: string - description: |- - A list of single-valued strings, to for example classify catalog entities in - various ways. - annotations: - $ref: '#/components/schemas/MapStringString' - labels: - $ref: '#/components/schemas/MapStringString' - description: - type: string - description: |- - A short (typically relatively few words, on one line) description of the - entity. - title: - type: string - description: |- - A display name of the entity, to be presented in user interfaces instead - of the `name` property above, when available. - This field is sometimes useful when the `name` is cumbersome or ends up - being perceived as overly technical. The title generally does not have - as stringent format requirements on it, so it may contain special - characters and be more explanatory. Do keep it very short though, and - avoid situations where a title can be confused with the name of another - entity, or where two entities share a title. - Note that this is only for display purposes, and may be ignored by some - parts of the code. Entity references still always make use of the `name` - property, not the title. - namespace: - type: string - description: The namespace that the entity belongs to. - name: - type: string - description: |- - The name of the entity. - Must be unique within the catalog at any given point in time, for any - given namespace + kind pair. This value is part of the technical - identifier of the entity, and as such it will appear in URLs, database - tables, entity references, and similar. It is subject to restrictions - regarding what characters are allowed. - If you want to use a different, more human readable string with fewer - restrictions on it in user interfaces, see the `title` field below. - etag: - type: string - description: |- - An opaque string that changes for each update operation to any part of - the entity, including metadata. - This field can not be set by the user at creation time, and the server - will reject an attempt to do so. The field will be populated in read - operations. The field can (optionally) be specified when performing - update or delete operations, and the server will then reject the - operation if it does not match the current stored value. - uid: - type: string - description: |- - A globally unique ID for the entity. - This field can not be set by the user at creation time, and the server - will reject an attempt to do so. The field will be populated in read - operations. The field can (optionally) be specified when performing - update or delete operations, but the server is free to reject requests - that do so in such a way that it breaks semantics. - required: - - name + type: object + properties: + links: + type: array + items: + $ref: '#/components/schemas/EntityLink' + description: A list of external hyperlinks related to the entity. + tags: + type: array + items: + type: string + description: |- + A list of single-valued strings, to for example classify catalog entities in + various ways. + annotations: + $ref: '#/components/schemas/MapStringString' + labels: + $ref: '#/components/schemas/MapStringString' + description: + type: string + description: |- + A short (typically relatively few words, on one line) description of the + entity. + title: + type: string + description: |- + A display name of the entity, to be presented in user interfaces instead + of the `name` property above, when available. + This field is sometimes useful when the `name` is cumbersome or ends up + being perceived as overly technical. The title generally does not have + as stringent format requirements on it, so it may contain special + characters and be more explanatory. Do keep it very short though, and + avoid situations where a title can be confused with the name of another + entity, or where two entities share a title. + Note that this is only for display purposes, and may be ignored by some + parts of the code. Entity references still always make use of the `name` + property, not the title. + namespace: + type: string + description: The namespace that the entity belongs to. + name: + type: string + description: |- + The name of the entity. + Must be unique within the catalog at any given point in time, for any + given namespace + kind pair. This value is part of the technical + identifier of the entity, and as such it will appear in URLs, database + tables, entity references, and similar. It is subject to restrictions + regarding what characters are allowed. + If you want to use a different, more human readable string with fewer + restrictions on it in user interfaces, see the `title` field below. + etag: + type: string + description: |- + An opaque string that changes for each update operation to any part of + the entity, including metadata. + This field can not be set by the user at creation time, and the server + will reject an attempt to do so. The field will be populated in read + operations. The field can (optionally) be specified when performing + update or delete operations, and the server will then reject the + operation if it does not match the current stored value. + uid: + type: string + description: |- + A globally unique ID for the entity. + This field can not be set by the user at creation time, and the server + will reject an attempt to do so. The field will be populated in read + operations. The field can (optionally) be specified when performing + update or delete operations, but the server is free to reject requests + that do so in such a way that it breaks semantics. + required: + - name description: Metadata fields common to all versions/kinds of entity. - additionalProperties: true + additionalProperties: {} EntityRelation: type: object properties: @@ -326,7 +327,6 @@ components: - kind - apiVersion description: The parts of the format that's common to all versions/kinds of entity. - additionalProperties: true NullableEntity: type: object properties: @@ -352,7 +352,6 @@ components: - kind - apiVersion description: The parts of the format that's common to all versions/kinds of entity. - additionalProperties: true nullable: true EntityAncestryResponse: type: object @@ -383,8 +382,7 @@ components: items: type: array items: - anyOf: - - $ref: '#/components/schemas/NullableEntity' + $ref: '#/components/schemas/NullableEntity' description: |- The list of entities, in the same order as the refs in the request. Entries that are null signify that no entity existed with that ref. @@ -398,6 +396,9 @@ components: type: string count: type: number + required: + - value + - count additionalProperties: false EntityFacetsResponse: type: object @@ -660,6 +661,10 @@ components: prevCursor: type: string description: The cursor for the previous batch of entities. + required: + - items + - totalItems + - pageInfo additionalProperties: false securitySchemes: JWT: @@ -829,8 +834,6 @@ paths: security: - {} - JWT: [] - parameters: - - $ref: '#/components/parameters/fields' requestBody: required: false content: @@ -942,8 +945,8 @@ paths: operationId: CreateLocation description: Create a location for a given target. responses: - '200': - description: Ok + '201': + description: Created content: application/json: schema: @@ -960,32 +963,6 @@ paths: required: - entities - location - '201': - description: 201 response - content: - application/json: - schema: - type: object - properties: - location: - type: object - properties: - id: - type: string - type: - type: string - target: - type: string - required: - - id - - type - - target - entities: - type: array - items: {} - required: - - location - - entities '400': $ref: '#/components/responses/ErrorResponse' default: @@ -1120,7 +1097,7 @@ paths: '200': description: Ok '400': - description: 400 response + description: Validation errors. content: application/json; charset=utf-8: schema: @@ -1138,6 +1115,7 @@ paths: required: - name - message + additionalProperties: {} required: - errors security: @@ -1155,7 +1133,7 @@ paths: type: string entity: type: object - additionalProperties: true + additionalProperties: {} required: - location - entity diff --git a/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.test.ts b/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.test.ts index 2ee47dae19..ce9221e16e 100644 --- a/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.test.ts +++ b/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.test.ts @@ -20,8 +20,9 @@ import { createConditionTransformer } from '@backstage/plugin-permission-node'; import { isEntityKind } from '../permissions/rules/isEntityKind'; import { CatalogPermissionRule } from '../permissions/rules'; import { AuthorizedEntitiesCatalog } from './AuthorizedEntitiesCatalog'; -import { Cursor, EntityFilter, QueryEntitiesResponse } from '../catalog/types'; +import { Cursor, QueryEntitiesResponse } from '../catalog/types'; import { Entity } from '@backstage/catalog-model'; +import { EntityFilter } from '@backstage/plugin-catalog-node'; describe('AuthorizedEntitiesCatalog', () => { const fakeCatalog = { diff --git a/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.ts b/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.ts index 0ce1ee87e7..6a04c4d7a1 100644 --- a/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.ts +++ b/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.ts @@ -35,12 +35,12 @@ import { EntityAncestryResponse, EntityFacetsRequest, EntityFacetsResponse, - EntityFilter, QueryEntitiesRequest, QueryEntitiesResponse, } from '../catalog/types'; import { basicEntityFilter } from './request/basicEntityFilter'; import { isQueryEntitiesCursorRequest } from './util'; +import { EntityFilter } from '@backstage/plugin-catalog-node'; export class AuthorizedEntitiesCatalog implements EntitiesCatalog { constructor( diff --git a/plugins/catalog-backend/src/service/CatalogBuilder.ts b/plugins/catalog-backend/src/service/CatalogBuilder.ts index 5cf76ee1ac..f8ed166118 100644 --- a/plugins/catalog-backend/src/service/CatalogBuilder.ts +++ b/plugins/catalog-backend/src/service/CatalogBuilder.ts @@ -37,6 +37,7 @@ import lodash, { keyBy } from 'lodash'; import { CatalogProcessor, CatalogProcessorParser, + EntitiesSearchFilter, EntityProvider, ScmLocationAnalyzer, } from '@backstage/plugin-catalog-node'; @@ -79,7 +80,6 @@ import { Config, readDurationFromConfig } from '@backstage/config'; import { Logger } from 'winston'; import { connectEntityProviders } from '../processing/connectEntityProviders'; import { PermissionRuleParams } from '@backstage/plugin-permission-common'; -import { EntitiesSearchFilter } from '../catalog/types'; import { permissionRules as catalogPermissionRules } from '../permissions/rules'; import { PermissionRule } from '@backstage/plugin-permission-node'; import { diff --git a/plugins/catalog-backend/src/service/CatalogPlugin.ts b/plugins/catalog-backend/src/service/CatalogPlugin.ts index fa53ef510b..0800d41340 100644 --- a/plugins/catalog-backend/src/service/CatalogPlugin.ts +++ b/plugins/catalog-backend/src/service/CatalogPlugin.ts @@ -17,12 +17,14 @@ import { createBackendPlugin, coreServices, } from '@backstage/backend-plugin-api'; -import { CatalogBuilder } from './CatalogBuilder'; +import { CatalogBuilder, CatalogPermissionRuleInput } from './CatalogBuilder'; import { CatalogAnalysisExtensionPoint, catalogAnalysisExtensionPoint, CatalogProcessingExtensionPoint, catalogProcessingExtensionPoint, + CatalogPermissionExtensionPoint, + catalogPermissionExtensionPoint, } from '@backstage/plugin-catalog-node/alpha'; import { CatalogProcessor, @@ -86,6 +88,24 @@ class CatalogAnalysisExtensionPointImpl } } +class CatalogPermissionExtensionPointImpl + implements CatalogPermissionExtensionPoint +{ + #permissionRules = new Array(); + + addPermissionRules( + ...rules: Array< + CatalogPermissionRuleInput | Array + > + ): void { + this.#permissionRules.push(...rules.flat()); + } + + get permissionRules() { + return this.#permissionRules; + } +} + /** * Catalog plugin * @alpha @@ -106,6 +126,12 @@ export const catalogPlugin = createBackendPlugin({ analysisExtensions, ); + const permissionExtensions = new CatalogPermissionExtensionPointImpl(); + env.registerExtensionPoint( + catalogPermissionExtensionPoint, + permissionExtensions, + ); + env.registerInit({ deps: { logger: coreServices.logger, @@ -142,6 +168,7 @@ export const catalogPlugin = createBackendPlugin({ ([key, resolver]) => builder.setPlaceholderResolver(key, resolver), ); builder.addLocationAnalyzers(...analysisExtensions.locationAnalyzers); + builder.addPermissionRules(...permissionExtensions.permissionRules); const { processingEngine, router } = await builder.build(); diff --git a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts index 24f0432419..0245fe1d4e 100644 --- a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts +++ b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts @@ -31,11 +31,9 @@ import { EntitiesCatalog, EntitiesRequest, EntitiesResponse, - EntitiesSearchFilter, EntityAncestryResponse, EntityFacetsRequest, EntityFacetsResponse, - EntityFilter, EntityPagination, QueryEntitiesRequest, QueryEntitiesResponse, @@ -55,6 +53,10 @@ import { isQueryEntitiesCursorRequest, isQueryEntitiesInitialRequest, } from './util'; +import { + EntitiesSearchFilter, + EntityFilter, +} from '@backstage/plugin-catalog-node'; const defaultSortField: EntityOrder = { field: 'metadata.uid', diff --git a/plugins/catalog-backend/src/service/createRouter.test.ts b/plugins/catalog-backend/src/service/createRouter.test.ts index 700e35edc1..05ebf6e1a9 100644 --- a/plugins/catalog-backend/src/service/createRouter.test.ts +++ b/plugins/catalog-backend/src/service/createRouter.test.ts @@ -198,6 +198,45 @@ describe('createRouter readonly disabled', () => { }); }); + it('parses encoded params request', async () => { + entitiesCatalog.queryEntities.mockResolvedValueOnce({ + items: [], + pageInfo: {}, + totalItems: 0, + }); + const response = await request(app).get( + `/entities/by-query?filter=${encodeURIComponent( + 'a=1,a=2,b=3', + )}&filter=c=4&orderField=${encodeURIComponent( + 'metadata.name,asc', + )}&orderField=metadata.uid,desc`, + ); + + expect(response.status).toEqual(200); + expect(entitiesCatalog.queryEntities).toHaveBeenCalledTimes(1); + expect(entitiesCatalog.queryEntities).toHaveBeenCalledWith({ + filter: { + anyOf: [ + { + allOf: [ + { key: 'a', values: ['1', '2'] }, + { key: 'b', values: ['3'] }, + ], + }, + { allOf: [{ key: 'c', values: ['4'] }] }, + ], + }, + orderFields: [ + { field: 'metadata.name', order: 'asc' }, + { field: 'metadata.uid', order: 'desc' }, + ], + fullTextFilter: { + fields: undefined, + term: '', + }, + }); + }); + it('parses cursor request', async () => { const items: Entity[] = [ { apiVersion: 'a', kind: 'b', metadata: { name: 'n' } }, diff --git a/plugins/catalog-backend/src/service/request/basicEntityFilter.ts b/plugins/catalog-backend/src/service/request/basicEntityFilter.ts index 36448e0304..43824160a2 100644 --- a/plugins/catalog-backend/src/service/request/basicEntityFilter.ts +++ b/plugins/catalog-backend/src/service/request/basicEntityFilter.ts @@ -14,7 +14,10 @@ * limitations under the License. */ -import { EntitiesSearchFilter, EntityFilter } from '../../catalog'; +import { + EntitiesSearchFilter, + EntityFilter, +} from '@backstage/plugin-catalog-node'; /** * Forms a full EntityFilter based on a single key-value(s) object. diff --git a/plugins/catalog-backend/src/service/request/parseEntityFilterParams.ts b/plugins/catalog-backend/src/service/request/parseEntityFilterParams.ts index 767a36ce71..cfd9aa0aa5 100644 --- a/plugins/catalog-backend/src/service/request/parseEntityFilterParams.ts +++ b/plugins/catalog-backend/src/service/request/parseEntityFilterParams.ts @@ -15,8 +15,11 @@ */ import { InputError } from '@backstage/errors'; -import { EntitiesSearchFilter, EntityFilter } from '../../catalog'; import { parseStringsParam } from './common'; +import { + EntitiesSearchFilter, + EntityFilter, +} from '@backstage/plugin-catalog-node'; /** * Parses the filtering part of a query, like diff --git a/plugins/catalog-backend/src/service/util.ts b/plugins/catalog-backend/src/service/util.ts index 699bca2fc9..af1279a59a 100644 --- a/plugins/catalog-backend/src/service/util.ts +++ b/plugins/catalog-backend/src/service/util.ts @@ -20,11 +20,11 @@ import lodash from 'lodash'; import { z } from 'zod'; import { Cursor, - EntityFilter, QueryEntitiesCursorRequest, QueryEntitiesInitialRequest, QueryEntitiesRequest, } from '../catalog/types'; +import { EntityFilter } from '@backstage/plugin-catalog-node'; export async function requireRequestBody(req: Request): Promise { const contentType = req.header('content-type'); diff --git a/plugins/catalog-backend/src/tests/performance/stitchingPerformance.test.ts b/plugins/catalog-backend/src/tests/performance/stitchingPerformance.test.ts index ad659ce845..7902a5e230 100644 --- a/plugins/catalog-backend/src/tests/performance/stitchingPerformance.test.ts +++ b/plugins/catalog-backend/src/tests/performance/stitchingPerformance.test.ts @@ -188,8 +188,8 @@ describePerformanceTest('stitchingPerformance', () => { factory: () => ({ getClient: async () => knex }), }), createBackendModule({ - moduleId: 'syntheticLoadEntities', pluginId: 'catalog', + moduleId: 'synthetic-load-entities', register(reg) { reg.registerInit({ deps: { @@ -245,8 +245,8 @@ describePerformanceTest('stitchingPerformance', () => { factory: () => ({ getClient: async () => knex }), }), createBackendModule({ - moduleId: 'syntheticLoadEntities', pluginId: 'catalog', + moduleId: 'synthetic-load-entities', register(reg) { reg.registerInit({ deps: { diff --git a/plugins/catalog-common/alpha-api-report.md b/plugins/catalog-common/api-report-alpha.md similarity index 100% rename from plugins/catalog-common/alpha-api-report.md rename to plugins/catalog-common/api-report-alpha.md diff --git a/plugins/catalog-graph/CHANGELOG.md b/plugins/catalog-graph/CHANGELOG.md index 7c5667aa3b..a91217563d 100644 --- a/plugins/catalog-graph/CHANGELOG.md +++ b/plugins/catalog-graph/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-catalog-graph +## 0.3.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-client@1.5.0-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/theme@0.5.0-next.0 + - @backstage/types@1.1.1 + ## 0.3.2-next.0 ### Patch Changes diff --git a/plugins/catalog-graph/package.json b/plugins/catalog-graph/package.json index a8be8619a4..2552f38f8a 100644 --- a/plugins/catalog-graph/package.json +++ b/plugins/catalog-graph/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-graph", - "version": "0.3.2-next.0", + "version": "0.3.2-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-import/CHANGELOG.md b/plugins/catalog-import/CHANGELOG.md index b5e55fa324..347d787911 100644 --- a/plugins/catalog-import/CHANGELOG.md +++ b/plugins/catalog-import/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-catalog-import +## 0.10.4-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.4.0-next.1 + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-client@1.5.0-next.0 + - @backstage/integration@1.8.0-next.1 + - @backstage/core-compat-api@0.0.1-next.1 + - @backstage/integration-react@1.1.22-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-catalog-common@1.0.18 + ## 0.10.4-next.1 ### Patch Changes diff --git a/plugins/catalog-import/alpha-api-report.md b/plugins/catalog-import/api-report-alpha.md similarity index 100% rename from plugins/catalog-import/alpha-api-report.md rename to plugins/catalog-import/api-report-alpha.md diff --git a/plugins/catalog-import/package.json b/plugins/catalog-import/package.json index 23e0ea6755..be0793b026 100644 --- a/plugins/catalog-import/package.json +++ b/plugins/catalog-import/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-import", "description": "A Backstage plugin the helps you import entities into your catalog", - "version": "0.10.4-next.1", + "version": "0.10.4-next.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-import/src/alpha.tsx b/plugins/catalog-import/src/alpha.tsx index 18ddda2ceb..7ecbac5346 100644 --- a/plugins/catalog-import/src/alpha.tsx +++ b/plugins/catalog-import/src/alpha.tsx @@ -38,7 +38,6 @@ import { catalogApiRef } from '@backstage/plugin-catalog-react'; // TODO: It's currently possible to override the import page with a custom one. We need to decide // whether this type of override is typically done with an input or by overriding the entire extension. const CatalogImportPageExtension = createPageExtension({ - id: 'plugin.catalog-import.page', defaultPath: '/catalog-import', routeRef: convertLegacyRouteRef(rootRouteRef), loader: () => import('./components/ImportPage').then(m => ), diff --git a/plugins/catalog-node/CHANGELOG.md b/plugins/catalog-node/CHANGELOG.md index 20e5462e95..175d4c33c0 100644 --- a/plugins/catalog-node/CHANGELOG.md +++ b/plugins/catalog-node/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-catalog-node +## 1.5.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.5.0-next.0 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.18 + ## 1.5.1-next.0 ### Patch Changes diff --git a/plugins/catalog-node/alpha-api-report.md b/plugins/catalog-node/api-report-alpha.md similarity index 65% rename from plugins/catalog-node/alpha-api-report.md rename to plugins/catalog-node/api-report-alpha.md index c4dc6b12e1..74339b1bc9 100644 --- a/plugins/catalog-node/alpha-api-report.md +++ b/plugins/catalog-node/api-report-alpha.md @@ -5,8 +5,12 @@ ```ts import { CatalogApi } from '@backstage/catalog-client'; import { CatalogProcessor } from '@backstage/plugin-catalog-node'; +import { EntitiesSearchFilter } from '@backstage/plugin-catalog-node'; +import { Entity } from '@backstage/catalog-model'; import { EntityProvider } from '@backstage/plugin-catalog-node'; import { ExtensionPoint } from '@backstage/backend-plugin-api'; +import { PermissionRule } from '@backstage/plugin-permission-node'; +import { PermissionRuleParams } from '@backstage/plugin-permission-common'; import { PlaceholderResolver } from '@backstage/plugin-catalog-node'; import { ScmLocationAnalyzer } from '@backstage/plugin-catalog-node'; import { ServiceRef } from '@backstage/backend-plugin-api'; @@ -20,6 +24,24 @@ export interface CatalogAnalysisExtensionPoint { // @alpha (undocumented) export const catalogAnalysisExtensionPoint: ExtensionPoint; +// @alpha (undocumented) +export interface CatalogPermissionExtensionPoint { + // (undocumented) + addPermissionRules( + ...rules: Array< + CatalogPermissionRuleInput | Array + > + ): void; +} + +// @alpha (undocumented) +export const catalogPermissionExtensionPoint: ExtensionPoint; + +// @alpha (undocumented) +export type CatalogPermissionRuleInput< + TParams extends PermissionRuleParams = PermissionRuleParams, +> = PermissionRule; + // @alpha (undocumented) export interface CatalogProcessingExtensionPoint { // (undocumented) diff --git a/plugins/catalog-node/api-report.md b/plugins/catalog-node/api-report.md index ef2c09e61f..a4ba95073f 100644 --- a/plugins/catalog-node/api-report.md +++ b/plugins/catalog-node/api-report.md @@ -105,6 +105,25 @@ export type DeferredEntity = { locationKey?: string; }; +// @public +export type EntitiesSearchFilter = { + key: string; + values?: string[]; +}; + +// @public +export type EntityFilter = + | { + allOf: EntityFilter[]; + } + | { + anyOf: EntityFilter[]; + } + | { + not: EntityFilter; + } + | EntitiesSearchFilter; + // @public export interface EntityProvider { connect(connection: EntityProviderConnection): Promise; diff --git a/plugins/catalog-node/package.json b/plugins/catalog-node/package.json index f07ffd2c91..85ca35831c 100644 --- a/plugins/catalog-node/package.json +++ b/plugins/catalog-node/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-node", "description": "The plugin-catalog-node module for @backstage/plugin-catalog-backend", - "version": "1.5.1-next.0", + "version": "1.5.1-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -47,6 +47,8 @@ "@backstage/catalog-model": "workspace:^", "@backstage/errors": "workspace:^", "@backstage/plugin-catalog-common": "workspace:^", + "@backstage/plugin-permission-common": "workspace:^", + "@backstage/plugin-permission-node": "workspace:^", "@backstage/types": "workspace:^" }, "devDependencies": { diff --git a/plugins/catalog-node/src/alpha.ts b/plugins/catalog-node/src/alpha.ts index cec1a0eedb..3e958d1df1 100644 --- a/plugins/catalog-node/src/alpha.ts +++ b/plugins/catalog-node/src/alpha.ts @@ -19,3 +19,6 @@ export type { CatalogProcessingExtensionPoint } from './extensions'; export { catalogProcessingExtensionPoint } from './extensions'; export type { CatalogAnalysisExtensionPoint } from './extensions'; export { catalogAnalysisExtensionPoint } from './extensions'; +export type { CatalogPermissionRuleInput } from './extensions'; +export type { CatalogPermissionExtensionPoint } from './extensions'; +export { catalogPermissionExtensionPoint } from './extensions'; diff --git a/plugins/catalog-node/src/api/common.ts b/plugins/catalog-node/src/api/common.ts index c91aeca2d9..7f9bd3eabf 100644 --- a/plugins/catalog-node/src/api/common.ts +++ b/plugins/catalog-node/src/api/common.ts @@ -52,3 +52,36 @@ export type EntityRelationSpec = { */ target: CompoundEntityRef; }; + +/** + * A filter expression for entities. + * + * @public + */ +export type EntityFilter = + | { allOf: EntityFilter[] } + | { anyOf: EntityFilter[] } + | { not: EntityFilter } + | EntitiesSearchFilter; + +/** + * Matches rows in the search table. + * + * @public + */ +export type EntitiesSearchFilter = { + /** + * The key to match on. + * + * Matches are always case insensitive. + */ + key: string; + + /** + * Match on plain equality of values. + * + * Match on values that are equal to any of the given array items. Matches are + * always case insensitive. + */ + values?: string[]; +}; diff --git a/plugins/catalog-node/src/api/index.ts b/plugins/catalog-node/src/api/index.ts index 66e8d6bd21..292a6cd872 100644 --- a/plugins/catalog-node/src/api/index.ts +++ b/plugins/catalog-node/src/api/index.ts @@ -15,7 +15,12 @@ */ export { processingResult } from './processingResult'; -export type { EntityRelationSpec, LocationSpec } from './common'; +export type { + EntityRelationSpec, + LocationSpec, + EntitiesSearchFilter, + EntityFilter, +} from './common'; export type { CatalogProcessor, CatalogProcessorParser, diff --git a/plugins/catalog-node/src/extensions.ts b/plugins/catalog-node/src/extensions.ts index 247ef142ad..7aa9264030 100644 --- a/plugins/catalog-node/src/extensions.ts +++ b/plugins/catalog-node/src/extensions.ts @@ -13,13 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import { createExtensionPoint } from '@backstage/backend-plugin-api'; +import { Entity } from '@backstage/catalog-model'; import { - EntityProvider, CatalogProcessor, + EntitiesSearchFilter, + EntityProvider, PlaceholderResolver, ScmLocationAnalyzer, } from '@backstage/plugin-catalog-node'; +import { PermissionRuleParams } from '@backstage/plugin-permission-common'; +import { PermissionRule } from '@backstage/plugin-permission-node'; /** * @alpha @@ -56,3 +61,29 @@ export const catalogAnalysisExtensionPoint = createExtensionPoint({ id: 'catalog.analysis', }); + +/** + * @alpha + */ +export type CatalogPermissionRuleInput< + TParams extends PermissionRuleParams = PermissionRuleParams, +> = PermissionRule; + +/** + * @alpha + */ +export interface CatalogPermissionExtensionPoint { + addPermissionRules( + ...rules: Array< + CatalogPermissionRuleInput | Array + > + ): void; +} + +/** + * @alpha + */ +export const catalogPermissionExtensionPoint = + createExtensionPoint({ + id: 'catalog.permission', + }); diff --git a/plugins/catalog-react/CHANGELOG.md b/plugins/catalog-react/CHANGELOG.md index 7bbbc00716..c4618fc659 100644 --- a/plugins/catalog-react/CHANGELOG.md +++ b/plugins/catalog-react/CHANGELOG.md @@ -1,5 +1,31 @@ # @backstage/plugin-catalog-react +## 1.9.2-next.1 + +### Patch Changes + +- 53600976bb: Ensure that passed-in icons are taken advantage of in the presentation API +- 08d9e67199: Add default icon for kind resource. +- a5a04739e1: Internal refactor of alpha exports due to a change in how extension factories are defined. +- e223f2264d: Breaking alpha-API change to entity visibility filter functions to accept a bare entity as their first argument, instead of an object with an entity property. + + Functions that accept such filters now also support the string expression form of filters. + +- Updated dependencies + - @backstage/frontend-plugin-api@0.4.0-next.1 + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/catalog-client@1.5.0-next.0 + - @backstage/integration-react@1.1.22-next.1 + - @backstage/plugin-permission-react@0.4.18-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.7 + - @backstage/plugin-catalog-common@1.0.18 + - @backstage/plugin-permission-common@0.7.10 + ## 1.9.2-next.0 ### Patch Changes diff --git a/plugins/catalog-react/alpha-api-report.md b/plugins/catalog-react/api-report-alpha.md similarity index 67% rename from plugins/catalog-react/alpha-api-report.md rename to plugins/catalog-react/api-report-alpha.md index ed851f810a..27ca640bbc 100644 --- a/plugins/catalog-react/alpha-api-report.md +++ b/plugins/catalog-react/api-report-alpha.md @@ -8,8 +8,8 @@ import { AnyExtensionInputMap } from '@backstage/frontend-plugin-api'; import { ConfigurableExtensionDataRef } from '@backstage/frontend-plugin-api'; import { Entity } from '@backstage/catalog-model'; -import { Extension } from '@backstage/frontend-plugin-api'; -import { ExtensionInputValues } from '@backstage/frontend-plugin-api'; +import { ExtensionDefinition } from '@backstage/frontend-plugin-api'; +import { ResolvedExtensionInputs } from '@backstage/frontend-plugin-api'; import { ResourcePermission } from '@backstage/plugin-permission-common'; import { RouteRef } from '@backstage/frontend-plugin-api'; @@ -17,31 +17,30 @@ import { RouteRef } from '@backstage/frontend-plugin-api'; export function createEntityCardExtension< TInputs extends AnyExtensionInputMap, >(options: { - id: string; + namespace?: string; + name?: string; attachTo?: { id: string; input: string; }; disabled?: boolean; inputs?: TInputs; - filter?: (ctx: { entity: Entity }) => boolean; - loader: (options: { - inputs: Expand>; - }) => Promise; -}): Extension<{ filter?: - | { - isKind?: string | undefined; - isType?: string | undefined; - }[] - | undefined; + | typeof entityFilterFunctionExtensionDataRef.T + | typeof entityFilterExpressionExtensionDataRef.T; + loader: (options: { + inputs: Expand>; + }) => Promise; +}): ExtensionDefinition<{ + filter?: string | undefined; }>; // @alpha (undocumented) export function createEntityContentExtension< TInputs extends AnyExtensionInputMap, >(options: { - id: string; + namespace?: string; + name?: string; attachTo?: { id: string; input: string; @@ -51,19 +50,16 @@ export function createEntityContentExtension< routeRef?: RouteRef; defaultPath: string; defaultTitle: string; - filter?: (ctx: { entity: Entity }) => boolean; + filter?: + | typeof entityFilterFunctionExtensionDataRef.T + | typeof entityFilterExpressionExtensionDataRef.T; loader: (options: { - inputs: Expand>; + inputs: Expand>; }) => Promise; -}): Extension<{ +}): ExtensionDefinition<{ title: string; path: string; - filter?: - | { - isKind?: string | undefined; - isType?: string | undefined; - }[] - | undefined; + filter?: string | undefined; }>; // @alpha (undocumented) @@ -73,8 +69,14 @@ export const entityContentTitleExtensionDataRef: ConfigurableExtensionDataRef< >; // @alpha (undocumented) -export const entityFilterExtensionDataRef: ConfigurableExtensionDataRef< - (ctx: { entity: Entity }) => boolean, +export const entityFilterExpressionExtensionDataRef: ConfigurableExtensionDataRef< + string, + {} +>; + +// @alpha (undocumented) +export const entityFilterFunctionExtensionDataRef: ConfigurableExtensionDataRef< + (entity: Entity) => boolean, {} >; diff --git a/plugins/catalog-react/package.json b/plugins/catalog-react/package.json index 1017cbf70b..4869b44cb8 100644 --- a/plugins/catalog-react/package.json +++ b/plugins/catalog-react/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-react", "description": "A frontend library that helps other Backstage plugins interact with the catalog", - "version": "1.9.2-next.0", + "version": "1.9.2-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-react/src/alpha.tsx b/plugins/catalog-react/src/alpha.tsx index a339816742..8f7144681b 100644 --- a/plugins/catalog-react/src/alpha.tsx +++ b/plugins/catalog-react/src/alpha.tsx @@ -14,103 +14,72 @@ * limitations under the License. */ -import React, { lazy } from 'react'; import { AnyExtensionInputMap, ExtensionBoundary, - ExtensionInputValues, + ResolvedExtensionInputs, RouteRef, coreExtensionData, createExtension, createExtensionDataRef, createSchemaFromZod, } from '@backstage/frontend-plugin-api'; +import React, { lazy } from 'react'; +import { Entity } from '@backstage/catalog-model'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports import { Expand } from '../../../packages/frontend-plugin-api/src/types'; -import { Entity } from '@backstage/catalog-model'; -export { isOwnerOf } from './utils'; export { useEntityPermission } from './hooks/useEntityPermission'; +export { isOwnerOf } from './utils'; /** @alpha */ export const entityContentTitleExtensionDataRef = createExtensionDataRef('plugin.catalog.entity.content.title'); /** @alpha */ -export const entityFilterExtensionDataRef = createExtensionDataRef< - (ctx: { entity: Entity }) => boolean ->('plugin.catalog.entity.filter'); +export const entityFilterFunctionExtensionDataRef = createExtensionDataRef< + (entity: Entity) => boolean +>('plugin.catalog.entity.filter.fn'); -function applyFilter(a?: string, b?: string): boolean { - if (!a) { - return true; - } - return a.toLocaleLowerCase('en-US') === b?.toLocaleLowerCase('en-US'); -} - -// TODO: Only two hardcoded isKind and isType filters are available for now -// This is just an initial config filter implementation and needs to be revisited -function buildFilter( - config: { filter?: { isKind?: string; isType?: string }[] }, - filterFunc?: (ctx: { entity: Entity }) => boolean, -) { - return (ctx: { entity: Entity }) => { - const configuredFilterMatch = config.filter?.some(filter => { - const kindMatch = applyFilter(filter.isKind, ctx.entity.kind); - const typeMatch = applyFilter( - filter.isType, - ctx.entity.spec?.type?.toString(), - ); - return kindMatch && typeMatch; - }); - if (configuredFilterMatch) { - return true; - } - if (filterFunc) { - return filterFunc(ctx); - } - return true; - }; -} +/** @alpha */ +export const entityFilterExpressionExtensionDataRef = + createExtensionDataRef('plugin.catalog.entity.filter.expression'); // TODO: Figure out how to merge with provided config schema /** @alpha */ export function createEntityCardExtension< TInputs extends AnyExtensionInputMap, >(options: { - id: string; + namespace?: string; + name?: string; attachTo?: { id: string; input: string }; disabled?: boolean; inputs?: TInputs; - filter?: (ctx: { entity: Entity }) => boolean; + filter?: + | typeof entityFilterFunctionExtensionDataRef.T + | typeof entityFilterExpressionExtensionDataRef.T; loader: (options: { - inputs: Expand>; + inputs: Expand>; }) => Promise; }) { - const id = `entity.cards.${options.id}`; - return createExtension({ - id, + kind: 'entity-card', + namespace: options.namespace, + name: options.name, attachTo: options.attachTo ?? { - id: 'entity.content.overview', + id: 'entity-content:catalog/overview', input: 'cards', }, disabled: options.disabled ?? true, output: { element: coreExtensionData.reactElement, - filter: entityFilterExtensionDataRef, + filterFunction: entityFilterFunctionExtensionDataRef.optional(), + filterExpression: entityFilterExpressionExtensionDataRef.optional(), }, inputs: options.inputs, configSchema: createSchemaFromZod(z => z.object({ - filter: z - .array( - z.object({ - isKind: z.string().optional(), - isType: z.string().optional(), - }), - ) - .optional(), + filter: z.string().optional(), }), ), factory({ config, inputs, node }) { @@ -126,7 +95,7 @@ export function createEntityCardExtension< ), - filter: buildFilter(config, options.filter), + ...mergeFilters({ config, options }), }; }, }); @@ -136,24 +105,27 @@ export function createEntityCardExtension< export function createEntityContentExtension< TInputs extends AnyExtensionInputMap, >(options: { - id: string; + namespace?: string; + name?: string; attachTo?: { id: string; input: string }; disabled?: boolean; inputs?: TInputs; routeRef?: RouteRef; defaultPath: string; defaultTitle: string; - filter?: (ctx: { entity: Entity }) => boolean; + filter?: + | typeof entityFilterFunctionExtensionDataRef.T + | typeof entityFilterExpressionExtensionDataRef.T; loader: (options: { - inputs: Expand>; + inputs: Expand>; }) => Promise; }) { - const id = `entity.content.${options.id}`; - return createExtension({ - id, + kind: 'entity-content', + namespace: options.namespace, + name: options.name, attachTo: options.attachTo ?? { - id: 'plugin.catalog.page.entity', + id: 'page:catalog/entity', input: 'contents', }, disabled: options.disabled ?? true, @@ -162,21 +134,15 @@ export function createEntityContentExtension< path: coreExtensionData.routePath, routeRef: coreExtensionData.routeRef.optional(), title: entityContentTitleExtensionDataRef, - filter: entityFilterExtensionDataRef, + filterFunction: entityFilterFunctionExtensionDataRef.optional(), + filterExpression: entityFilterExpressionExtensionDataRef.optional(), }, inputs: options.inputs, configSchema: createSchemaFromZod(z => z.object({ path: z.string().default(options.defaultPath), title: z.string().default(options.defaultTitle), - filter: z - .array( - z.object({ - isKind: z.string().optional(), - isType: z.string().optional(), - }), - ) - .optional(), + filter: z.string().optional(), }), ), factory({ config, inputs, node }) { @@ -195,8 +161,35 @@ export function createEntityContentExtension< ), - filter: buildFilter(config, options.filter), + ...mergeFilters({ config, options }), }; }, }); } + +/** + * Decides what filter outputs to produce, given some options and config + */ +function mergeFilters(inputs: { + options: { + filter?: + | typeof entityFilterFunctionExtensionDataRef.T + | typeof entityFilterExpressionExtensionDataRef.T; + }; + config: { + filter?: string; + }; +}): { + filterFunction?: typeof entityFilterFunctionExtensionDataRef.T; + filterExpression?: typeof entityFilterExpressionExtensionDataRef.T; +} { + const { options, config } = inputs; + if (config.filter) { + return { filterExpression: config.filter }; + } else if (typeof options.filter === 'string') { + return { filterExpression: options.filter }; + } else if (typeof options.filter === 'function') { + return { filterFunction: options.filter }; + } + return {}; +} diff --git a/plugins/catalog-react/src/apis/EntityPresentationApi/defaultEntityPresentation.test.ts b/plugins/catalog-react/src/apis/EntityPresentationApi/defaultEntityPresentation.test.ts index ea755dfebc..5985a50b0d 100644 --- a/plugins/catalog-react/src/apis/EntityPresentationApi/defaultEntityPresentation.test.ts +++ b/plugins/catalog-react/src/apis/EntityPresentationApi/defaultEntityPresentation.test.ts @@ -37,7 +37,7 @@ describe('defaultEntityPresentation', () => { entityRef: 'component:default/test', primaryTitle: 'test', secondaryTitle: 'component:default/test | type | desc', - Icon: expect.anything(), + Icon: undefined, }); expect( @@ -58,7 +58,7 @@ describe('defaultEntityPresentation', () => { entityRef: 'component:default/test', primaryTitle: 'title', secondaryTitle: 'component:default/test | type | desc', - Icon: expect.anything(), + Icon: undefined, }); expect( @@ -82,7 +82,7 @@ describe('defaultEntityPresentation', () => { entityRef: 'component:default/test', primaryTitle: 'displayName', secondaryTitle: 'component:default/test | type | desc', - Icon: expect.anything(), + Icon: undefined, }); }); @@ -96,7 +96,7 @@ describe('defaultEntityPresentation', () => { entityRef: 'component:default/test', primaryTitle: 'test', secondaryTitle: 'component:default/test', - Icon: expect.anything(), + Icon: undefined, }); }); @@ -107,7 +107,7 @@ describe('defaultEntityPresentation', () => { entityRef: 'unknown:default/unknown', primaryTitle: 'unknown', secondaryTitle: 'unknown:default/unknown', - Icon: expect.anything(), + Icon: undefined, }); }); }); @@ -118,7 +118,7 @@ describe('defaultEntityPresentation', () => { entityRef: 'component:default/test', primaryTitle: 'test', secondaryTitle: 'component:default/test', - Icon: expect.anything(), + Icon: undefined, }); expect( @@ -129,7 +129,7 @@ describe('defaultEntityPresentation', () => { entityRef: 'component:default/test', primaryTitle: 'component:test', secondaryTitle: 'component:default/test', - Icon: expect.anything(), + Icon: undefined, }); expect( @@ -140,7 +140,7 @@ describe('defaultEntityPresentation', () => { entityRef: 'component:default/test', primaryTitle: 'default/test', secondaryTitle: 'component:default/test', - Icon: expect.anything(), + Icon: undefined, }); }); @@ -149,14 +149,14 @@ describe('defaultEntityPresentation', () => { entityRef: 'unknown:default/unknown', primaryTitle: 'unknown', secondaryTitle: 'unknown:default/unknown', - Icon: expect.anything(), + Icon: undefined, }); expect(defaultEntityPresentation('name')).toEqual({ entityRef: 'unknown:default/name', primaryTitle: 'name', secondaryTitle: 'unknown:default/name', - Icon: expect.anything(), + Icon: undefined, }); }); }); @@ -173,7 +173,7 @@ describe('defaultEntityPresentation', () => { entityRef: 'component:default/test', primaryTitle: 'test', secondaryTitle: 'component:default/test', - Icon: expect.anything(), + Icon: undefined, }); expect( @@ -187,7 +187,7 @@ describe('defaultEntityPresentation', () => { entityRef: 'component:default/test', primaryTitle: 'component:test', secondaryTitle: 'component:default/test', - Icon: expect.anything(), + Icon: undefined, }); expect( @@ -201,7 +201,7 @@ describe('defaultEntityPresentation', () => { entityRef: 'component:default/test', primaryTitle: 'default/test', secondaryTitle: 'component:default/test', - Icon: expect.anything(), + Icon: undefined, }); }); @@ -217,14 +217,14 @@ describe('defaultEntityPresentation', () => { entityRef: 'component:default/test', primaryTitle: 'default/test', secondaryTitle: 'component:default/test', - Icon: expect.anything(), + Icon: undefined, }); expect(defaultEntityPresentation('')).toEqual({ entityRef: 'unknown:default/unknown', primaryTitle: 'unknown', secondaryTitle: 'unknown:default/unknown', - Icon: expect.anything(), + Icon: undefined, }); }); }); @@ -235,7 +235,7 @@ describe('defaultEntityPresentation', () => { entityRef: 'unknown:default/unknown', primaryTitle: 'unknown', secondaryTitle: 'unknown:default/unknown', - Icon: expect.anything(), + Icon: undefined, }); expect(defaultEntityPresentation(undefined as unknown as Entity)).toEqual( @@ -243,7 +243,7 @@ describe('defaultEntityPresentation', () => { entityRef: 'unknown:default/unknown', primaryTitle: 'unknown', secondaryTitle: 'unknown:default/unknown', - Icon: expect.anything(), + Icon: undefined, }, ); @@ -253,7 +253,7 @@ describe('defaultEntityPresentation', () => { entityRef: 'unknown:default/unknown', primaryTitle: 'unknown', secondaryTitle: 'unknown:default/unknown', - Icon: expect.anything(), + Icon: undefined, }); }); }); diff --git a/plugins/catalog-react/src/apis/EntityPresentationApi/defaultEntityPresentation.ts b/plugins/catalog-react/src/apis/EntityPresentationApi/defaultEntityPresentation.ts index 4ba18e35d1..8d841f3c3c 100644 --- a/plugins/catalog-react/src/apis/EntityPresentationApi/defaultEntityPresentation.ts +++ b/plugins/catalog-react/src/apis/EntityPresentationApi/defaultEntityPresentation.ts @@ -20,34 +20,9 @@ import { Entity, stringifyEntityRef, } from '@backstage/catalog-model'; -import { IconComponent } from '@backstage/core-plugin-api'; -import ApartmentIcon from '@material-ui/icons/Apartment'; -import BusinessIcon from '@material-ui/icons/Business'; -import ExtensionIcon from '@material-ui/icons/Extension'; -import HelpIcon from '@material-ui/icons/Help'; -import LibraryAddIcon from '@material-ui/icons/LibraryAdd'; -import LocationOnIcon from '@material-ui/icons/LocationOn'; -import MemoryIcon from '@material-ui/icons/Memory'; -import PeopleIcon from '@material-ui/icons/People'; -import PersonIcon from '@material-ui/icons/Person'; -import WorkIcon from '@material-ui/icons/Work'; import get from 'lodash/get'; import { EntityRefPresentationSnapshot } from './EntityPresentationApi'; -const UNKNOWN_KIND_ICON: IconComponent = HelpIcon; - -const DEFAULT_ICONS: Record = { - api: ExtensionIcon, - component: MemoryIcon, - system: BusinessIcon, - resource: WorkIcon, - domain: ApartmentIcon, - location: LocationOnIcon, - user: PersonIcon, - group: PeopleIcon, - template: LibraryAddIcon, -}; - /** * This returns the default representation of an entity. * @@ -68,10 +43,6 @@ export function defaultEntityPresentation( const { kind, namespace, name, title, description, displayName, type } = getParts(entityOrRef); - const Icon = - (kind && DEFAULT_ICONS[kind.toLocaleLowerCase('en-US')]) || - UNKNOWN_KIND_ICON; - const entityRef: string = stringifyEntityRef({ kind: kind || 'unknown', namespace: namespace || DEFAULT_NAMESPACE, @@ -96,7 +67,7 @@ export function defaultEntityPresentation( entityRef, primaryTitle: primary, secondaryTitle: secondary || undefined, - Icon, + Icon: undefined, // leave it up to the presentation API to handle }; } diff --git a/plugins/catalog-unprocessed-entities/CHANGELOG.md b/plugins/catalog-unprocessed-entities/CHANGELOG.md index 44fcaa4626..171f26282a 100644 --- a/plugins/catalog-unprocessed-entities/CHANGELOG.md +++ b/plugins/catalog-unprocessed-entities/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-catalog-unprocessed-entities +## 0.1.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + ## 0.1.6-next.0 ### Patch Changes diff --git a/plugins/catalog-unprocessed-entities/package.json b/plugins/catalog-unprocessed-entities/package.json index d1e466d27c..a443949861 100644 --- a/plugins/catalog-unprocessed-entities/package.json +++ b/plugins/catalog-unprocessed-entities/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-unprocessed-entities", - "version": "0.1.6-next.0", + "version": "0.1.6-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog/CHANGELOG.md b/plugins/catalog/CHANGELOG.md index 95bbf0bfb6..02a765dcf4 100644 --- a/plugins/catalog/CHANGELOG.md +++ b/plugins/catalog/CHANGELOG.md @@ -1,5 +1,35 @@ # @backstage/plugin-catalog +## 1.16.0-next.2 + +### Minor Changes + +- e223f2264d: Properly support both function- and string-form visibility filter expressions in the new extensions exported via `/alpha`. + +### Patch Changes + +- 53600976bb: Ensure that passed-in icons are taken advantage of in the presentation API +- a5a04739e1: Internal refactor of alpha exports due to a change in how extension factories are defined. +- 78a10bb085: Adding in spec.type chip to search results for clarity +- fb8f3bdbc2: Updated alpha translation message keys to use nested format and camel case. +- 531e1a2a79: Updated alpha plugin to include the `unregisterRedirect` external route. +- Updated dependencies + - @backstage/frontend-plugin-api@0.4.0-next.1 + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-client@1.5.0-next.0 + - @backstage/plugin-search-react@1.7.4-next.1 + - @backstage/core-compat-api@0.0.1-next.1 + - @backstage/integration-react@1.1.22-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.18 + - @backstage/plugin-scaffolder-common@1.4.3 + - @backstage/plugin-search-common@1.2.8 + ## 1.16.0-next.1 ### Patch Changes diff --git a/plugins/catalog/alpha-api-report.md b/plugins/catalog/api-report-alpha.md similarity index 91% rename from plugins/catalog/alpha-api-report.md rename to plugins/catalog/api-report-alpha.md index 52afb0894a..3226d10270 100644 --- a/plugins/catalog/alpha-api-report.md +++ b/plugins/catalog/api-report-alpha.md @@ -7,7 +7,7 @@ import { AnyExtensionInputMap } from '@backstage/frontend-plugin-api'; import { BackstagePlugin } from '@backstage/frontend-plugin-api'; -import { Extension } from '@backstage/frontend-plugin-api'; +import { ExtensionDefinition } from '@backstage/frontend-plugin-api'; import { ExternalRouteRef } from '@backstage/frontend-plugin-api'; import { PortableSchema } from '@backstage/frontend-plugin-api'; import { RouteRef } from '@backstage/frontend-plugin-api'; @@ -17,11 +17,12 @@ export function createCatalogFilterExtension< TInputs extends AnyExtensionInputMap, TConfig = never, >(options: { - id: string; + namespace?: string; + name?: string; inputs?: TInputs; configSchema?: PortableSchema; loader: (options: { config: TConfig }) => Promise; -}): Extension; +}): ExtensionDefinition; // @alpha (undocumented) const _default: BackstagePlugin< diff --git a/plugins/catalog/package.json b/plugins/catalog/package.json index 1ac4fe3b73..88ee613922 100644 --- a/plugins/catalog/package.json +++ b/plugins/catalog/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog", "description": "The Backstage plugin for browsing the Backstage catalog", - "version": "1.16.0-next.1", + "version": "1.16.0-next.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog/src/alpha/EntityOverviewPage.tsx b/plugins/catalog/src/alpha/EntityOverviewPage.tsx index baa93845ae..932e13d962 100644 --- a/plugins/catalog/src/alpha/EntityOverviewPage.tsx +++ b/plugins/catalog/src/alpha/EntityOverviewPage.tsx @@ -14,29 +14,95 @@ * limitations under the License. */ -import React from 'react'; -import { useEntity } from '@backstage/plugin-catalog-react'; import { Entity } from '@backstage/catalog-model'; +import { useEntity } from '@backstage/plugin-catalog-react'; import Grid from '@material-ui/core/Grid'; +import React, { useMemo } from 'react'; +import { parseFilterExpression } from './filter/parseFilterExpression'; interface EntityOverviewPageProps { cards: Array<{ element: React.JSX.Element; - filter: (ctx: { entity: Entity }) => boolean; + filterFunction?: (entity: Entity) => boolean; + filterExpression?: string; }>; } +// Keeps track of what filter expression strings that we've seen duplicates of +// with functions, or which emitted parsing errors for so far +const seenParseErrorExpressionStrings = new Set(); +const seenDuplicateExpressionStrings = new Set(); + +// Given an optional filter function and an optional filter expression, make +// sure that at most one of them was given, and return a filter function that +// does the right thing. +function buildFilterFn( + filterFunction?: (entity: Entity) => boolean, + filterExpression?: string, +): (entity: Entity) => boolean { + if ( + filterFunction && + filterExpression && + !seenDuplicateExpressionStrings.has(filterExpression) + ) { + // eslint-disable-next-line no-console + console.warn( + `Duplicate entity filter methods found, both '${filterExpression}' as well as a callback function, which is not permitted - using the callback`, + ); + seenDuplicateExpressionStrings.add(filterExpression); + } + + const filter = filterFunction || filterExpression; + if (!filter) { + return () => true; + } else if (typeof filter === 'function') { + return subject => filter(subject); + } + + const result = parseFilterExpression(filter); + if ( + result.expressionParseErrors.length && + !seenParseErrorExpressionStrings.has(filter) + ) { + // eslint-disable-next-line no-console + console.warn( + `Error(s) in entity filter expression '${filter}'`, + result.expressionParseErrors, + ); + seenParseErrorExpressionStrings.add(filter); + } + + return result.filterFn; +} + +// Handles the memoized parsing of filter expressions for each card +function CardWrapper(props: { + entity: Entity; + element: React.JSX.Element; + filterFunction?: (entity: Entity) => boolean; + filterExpression?: string; +}) { + const { entity, element, filterFunction, filterExpression } = props; + + const filterFn = useMemo( + () => buildFilterFn(filterFunction, filterExpression), + [filterFunction, filterExpression], + ); + + return filterFn(entity) ? ( + + {element} + + ) : null; +} + export function EntityOverviewPage(props: EntityOverviewPageProps) { const { entity } = useEntity(); return ( - {props.cards - .filter(card => card.filter({ entity })) - .map(card => ( - - {card.element} - - ))} + {props.cards.map((card, index) => ( + + ))} ); } diff --git a/plugins/catalog/src/alpha/createCatalogFilterExtension.tsx b/plugins/catalog/src/alpha/createCatalogFilterExtension.tsx index 7dae30c1fa..48ee647b81 100644 --- a/plugins/catalog/src/alpha/createCatalogFilterExtension.tsx +++ b/plugins/catalog/src/alpha/createCatalogFilterExtension.tsx @@ -28,16 +28,17 @@ export function createCatalogFilterExtension< TInputs extends AnyExtensionInputMap, TConfig = never, >(options: { - id: string; + namespace?: string; + name?: string; inputs?: TInputs; configSchema?: PortableSchema; loader: (options: { config: TConfig }) => Promise; }) { - const id = `catalog.filter.${options.id}`; - return createExtension({ - id, - attachTo: { id: 'plugin.catalog.page.index', input: 'filters' }, + kind: 'catalog-filter', + namespace: options.namespace, + name: options.name, + attachTo: { id: 'page:catalog', input: 'filters' }, inputs: options.inputs ?? {}, configSchema: options.configSchema, output: { diff --git a/plugins/catalog/src/alpha/entityCards.tsx b/plugins/catalog/src/alpha/entityCards.tsx index c7b75679a7..b6a804a576 100644 --- a/plugins/catalog/src/alpha/entityCards.tsx +++ b/plugins/catalog/src/alpha/entityCards.tsx @@ -18,7 +18,7 @@ import React from 'react'; import { createEntityCardExtension } from '@backstage/plugin-catalog-react/alpha'; export const EntityAboutCard = createEntityCardExtension({ - id: 'about', + name: 'about', loader: async () => import('../components/AboutCard').then(m => ( @@ -26,8 +26,8 @@ export const EntityAboutCard = createEntityCardExtension({ }); export const EntityLinksCard = createEntityCardExtension({ - id: 'links', - filter: ({ entity }) => Boolean(entity.metadata.links), + name: 'links', + filter: 'has:links', loader: async () => import('../components/EntityLinksCard').then(m => { return ; @@ -35,8 +35,8 @@ export const EntityLinksCard = createEntityCardExtension({ }); export const EntityLabelsCard = createEntityCardExtension({ - id: 'labels', - filter: ({ entity }) => Boolean(entity.metadata.labels), + name: 'labels', + filter: 'has:labels', loader: async () => import('../components/EntityLabelsCard').then(m => ( @@ -44,7 +44,7 @@ export const EntityLabelsCard = createEntityCardExtension({ }); export const EntityDependsOnComponentsCard = createEntityCardExtension({ - id: 'dependsOn.components', + name: 'depends-on-components', loader: async () => import('../components/DependsOnComponentsCard').then(m => ( @@ -52,7 +52,7 @@ export const EntityDependsOnComponentsCard = createEntityCardExtension({ }); export const EntityDependsOnResourcesCard = createEntityCardExtension({ - id: 'dependsOn.resources', + name: 'depends-on-resources', loader: async () => import('../components/DependsOnResourcesCard').then(m => ( @@ -60,7 +60,7 @@ export const EntityDependsOnResourcesCard = createEntityCardExtension({ }); export const EntityHasComponentsCard = createEntityCardExtension({ - id: 'has.components', + name: 'has-components', loader: async () => import('../components/HasComponentsCard').then(m => ( @@ -68,7 +68,7 @@ export const EntityHasComponentsCard = createEntityCardExtension({ }); export const EntityHasResourcesCard = createEntityCardExtension({ - id: 'has.resources', + name: 'has-resources', loader: async () => import('../components/HasResourcesCard').then(m => ( @@ -76,7 +76,7 @@ export const EntityHasResourcesCard = createEntityCardExtension({ }); export const EntityHasSubcomponentsCard = createEntityCardExtension({ - id: 'has.subcomponents', + name: 'has-subcomponents', loader: async () => import('../components/HasSubcomponentsCard').then(m => ( @@ -84,7 +84,7 @@ export const EntityHasSubcomponentsCard = createEntityCardExtension({ }); export const EntityHasSystemsCard = createEntityCardExtension({ - id: 'has.systems', + name: 'has-systems', loader: async () => import('../components/HasSystemsCard').then(m => ( diff --git a/plugins/catalog/src/alpha/entityContents.tsx b/plugins/catalog/src/alpha/entityContents.tsx index 9bef4a5e0d..b278e15735 100644 --- a/plugins/catalog/src/alpha/entityContents.tsx +++ b/plugins/catalog/src/alpha/entityContents.tsx @@ -21,23 +21,25 @@ import { } from '@backstage/frontend-plugin-api'; import { createEntityContentExtension, - entityFilterExtensionDataRef, + entityFilterFunctionExtensionDataRef, + entityFilterExpressionExtensionDataRef, } from '@backstage/plugin-catalog-react/alpha'; export const OverviewEntityContent = createEntityContentExtension({ - id: 'overview', + name: 'overview', defaultPath: '/', defaultTitle: 'Overview', disabled: false, inputs: { cards: createExtensionInput({ element: coreExtensionData.reactElement, - filter: entityFilterExtensionDataRef, + filterFunction: entityFilterFunctionExtensionDataRef.optional(), + filterExpression: entityFilterExpressionExtensionDataRef.optional(), }), }, loader: async ({ inputs }) => import('./EntityOverviewPage').then(m => ( - + c.output)} /> )), }); diff --git a/plugins/catalog/src/alpha/filter/matrchers/createHasMatcher.test.ts b/plugins/catalog/src/alpha/filter/matrchers/createHasMatcher.test.ts new file mode 100644 index 0000000000..53524d228e --- /dev/null +++ b/plugins/catalog/src/alpha/filter/matrchers/createHasMatcher.test.ts @@ -0,0 +1,98 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Entity } from '@backstage/catalog-model'; +import { createHasMatcher } from './createHasMatcher'; + +describe('createHasMatcher', () => { + const empty1 = { + metadata: {}, + } as unknown as Entity; + const empty2 = { + metadata: { + labels: {}, + links: [], + }, + } as unknown as Entity; + const labels = { + metadata: { + labels: { a: 'b' }, + }, + } as unknown as Entity; + const links = { + metadata: { links: [{}] }, + } as unknown as Entity; + + const err = jest.fn(); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('supports valid parameters', () => { + expect(createHasMatcher(['labels'], err)(empty1)).toBe(false); + expect(createHasMatcher(['labels'], err)(empty2)).toBe(false); + expect(createHasMatcher(['labels'], err)(labels)).toBe(true); + + expect(createHasMatcher(['links'], err)(empty1)).toBe(false); + expect(createHasMatcher(['links'], err)(empty2)).toBe(false); + expect(createHasMatcher(['links'], err)(links)).toBe(true); + + expect(err).not.toHaveBeenCalled(); + }); + + it('matches if ANY parameter matches', () => { + expect(createHasMatcher(['labels', 'links'], err)(empty1)).toBe(false); + expect(createHasMatcher(['labels', 'links'], err)(empty2)).toBe(false); + expect(createHasMatcher(['labels', 'links'], err)(labels)).toBe(true); + expect(createHasMatcher(['labels', 'links'], err)(links)).toBe(true); + + expect(err).not.toHaveBeenCalled(); + }); + + it('emits errors properly, and skips over bad parameters', () => { + // throw if callback throws + expect(() => + createHasMatcher(['labels', 'bar'], e => { + throw e; + }), + ).toThrowErrorMatchingInlineSnapshot( + `"'bar' is not a valid parameter for 'has' filter expressions, expected one of 'labels','links'"`, + ); + expect(err).not.toHaveBeenCalled(); + + // continue if callback does not throw, and skip over the bad parameter + let matcher = createHasMatcher(['labels', 'foo', 'bar'], err); + expect(err).toHaveBeenCalledTimes(2); + expect(err).toHaveBeenCalledWith( + expect.objectContaining({ + message: `'foo' is not a valid parameter for 'has' filter expressions, expected one of 'labels','links'`, + }), + ); + expect(err).toHaveBeenCalledWith( + expect.objectContaining({ + message: `'bar' is not a valid parameter for 'has' filter expressions, expected one of 'labels','links'`, + }), + ); + expect(matcher(empty1)).toBe(false); + expect(matcher(labels)).toBe(true); + + // when no parameters at all match, just return true + matcher = createHasMatcher(['foo', 'bar'], err); + expect(matcher(empty1)).toBe(true); + expect(matcher(labels)).toBe(true); + }); +}); diff --git a/plugins/catalog/src/alpha/filter/matrchers/createHasMatcher.ts b/plugins/catalog/src/alpha/filter/matrchers/createHasMatcher.ts new file mode 100644 index 0000000000..9fd393a561 --- /dev/null +++ b/plugins/catalog/src/alpha/filter/matrchers/createHasMatcher.ts @@ -0,0 +1,52 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { InputError } from '@backstage/errors'; +import { EntityMatcherFn } from './types'; + +const allowedMatchers: Record = { + labels: entity => { + return Object.keys(entity.metadata.labels ?? {}).length > 0; + }, + links: entity => { + return (entity.metadata.links ?? []).length > 0; + }, +}; + +/** + * Matches on the non-empty presence of different parts of the entity + */ +export function createHasMatcher( + parameters: string[], + onParseError: (error: Error) => void, +): EntityMatcherFn { + const matchers = parameters.flatMap(parameter => { + const matcher = allowedMatchers[parameter.toLocaleLowerCase('en-US')]; + if (!matcher) { + const known = Object.keys(allowedMatchers).map(m => `'${m}'`); + onParseError( + new InputError( + `'${parameter}' is not a valid parameter for 'has' filter expressions, expected one of ${known}`, + ), + ); + return []; + } + return [matcher]; + }); + + return entity => + matchers.length ? matchers.some(matcher => matcher(entity)) : true; +} diff --git a/plugins/catalog/src/alpha/filter/matrchers/createIsMatcher.test.ts b/plugins/catalog/src/alpha/filter/matrchers/createIsMatcher.test.ts new file mode 100644 index 0000000000..4cb3094d62 --- /dev/null +++ b/plugins/catalog/src/alpha/filter/matrchers/createIsMatcher.test.ts @@ -0,0 +1,75 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Entity } from '@backstage/catalog-model'; +import { createIsMatcher } from './createIsMatcher'; + +describe('createIsMatcher', () => { + const empty = { + metadata: {}, + } as unknown as Entity; + const orphan = { + metadata: { + annotations: { ['backstage.io/orphan']: 'true' }, + }, + } as unknown as Entity; + + const err = jest.fn(); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('supports valid parameters', () => { + expect(createIsMatcher(['orphan'], err)(empty)).toBe(false); + expect(createIsMatcher(['orphan'], err)(orphan)).toBe(true); + + expect(err).not.toHaveBeenCalled(); + }); + + it('emits errors properly, and skips over bad parameters', () => { + // throw if callback throws + expect(() => + createIsMatcher(['orphan', 'foo'], e => { + throw e; + }), + ).toThrowErrorMatchingInlineSnapshot( + `"'foo' is not a valid parameter for 'is' filter expressions, expected one of 'orphan'"`, + ); + expect(err).not.toHaveBeenCalled(); + + // continue if callback does not throw, and skip over the bad parameter + let matcher = createIsMatcher(['orphan', 'foo', 'bar'], err); + expect(err).toHaveBeenCalledTimes(2); + expect(err).toHaveBeenCalledWith( + expect.objectContaining({ + message: `'foo' is not a valid parameter for 'is' filter expressions, expected one of 'orphan'`, + }), + ); + expect(err).toHaveBeenCalledWith( + expect.objectContaining({ + message: `'bar' is not a valid parameter for 'is' filter expressions, expected one of 'orphan'`, + }), + ); + expect(matcher(empty)).toBe(false); + expect(matcher(orphan)).toBe(true); + + // when no parameters at all match, just return true + matcher = createIsMatcher(['foo', 'bar'], err); + expect(matcher(empty)).toBe(true); + expect(matcher(orphan)).toBe(true); + }); +}); diff --git a/plugins/catalog/src/alpha/filter/matrchers/createIsMatcher.ts b/plugins/catalog/src/alpha/filter/matrchers/createIsMatcher.ts new file mode 100644 index 0000000000..49a54349d1 --- /dev/null +++ b/plugins/catalog/src/alpha/filter/matrchers/createIsMatcher.ts @@ -0,0 +1,48 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { InputError } from '@backstage/errors'; +import { EntityMatcherFn } from './types'; + +const allowedMatchers: Record = { + orphan: entity => + Boolean(entity.metadata.annotations?.['backstage.io/orphan']), +}; + +/** + * Matches on different semantic properties of the entity + */ +export function createIsMatcher( + parameters: string[], + onParseError: (error: Error) => void, +): EntityMatcherFn { + const matchers = parameters.flatMap(parameter => { + const matcher = allowedMatchers[parameter.toLocaleLowerCase('en-US')]; + if (!matcher) { + const known = Object.keys(allowedMatchers).map(m => `'${m}'`); + onParseError( + new InputError( + `'${parameter}' is not a valid parameter for 'is' filter expressions, expected one of ${known}`, + ), + ); + return []; + } + return [matcher]; + }); + + return entity => + matchers.length ? matchers.some(matcher => matcher(entity)) : true; +} diff --git a/plugins/catalog/src/alpha/filter/matrchers/createKindMatcher.test.ts b/plugins/catalog/src/alpha/filter/matrchers/createKindMatcher.test.ts new file mode 100644 index 0000000000..f109acccd4 --- /dev/null +++ b/plugins/catalog/src/alpha/filter/matrchers/createKindMatcher.test.ts @@ -0,0 +1,46 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Entity } from '@backstage/catalog-model'; +import { createKindMatcher } from './createKindMatcher'; + +describe('createKindMatcher', () => { + const component = { kind: 'Component' } as unknown as Entity; + const user = { kind: 'User' } as unknown as Entity; + const resource = { kind: 'Resource' } as unknown as Entity; + + const err = jest.fn(); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('supports valid parameters', () => { + expect(createKindMatcher(['component'], err)(component)).toBe(true); + expect(createKindMatcher(['componenT'], err)(component)).toBe(true); + expect(createKindMatcher(['user'], err)(component)).toBe(false); + + expect(err).not.toHaveBeenCalled(); + }); + + it('matches if ANY parameter matches', () => { + expect(createKindMatcher(['user', 'component'], err)(user)).toBe(true); + expect(createKindMatcher(['user', 'component'], err)(component)).toBe(true); + expect(createKindMatcher(['user', 'component'], err)(resource)).toBe(false); + + expect(err).not.toHaveBeenCalled(); + }); +}); diff --git a/plugins/catalog/src/alpha/filter/matrchers/createKindMatcher.ts b/plugins/catalog/src/alpha/filter/matrchers/createKindMatcher.ts new file mode 100644 index 0000000000..4f7c475ebe --- /dev/null +++ b/plugins/catalog/src/alpha/filter/matrchers/createKindMatcher.ts @@ -0,0 +1,28 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { EntityMatcherFn } from './types'; + +/** + * Matches on kind + */ +export function createKindMatcher( + parameters: string[], + _onParseError: (error: Error) => void, +): EntityMatcherFn { + const items = parameters.map(p => p.toLocaleLowerCase('en-US')); + return entity => items.includes(entity.kind.toLocaleLowerCase('en-US')); +} diff --git a/plugins/catalog/src/alpha/filter/matrchers/createTypeMatcher.test.ts b/plugins/catalog/src/alpha/filter/matrchers/createTypeMatcher.test.ts new file mode 100644 index 0000000000..31c9af7e11 --- /dev/null +++ b/plugins/catalog/src/alpha/filter/matrchers/createTypeMatcher.test.ts @@ -0,0 +1,54 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Entity } from '@backstage/catalog-model'; +import { createTypeMatcher } from './createTypeMatcher'; + +describe('createTypeMatcher', () => { + const empty1 = {} as unknown as Entity; + const empty2 = { spec: {} } as unknown as Entity; + const service = { spec: { type: 'service' } } as unknown as Entity; + const website = { spec: { type: 'website' } } as unknown as Entity; + const pipeline = { spec: { type: 'pipeline' } } as unknown as Entity; + + const err = jest.fn(); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('supports valid parameters', () => { + expect(createTypeMatcher(['service'], err)(empty1)).toBe(false); + expect(createTypeMatcher(['service'], err)(empty2)).toBe(false); + + expect(createTypeMatcher(['service'], err)(service)).toBe(true); + expect(createTypeMatcher(['sErViCe'], err)(service)).toBe(true); + expect(createTypeMatcher(['service'], err)(service)).toBe(true); + expect(createTypeMatcher(['website'], err)(service)).toBe(false); + + expect(err).not.toHaveBeenCalled(); + }); + + it('matches if ANY parameter matches', () => { + expect(createTypeMatcher(['service', 'website'], err)(service)).toBe(true); + expect(createTypeMatcher(['service', 'website'], err)(website)).toBe(true); + expect(createTypeMatcher(['service', 'website'], err)(pipeline)).toBe( + false, + ); + + expect(err).not.toHaveBeenCalled(); + }); +}); diff --git a/plugins/catalog/src/alpha/filter/matrchers/createTypeMatcher.ts b/plugins/catalog/src/alpha/filter/matrchers/createTypeMatcher.ts new file mode 100644 index 0000000000..31f7a5a461 --- /dev/null +++ b/plugins/catalog/src/alpha/filter/matrchers/createTypeMatcher.ts @@ -0,0 +1,34 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { EntityMatcherFn } from './types'; + +/** + * Matches on spec.type + */ +export function createTypeMatcher( + parameters: string[], + _onParseError: (error: Error) => void, +): EntityMatcherFn { + const items = parameters.map(p => p.toLocaleLowerCase('en-US')); + return entity => { + const value = entity.spec?.type; + return ( + typeof value === 'string' && + items.includes(value.toLocaleLowerCase('en-US')) + ); + }; +} diff --git a/plugins/catalog/src/alpha/filter/matrchers/types.ts b/plugins/catalog/src/alpha/filter/matrchers/types.ts new file mode 100644 index 0000000000..0fff91b3d5 --- /dev/null +++ b/plugins/catalog/src/alpha/filter/matrchers/types.ts @@ -0,0 +1,19 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Entity } from '@backstage/catalog-model'; + +export type EntityMatcherFn = (entity: Entity) => boolean; diff --git a/plugins/catalog/src/alpha/filter/parseFilterExpression.test.ts b/plugins/catalog/src/alpha/filter/parseFilterExpression.test.ts new file mode 100644 index 0000000000..616091739e --- /dev/null +++ b/plugins/catalog/src/alpha/filter/parseFilterExpression.test.ts @@ -0,0 +1,151 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Entity } from '@backstage/catalog-model'; +import { + parseFilterExpression, + splitFilterExpression, +} from './parseFilterExpression'; + +describe('parseFilterExpression', () => { + function run(expression: string) { + const result = parseFilterExpression(expression); + if (result.expressionParseErrors.length) { + throw result.expressionParseErrors[0]; + } + return result.filterFn; + } + + it('supports "kind" expressions', () => { + const component = { kind: 'Component' } as unknown as Entity; + const user = { kind: 'User' } as unknown as Entity; + const resource = { kind: 'Resource' } as unknown as Entity; + + expect(run('kind:user,component')(user)).toBe(true); + expect(run('kind:user,component')(component)).toBe(true); + expect(run('kind:user,component')(resource)).toBe(false); + }); + + it('supports "type" expressions', () => { + const empty = {} as unknown as Entity; + const service = { spec: { type: 'service' } } as unknown as Entity; + const website = { spec: { type: 'website' } } as unknown as Entity; + const pipeline = { spec: { type: 'pipeline' } } as unknown as Entity; + + expect(run('type:service,website')(empty)).toBe(false); + expect(run('type:service,website')(service)).toBe(true); + expect(run('type:service,website')(website)).toBe(true); + expect(run('type:service,website')(pipeline)).toBe(false); + }); + + it('supports "is" expressions', () => { + const empty = { + metadata: {}, + } as unknown as Entity; + const orphan = { + metadata: { + annotations: { ['backstage.io/orphan']: 'true' }, + }, + } as unknown as Entity; + + expect(run('is:orphan')(empty)).toBe(false); + expect(run('is:orphan')(orphan)).toBe(true); + + expect(() => run('is:orphan,bar')).toThrowErrorMatchingInlineSnapshot( + `"'bar' is not a valid parameter for 'is' filter expressions, expected one of 'orphan'"`, + ); + }); + + it('supports "has" expressions', () => { + const empty = { + metadata: {}, + } as unknown as Entity; + const labels = { + metadata: { labels: { a: 'b' } }, + } as unknown as Entity; + const annotations = { + metadata: { annotations: { a: 'b' } }, + } as unknown as Entity; + const links = { + metadata: { links: [{}] }, + } as unknown as Entity; + + expect(run('has:labels,links')(empty)).toBe(false); + expect(run('has:labels,links')(labels)).toBe(true); + expect(run('has:labels,links')(links)).toBe(true); + expect(run('has:labels,links')(annotations)).toBe(false); + + expect(() => run('has:labels,bar')).toThrowErrorMatchingInlineSnapshot( + `"'bar' is not a valid parameter for 'has' filter expressions, expected one of 'labels','links'"`, + ); + }); + + it('rejects unknown keys', () => { + expect(() => run('unknown:foo')).toThrowErrorMatchingInlineSnapshot( + `"'unknown' is not a valid filter expression key, expected one of 'kind','type','is','has'"`, + ); + }); + + it('rejects malformed inputs', () => { + expect(() => run(':')).toThrowErrorMatchingInlineSnapshot( + `"':' is not a valid filter expression, expected 'key:parameter' form"`, + ); + expect(() => run(':a')).toThrowErrorMatchingInlineSnapshot( + `"':a' is not a valid filter expression, expected 'key:parameter' form"`, + ); + expect(() => run('a:')).toThrowErrorMatchingInlineSnapshot( + `"'a:' is not a valid filter expression, expected 'key:parameter' form"`, + ); + }); +}); + +describe('splitFilterExpression', () => { + function run(expression: string) { + return splitFilterExpression(expression, e => { + throw e; + }); + } + + it('properly splits into expression atoms', () => { + expect(run('')).toEqual([]); + expect(run(' ')).toEqual([]); + expect(run('kind:component')).toEqual([ + { key: 'kind', parameters: ['component'] }, + ]); + expect(run('kind:component,user')).toEqual([ + { key: 'kind', parameters: ['component', 'user'] }, + ]); + expect(run('kind:component,user type:foo')).toEqual([ + { key: 'kind', parameters: ['component', 'user'] }, + { key: 'type', parameters: ['foo'] }, + ]); + expect(run('with:multiple:colons')).toEqual([ + { key: 'with', parameters: ['multiple:colons'] }, + ]); + }); + + it('rejects malformed inputs', () => { + expect(() => run(':')).toThrowErrorMatchingInlineSnapshot( + `"':' is not a valid filter expression, expected 'key:parameter' form"`, + ); + expect(() => run(':a')).toThrowErrorMatchingInlineSnapshot( + `"':a' is not a valid filter expression, expected 'key:parameter' form"`, + ); + expect(() => run('a:')).toThrowErrorMatchingInlineSnapshot( + `"'a:' is not a valid filter expression, expected 'key:parameter' form"`, + ); + }); +}); diff --git a/plugins/catalog/src/alpha/filter/parseFilterExpression.ts b/plugins/catalog/src/alpha/filter/parseFilterExpression.ts new file mode 100644 index 0000000000..a4c17cb8da --- /dev/null +++ b/plugins/catalog/src/alpha/filter/parseFilterExpression.ts @@ -0,0 +1,126 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Entity } from '@backstage/catalog-model'; +import { InputError } from '@backstage/errors'; +import { EntityMatcherFn } from './matrchers/types'; +import { createKindMatcher } from './matrchers/createKindMatcher'; +import { createTypeMatcher } from './matrchers/createTypeMatcher'; +import { createIsMatcher } from './matrchers/createIsMatcher'; +import { createHasMatcher } from './matrchers/createHasMatcher'; + +const rootMatcherFactories: Record< + string, + ( + parameters: string[], + onParseError: (error: Error) => void, + ) => EntityMatcherFn +> = { + kind: createKindMatcher, + type: createTypeMatcher, + is: createIsMatcher, + has: createHasMatcher, +}; + +/** + * Parses a filter expression that decides whether to render an entity component + * or not. Returns a function that matches entities based on that expression. + * + * @remarks + * + * Filter strings are on the form `kind:user,group is:orphan`. There's + * effectively an AND between the space separated parts, and an OR between comma + * separated parameters. So the example filter string semantically means + * "entities that are of either User or Group kind, and also are orphans". + * + * The `expressionParseErrors` array contains any errors that were encountered + * during initial parsing of the expression. Note that the parts of the input + * expression that had errors are ignored entirely and parsing continues as if + * they didn't exist. + */ +export function parseFilterExpression(expression: string): { + filterFn: (entity: Entity) => boolean; + expressionParseErrors: Error[]; +} { + const expressionParseErrors: Error[] = []; + + const parts = splitFilterExpression(expression, e => + expressionParseErrors.push(e), + ); + + const matchers = parts.flatMap(part => { + const factory = rootMatcherFactories[part.key]; + if (!factory) { + const known = Object.keys(rootMatcherFactories).map(m => `'${m}'`); + expressionParseErrors.push( + new InputError( + `'${part.key}' is not a valid filter expression key, expected one of ${known}`, + ), + ); + return []; + } + + const matcher = factory(part.parameters, e => + expressionParseErrors.push(e), + ); + return [matcher]; + }); + + const filterFn = (entity: Entity) => + matchers.every(matcher => { + try { + return matcher(entity); + } catch { + return false; + } + }); + + return { + filterFn, + expressionParseErrors, + }; +} + +export function splitFilterExpression( + expression: string, + onParseError: (error: Error) => void, +): Array<{ key: string; parameters: string[] }> { + const words = expression + .split(' ') + .map(w => w.trim()) + .filter(Boolean); + + const result = new Array<{ key: string; parameters: string[] }>(); + + for (const word of words) { + const match = word.match(/^([^:]+):(.+)$/); + if (!match) { + onParseError( + new InputError( + `'${word}' is not a valid filter expression, expected 'key:parameter' form`, + ), + ); + continue; + } + + const key = match[1]; + const parameters = match[2].split(',').filter(Boolean); // silently ignore double commas + + result.push({ key, parameters }); + } + + return result; +} diff --git a/plugins/catalog/src/alpha/filters.tsx b/plugins/catalog/src/alpha/filters.tsx index 7b73c2d14b..be06e70611 100644 --- a/plugins/catalog/src/alpha/filters.tsx +++ b/plugins/catalog/src/alpha/filters.tsx @@ -19,7 +19,7 @@ import { createCatalogFilterExtension } from './createCatalogFilterExtension'; import { createSchemaFromZod } from '@backstage/frontend-plugin-api'; const CatalogEntityTagFilter = createCatalogFilterExtension({ - id: 'entity.tag', + name: 'tag', loader: async () => { const { EntityTagPicker } = await import('@backstage/plugin-catalog-react'); return ; @@ -27,7 +27,7 @@ const CatalogEntityTagFilter = createCatalogFilterExtension({ }); const CatalogEntityKindFilter = createCatalogFilterExtension({ - id: 'entity.kind', + name: 'kind', configSchema: createSchemaFromZod(z => z.object({ initialFilter: z.string().default('component'), @@ -42,7 +42,7 @@ const CatalogEntityKindFilter = createCatalogFilterExtension({ }); const CatalogEntityTypeFilter = createCatalogFilterExtension({ - id: 'entity.type', + name: 'type', loader: async () => { const { EntityTypePicker } = await import( '@backstage/plugin-catalog-react' @@ -52,7 +52,7 @@ const CatalogEntityTypeFilter = createCatalogFilterExtension({ }); const CatalogEntityOwnerFilter = createCatalogFilterExtension({ - id: 'entity.mode', + name: 'mode', configSchema: createSchemaFromZod(z => z.object({ mode: z.enum(['owners-only', 'all']).optional(), @@ -67,7 +67,7 @@ const CatalogEntityOwnerFilter = createCatalogFilterExtension({ }); const CatalogEntityNamespaceFilter = createCatalogFilterExtension({ - id: 'entity.namespace', + name: 'namespace', loader: async () => { const { EntityNamespacePicker } = await import( '@backstage/plugin-catalog-react' @@ -77,7 +77,7 @@ const CatalogEntityNamespaceFilter = createCatalogFilterExtension({ }); const CatalogEntityLifecycleFilter = createCatalogFilterExtension({ - id: 'entity.lifecycle', + name: 'lifecycle', loader: async () => { const { EntityLifecyclePicker } = await import( '@backstage/plugin-catalog-react' @@ -87,7 +87,7 @@ const CatalogEntityLifecycleFilter = createCatalogFilterExtension({ }); const CatalogEntityProcessingStatusFilter = createCatalogFilterExtension({ - id: 'entity.processing.status', + name: 'processing.status', loader: async () => { const { EntityProcessingStatusPicker } = await import( '@backstage/plugin-catalog-react' @@ -97,7 +97,7 @@ const CatalogEntityProcessingStatusFilter = createCatalogFilterExtension({ }); const CatalogUserListFilter = createCatalogFilterExtension({ - id: 'user.list', + name: 'list', configSchema: createSchemaFromZod(z => z.object({ initialFilter: z.enum(['owned', 'starred', 'all']).default('owned'), diff --git a/plugins/catalog/src/alpha/navItems.tsx b/plugins/catalog/src/alpha/navItems.tsx index 77ea394a97..8dde27b95a 100644 --- a/plugins/catalog/src/alpha/navItems.tsx +++ b/plugins/catalog/src/alpha/navItems.tsx @@ -20,7 +20,6 @@ import { createNavItemExtension } from '@backstage/frontend-plugin-api'; import { rootRouteRef } from '../routes'; export const CatalogIndexNavItem = createNavItemExtension({ - id: 'catalog.nav.index', routeRef: convertLegacyRouteRef(rootRouteRef), title: 'Catalog', icon: HomeIcon, diff --git a/plugins/catalog/src/alpha/pages.tsx b/plugins/catalog/src/alpha/pages.tsx index 6324f53083..ee1da49147 100644 --- a/plugins/catalog/src/alpha/pages.tsx +++ b/plugins/catalog/src/alpha/pages.tsx @@ -30,7 +30,6 @@ import { rootRouteRef } from '../routes'; import { useEntityFromUrl } from '../components/CatalogEntityPage/useEntityFromUrl'; export const CatalogIndexPage = createPageExtension({ - id: 'plugin.catalog.page.index', defaultPath: '/catalog', routeRef: convertLegacyRouteRef(rootRouteRef), inputs: { @@ -40,13 +39,13 @@ export const CatalogIndexPage = createPageExtension({ }, loader: async ({ inputs }) => { const { BaseCatalogPage } = await import('../components/CatalogPage'); - const filters = inputs.filters.map(filter => filter.element); + const filters = inputs.filters.map(filter => filter.output.element); return {filters}} />; }, }); export const CatalogEntityPage = createPageExtension({ - id: 'plugin.catalog.page.entity', + name: 'entity', defaultPath: '/catalog/:namespace/:kind/:name', routeRef: convertLegacyRouteRef(entityRouteRef), inputs: { @@ -65,11 +64,11 @@ export const CatalogEntityPage = createPageExtension({ {inputs.contents.map(content => ( - {content.element} + {content.output.element} ))} diff --git a/plugins/catalog/src/alpha/plugin.tsx b/plugins/catalog/src/alpha/plugin.tsx index 7140259df0..99aacd27da 100644 --- a/plugins/catalog/src/alpha/plugin.tsx +++ b/plugins/catalog/src/alpha/plugin.tsx @@ -22,8 +22,8 @@ import { entityRouteRef } from '@backstage/plugin-catalog-react'; import { createComponentRouteRef, createFromTemplateRouteRef, - unregisterRedirectRouteRef, rootRouteRef, + unregisterRedirectRouteRef, viewTechDocRouteRef, } from '../routes'; diff --git a/plugins/catalog/src/alpha/searchResultItems.tsx b/plugins/catalog/src/alpha/searchResultItems.tsx index f677869136..c84aabf300 100644 --- a/plugins/catalog/src/alpha/searchResultItems.tsx +++ b/plugins/catalog/src/alpha/searchResultItems.tsx @@ -18,7 +18,6 @@ import { createSearchResultListItemExtension } from '@backstage/plugin-search-re export const CatalogSearchResultListItemExtension = createSearchResultListItemExtension({ - id: 'catalog', predicate: result => result.type === 'software-catalog', component: () => import('../components/CatalogSearchResultListItem').then( diff --git a/plugins/catalog/src/apis/EntityPresentationApi/DefaultEntityPresentationApi.ts b/plugins/catalog/src/apis/EntityPresentationApi/DefaultEntityPresentationApi.ts index 5f1119e598..b610173421 100644 --- a/plugins/catalog/src/apis/EntityPresentationApi/DefaultEntityPresentationApi.ts +++ b/plugins/catalog/src/apis/EntityPresentationApi/DefaultEntityPresentationApi.ts @@ -34,6 +34,7 @@ import { DEFAULT_BATCH_DELAY, DEFAULT_CACHE_TTL, DEFAULT_ICONS, + UNKNOWN_KIND_ICON, createDefaultRenderer, } from './defaults'; @@ -134,10 +135,7 @@ export interface DefaultEntityPresentationApiOptions { * @remarks * * The keys are kinds (case insensitive) that map to icon values to represent - * kinds by. - * - * If you do not supply a set of icons here, a set of fallback icons will be - * used. If you supply the empty object, no fallback icons will be used. + * kinds by. These are merged with the default set of icons. */ kindIcons?: Record; @@ -194,11 +192,12 @@ export class DefaultEntityPresentationApi implements EntityPresentationApi { const renderer = options.renderer ?? createDefaultRenderer({ async: true }); const kindIcons: Record = {}; - Object.entries(options.kindIcons ?? DEFAULT_ICONS).forEach( - ([kind, icon]) => { - kindIcons[kind.toLocaleLowerCase('en-US')] = icon; - }, - ); + Object.entries(DEFAULT_ICONS).forEach(([kind, icon]) => { + kindIcons[kind.toLocaleLowerCase('en-US')] = icon; + }); + Object.entries(options.kindIcons ?? {}).forEach(([kind, icon]) => { + kindIcons[kind.toLocaleLowerCase('en-US')] = icon; + }); if (renderer.async) { if (!options.catalogApi) { @@ -396,6 +395,8 @@ export class DefaultEntityPresentationApi implements EntityPresentationApi { return false; } - return this.#kindIcons[kind.toLocaleLowerCase('en-US')]; + return ( + this.#kindIcons[kind.toLocaleLowerCase('en-US')] ?? UNKNOWN_KIND_ICON + ); } } diff --git a/plugins/catalog/src/apis/EntityPresentationApi/defaults.tsx b/plugins/catalog/src/apis/EntityPresentationApi/defaults.tsx index afcd1f5464..84f04c6f83 100644 --- a/plugins/catalog/src/apis/EntityPresentationApi/defaults.tsx +++ b/plugins/catalog/src/apis/EntityPresentationApi/defaults.tsx @@ -26,6 +26,7 @@ import LocationOnIcon from '@material-ui/icons/LocationOn'; import MemoryIcon from '@material-ui/icons/Memory'; import PeopleIcon from '@material-ui/icons/People'; import PersonIcon from '@material-ui/icons/Person'; +import WorkIcon from '@material-ui/icons/Work'; import { DefaultEntityPresentationApiRenderer } from './DefaultEntityPresentationApi'; export const DEFAULT_CACHE_TTL: HumanDuration = { seconds: 10 }; @@ -38,6 +39,7 @@ export const DEFAULT_ICONS: Record = { api: ExtensionIcon, component: MemoryIcon, system: BusinessIcon, + resource: WorkIcon, domain: ApartmentIcon, location: LocationOnIcon, user: PersonIcon, diff --git a/plugins/catalog/src/components/CatalogPage/DefaultCatalogPage.test.tsx b/plugins/catalog/src/components/CatalogPage/DefaultCatalogPage.test.tsx index f3cc0cb042..e7ba2b960e 100644 --- a/plugins/catalog/src/components/CatalogPage/DefaultCatalogPage.test.tsx +++ b/plugins/catalog/src/components/CatalogPage/DefaultCatalogPage.test.tsx @@ -32,8 +32,8 @@ import { MockStarredEntitiesApi, starredEntitiesApiRef, } from '@backstage/plugin-catalog-react'; +import { mockBreakpoint } from '@backstage/core-components/testUtils'; import { - mockBreakpoint, MockStorageApi, TestApiProvider, renderInTestApp, diff --git a/plugins/catalog/src/components/CatalogPage/DefaultCatalogPage.tsx b/plugins/catalog/src/components/CatalogPage/DefaultCatalogPage.tsx index 1f724d10fc..8dfc29f87f 100644 --- a/plugins/catalog/src/components/CatalogPage/DefaultCatalogPage.tsx +++ b/plugins/catalog/src/components/CatalogPage/DefaultCatalogPage.tsx @@ -62,11 +62,11 @@ export function BaseCatalogPage(props: BaseCatalogPageProps) { const { t } = useTranslationRef(catalogTranslationRef); return ( - + All your software catalog entities diff --git a/plugins/catalog/src/components/CatalogSearchResultListItem/CatalogSearchResultListItem.tsx b/plugins/catalog/src/components/CatalogSearchResultListItem/CatalogSearchResultListItem.tsx index 06906aecc1..4c6b567d8a 100644 --- a/plugins/catalog/src/components/CatalogSearchResultListItem/CatalogSearchResultListItem.tsx +++ b/plugins/catalog/src/components/CatalogSearchResultListItem/CatalogSearchResultListItem.tsx @@ -107,6 +107,7 @@ export function CatalogSearchResultListItem( /> {result.kind && } + {result.type && } {result.lifecycle && ( )} diff --git a/plugins/catalog/src/translation.ts b/plugins/catalog/src/translation.ts index 2d1e3f2c8a..bd27069fde 100644 --- a/plugins/catalog/src/translation.ts +++ b/plugins/catalog/src/translation.ts @@ -20,7 +20,9 @@ import { createTranslationRef } from '@backstage/core-plugin-api/alpha'; export const catalogTranslationRef = createTranslationRef({ id: 'catalog', messages: { - catalog_page_title: `{{orgName}} Catalog`, - catalog_page_create_button_title: 'Create', + indexPage: { + title: `{{orgName}} Catalog`, + createButtonTitle: 'Create', + }, }, }); diff --git a/plugins/cicd-statistics-module-gitlab/CHANGELOG.md b/plugins/cicd-statistics-module-gitlab/CHANGELOG.md index 06bce48963..0a86a58cc5 100644 --- a/plugins/cicd-statistics-module-gitlab/CHANGELOG.md +++ b/plugins/cicd-statistics-module-gitlab/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-cicd-statistics-module-gitlab +## 0.1.24-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-cicd-statistics@0.1.30-next.1 + - @backstage/catalog-model@1.4.3 + ## 0.1.24-next.0 ### Patch Changes diff --git a/plugins/cicd-statistics-module-gitlab/package.json b/plugins/cicd-statistics-module-gitlab/package.json index 15c0602394..534060d805 100644 --- a/plugins/cicd-statistics-module-gitlab/package.json +++ b/plugins/cicd-statistics-module-gitlab/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-cicd-statistics-module-gitlab", "description": "CI/CD Statistics plugin module; Gitlab CICD", - "version": "0.1.24-next.0", + "version": "0.1.24-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/cicd-statistics/CHANGELOG.md b/plugins/cicd-statistics/CHANGELOG.md index 0cdd53dc87..187f00e25f 100644 --- a/plugins/cicd-statistics/CHANGELOG.md +++ b/plugins/cicd-statistics/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-cicd-statistics +## 0.1.30-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + ## 0.1.30-next.0 ### Patch Changes diff --git a/plugins/cicd-statistics/package.json b/plugins/cicd-statistics/package.json index 35a44e3fd0..f2bf5b9a64 100644 --- a/plugins/cicd-statistics/package.json +++ b/plugins/cicd-statistics/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-cicd-statistics", "description": "A frontend plugin visualizing CI/CD pipeline statistics (build time)", - "version": "0.1.30-next.0", + "version": "0.1.30-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/circleci/CHANGELOG.md b/plugins/circleci/CHANGELOG.md index 79a0b71b57..4740e2afb6 100644 --- a/plugins/circleci/CHANGELOG.md +++ b/plugins/circleci/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-circleci +## 0.3.28-next.1 + +### Patch Changes + +- 375b6f7d68: CircelCI plugin moved permanently +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/theme@0.5.0-next.0 + ## 0.3.28-next.0 ### Patch Changes diff --git a/plugins/circleci/README.md b/plugins/circleci/README.md index f468ab37e2..86af7081c7 100644 --- a/plugins/circleci/README.md +++ b/plugins/circleci/README.md @@ -1,6 +1,7 @@ # CircleCI Plugin -Website: [https://circleci.com/](https://circleci.com/) +> [!IMPORTANT] +> This plugin is now developed & maintained by CircleCI. Please refer to [their up-to-date documentation](https://github.com/CircleCI-Public/backstage-plugin) & [plugin repository](https://github.com/CircleCI-Public/backstage-plugin/) for help. ## Screenshots @@ -14,7 +15,7 @@ Website: [https://circleci.com/](https://circleci.com/) ```bash # From your Backstage root directory -yarn add --cwd packages/app @backstage/plugin-circleci +yarn add --cwd packages/app @circleci/backstage-plugin ``` 2. Add the `EntityCircleCIContent` extension to the entity page in your app: @@ -24,7 +25,7 @@ yarn add --cwd packages/app @backstage/plugin-circleci import { EntityCircleCIContent, isCircleCIAvailable, -} from '@backstage/plugin-circleci'; +} from '@circleci/backstage-plugin'; // For example in the CI/CD section const cicdContent = ( diff --git a/plugins/circleci/package.json b/plugins/circleci/package.json index 7075a383a8..b3dadfb55b 100644 --- a/plugins/circleci/package.json +++ b/plugins/circleci/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-circleci", "description": "A Backstage plugin that integrates towards Circle CI", - "version": "0.3.28-next.0", + "version": "0.3.28-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/cloudbuild/CHANGELOG.md b/plugins/cloudbuild/CHANGELOG.md index 32ea259d87..b4403f7678 100644 --- a/plugins/cloudbuild/CHANGELOG.md +++ b/plugins/cloudbuild/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-cloudbuild +## 0.3.28-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/theme@0.5.0-next.0 + ## 0.3.28-next.0 ### Patch Changes diff --git a/plugins/cloudbuild/package.json b/plugins/cloudbuild/package.json index 1caaf87edd..f480404563 100644 --- a/plugins/cloudbuild/package.json +++ b/plugins/cloudbuild/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-cloudbuild", "description": "A Backstage plugin that integrates towards Google Cloud Build", - "version": "0.3.28-next.0", + "version": "0.3.28-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/code-climate/CHANGELOG.md b/plugins/code-climate/CHANGELOG.md index dd10502cf0..b10c55fe3a 100644 --- a/plugins/code-climate/CHANGELOG.md +++ b/plugins/code-climate/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-code-climate +## 0.1.28-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/theme@0.5.0-next.0 + ## 0.1.28-next.0 ### Patch Changes diff --git a/plugins/code-climate/package.json b/plugins/code-climate/package.json index 5210337fda..f0800fce83 100644 --- a/plugins/code-climate/package.json +++ b/plugins/code-climate/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-code-climate", - "version": "0.1.28-next.0", + "version": "0.1.28-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/code-coverage-backend/CHANGELOG.md b/plugins/code-coverage-backend/CHANGELOG.md index 08fe1692d8..37aedd5dce 100644 --- a/plugins/code-coverage-backend/CHANGELOG.md +++ b/plugins/code-coverage-backend/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-code-coverage-backend +## 0.2.22-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.5.0-next.0 + - @backstage/integration@1.8.0-next.1 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-auth-node@0.4.2-next.1 + ## 0.2.22-next.0 ### Patch Changes diff --git a/plugins/code-coverage-backend/package.json b/plugins/code-coverage-backend/package.json index 4bd83b92f6..61f21d7438 100644 --- a/plugins/code-coverage-backend/package.json +++ b/plugins/code-coverage-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-code-coverage-backend", "description": "A Backstage backend plugin that helps you keep track of your code coverage", - "version": "0.2.22-next.0", + "version": "0.2.22-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/code-coverage/CHANGELOG.md b/plugins/code-coverage/CHANGELOG.md index a29cf758a8..d64996306c 100644 --- a/plugins/code-coverage/CHANGELOG.md +++ b/plugins/code-coverage/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-code-coverage +## 0.2.21-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + ## 0.2.21-next.0 ### Patch Changes diff --git a/plugins/code-coverage/package.json b/plugins/code-coverage/package.json index c4fda6c18b..e03d65a7b2 100644 --- a/plugins/code-coverage/package.json +++ b/plugins/code-coverage/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-code-coverage", "description": "A Backstage plugin that helps you keep track of your code coverage", - "version": "0.2.21-next.0", + "version": "0.2.21-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/codescene/CHANGELOG.md b/plugins/codescene/CHANGELOG.md index 36af453283..2d7f295123 100644 --- a/plugins/codescene/CHANGELOG.md +++ b/plugins/codescene/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-codescene +## 0.1.20-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + ## 0.1.20-next.0 ### Patch Changes diff --git a/plugins/codescene/package.json b/plugins/codescene/package.json index 9296b76c55..d21f66ac2b 100644 --- a/plugins/codescene/package.json +++ b/plugins/codescene/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-codescene", - "version": "0.1.20-next.0", + "version": "0.1.20-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/config-schema/CHANGELOG.md b/plugins/config-schema/CHANGELOG.md index b05b2e5e85..b57892c764 100644 --- a/plugins/config-schema/CHANGELOG.md +++ b/plugins/config-schema/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-config-schema +## 0.1.48-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + - @backstage/types@1.1.1 + ## 0.1.48-next.0 ### Patch Changes diff --git a/plugins/config-schema/package.json b/plugins/config-schema/package.json index da59ec9de9..12740e5f49 100644 --- a/plugins/config-schema/package.json +++ b/plugins/config-schema/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-config-schema", "description": "A Backstage plugin that lets you browse the configuration schema of your app", - "version": "0.1.48-next.0", + "version": "0.1.48-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/cost-insights/CHANGELOG.md b/plugins/cost-insights/CHANGELOG.md index 471675b140..c941b62f66 100644 --- a/plugins/cost-insights/CHANGELOG.md +++ b/plugins/cost-insights/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-cost-insights +## 0.12.17-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/theme@0.5.0-next.0 + - @backstage/plugin-cost-insights-common@0.1.2 + ## 0.12.17-next.0 ### Patch Changes diff --git a/plugins/cost-insights/package.json b/plugins/cost-insights/package.json index 88166e729d..c9258bc464 100644 --- a/plugins/cost-insights/package.json +++ b/plugins/cost-insights/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-cost-insights", "description": "A Backstage plugin that helps you keep track of your cloud spend", - "version": "0.12.17-next.0", + "version": "0.12.17-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/devtools-backend/CHANGELOG.md b/plugins/devtools-backend/CHANGELOG.md index a1e9a0d4fd..655743c818 100644 --- a/plugins/devtools-backend/CHANGELOG.md +++ b/plugins/devtools-backend/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-devtools-backend +## 0.2.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/cli-common@0.1.13 + - @backstage/config@1.1.1 + - @backstage/config-loader@1.5.3 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.2-next.1 + - @backstage/plugin-devtools-common@0.1.6 + - @backstage/plugin-permission-common@0.7.10 + - @backstage/plugin-permission-node@0.7.19-next.1 + ## 0.2.5-next.0 ### Patch Changes diff --git a/plugins/devtools-backend/package.json b/plugins/devtools-backend/package.json index f4976a695f..b445c25ae6 100644 --- a/plugins/devtools-backend/package.json +++ b/plugins/devtools-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-devtools-backend", - "version": "0.2.5-next.0", + "version": "0.2.5-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/devtools/CHANGELOG.md b/plugins/devtools/CHANGELOG.md index f00218080d..a71d43fbd8 100644 --- a/plugins/devtools/CHANGELOG.md +++ b/plugins/devtools/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-devtools +## 0.1.7-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-permission-react@0.4.18-next.1 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + - @backstage/types@1.1.1 + - @backstage/plugin-devtools-common@0.1.6 + ## 0.1.7-next.0 ### Patch Changes diff --git a/plugins/devtools/package.json b/plugins/devtools/package.json index 9f0318df40..67cbae9802 100644 --- a/plugins/devtools/package.json +++ b/plugins/devtools/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-devtools", - "version": "0.1.7-next.0", + "version": "0.1.7-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/dynatrace/CHANGELOG.md b/plugins/dynatrace/CHANGELOG.md index 180444eabd..102f88c562 100644 --- a/plugins/dynatrace/CHANGELOG.md +++ b/plugins/dynatrace/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-dynatrace +## 8.0.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/theme@0.5.0-next.0 + ## 8.0.2-next.0 ### Patch Changes diff --git a/plugins/dynatrace/package.json b/plugins/dynatrace/package.json index cd09c89a1c..552ebfaed3 100644 --- a/plugins/dynatrace/package.json +++ b/plugins/dynatrace/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-dynatrace", - "version": "8.0.2-next.0", + "version": "8.0.2-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/entity-feedback-backend/CHANGELOG.md b/plugins/entity-feedback-backend/CHANGELOG.md index 441859172c..101e8b0654 100644 --- a/plugins/entity-feedback-backend/CHANGELOG.md +++ b/plugins/entity-feedback-backend/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-entity-feedback-backend +## 0.2.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.5.0-next.0 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/plugin-auth-node@0.4.2-next.1 + - @backstage/plugin-entity-feedback-common@0.1.3 + ## 0.2.5-next.0 ### Patch Changes diff --git a/plugins/entity-feedback-backend/package.json b/plugins/entity-feedback-backend/package.json index e13f20fa64..c232a67307 100644 --- a/plugins/entity-feedback-backend/package.json +++ b/plugins/entity-feedback-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-entity-feedback-backend", - "version": "0.2.5-next.0", + "version": "0.2.5-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/entity-feedback/CHANGELOG.md b/plugins/entity-feedback/CHANGELOG.md index e890abd312..622d5c98f8 100644 --- a/plugins/entity-feedback/CHANGELOG.md +++ b/plugins/entity-feedback/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-entity-feedback +## 0.2.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + - @backstage/plugin-entity-feedback-common@0.1.3 + ## 0.2.11-next.0 ### Patch Changes diff --git a/plugins/entity-feedback/package.json b/plugins/entity-feedback/package.json index 4569a53c34..ab6c40e51e 100644 --- a/plugins/entity-feedback/package.json +++ b/plugins/entity-feedback/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-entity-feedback", - "version": "0.2.11-next.0", + "version": "0.2.11-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/entity-validation/CHANGELOG.md b/plugins/entity-validation/CHANGELOG.md index 3c5dd92fa1..843be5d6d1 100644 --- a/plugins/entity-validation/CHANGELOG.md +++ b/plugins/entity-validation/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-entity-validation +## 0.1.13-next.1 + +### Patch Changes + +- a518c5a25b: Updated dependency `@react-hookz/web` to `^23.0.0`. +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-client@1.5.0-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + - @backstage/plugin-catalog-common@1.0.18 + ## 0.1.13-next.0 ### Patch Changes diff --git a/plugins/entity-validation/package.json b/plugins/entity-validation/package.json index 8657c2cbdc..dc0bd93565 100644 --- a/plugins/entity-validation/package.json +++ b/plugins/entity-validation/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-entity-validation", - "version": "0.1.13-next.0", + "version": "0.1.13-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -43,7 +43,7 @@ "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.61", - "@react-hookz/web": "^20.0.0", + "@react-hookz/web": "^23.0.0", "@types/react": "^16.13.1 || ^17.0.0", "@uiw/react-codemirror": "^4.9.3", "lodash": "^4.17.21", diff --git a/plugins/events-backend-module-aws-sqs/CHANGELOG.md b/plugins/events-backend-module-aws-sqs/CHANGELOG.md index c4f5ad2d51..113a641795 100644 --- a/plugins/events-backend-module-aws-sqs/CHANGELOG.md +++ b/plugins/events-backend-module-aws-sqs/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-events-backend-module-aws-sqs +## 0.2.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/backend-tasks@0.5.13-next.1 + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + - @backstage/plugin-events-node@0.2.17-next.1 + ## 0.2.11-next.0 ### Patch Changes diff --git a/plugins/events-backend-module-aws-sqs/alpha-api-report.md b/plugins/events-backend-module-aws-sqs/api-report-alpha.md similarity index 100% rename from plugins/events-backend-module-aws-sqs/alpha-api-report.md rename to plugins/events-backend-module-aws-sqs/api-report-alpha.md diff --git a/plugins/events-backend-module-aws-sqs/package.json b/plugins/events-backend-module-aws-sqs/package.json index 74a36221e3..627aedd0be 100644 --- a/plugins/events-backend-module-aws-sqs/package.json +++ b/plugins/events-backend-module-aws-sqs/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-backend-module-aws-sqs", - "version": "0.2.11-next.0", + "version": "0.2.11-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/events-backend-module-aws-sqs/src/service/eventsModuleAwsSqsConsumingEventPublisher.ts b/plugins/events-backend-module-aws-sqs/src/service/eventsModuleAwsSqsConsumingEventPublisher.ts index 9045047094..eabab94708 100644 --- a/plugins/events-backend-module-aws-sqs/src/service/eventsModuleAwsSqsConsumingEventPublisher.ts +++ b/plugins/events-backend-module-aws-sqs/src/service/eventsModuleAwsSqsConsumingEventPublisher.ts @@ -29,7 +29,7 @@ import { AwsSqsConsumingEventPublisher } from '../publisher/AwsSqsConsumingEvent */ export const eventsModuleAwsSqsConsumingEventPublisher = createBackendModule({ pluginId: 'events', - moduleId: 'awsSqsConsumingEventPublisher', + moduleId: 'aws-sqs-consuming-event-publisher', register(env) { env.registerInit({ deps: { diff --git a/plugins/events-backend-module-azure/CHANGELOG.md b/plugins/events-backend-module-azure/CHANGELOG.md index 6ff642a8e3..e5ae8e0536 100644 --- a/plugins/events-backend-module-azure/CHANGELOG.md +++ b/plugins/events-backend-module-azure/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-events-backend-module-azure +## 0.1.18-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/plugin-events-node@0.2.17-next.1 + ## 0.1.18-next.0 ### Patch Changes diff --git a/plugins/events-backend-module-azure/alpha-api-report.md b/plugins/events-backend-module-azure/api-report-alpha.md similarity index 100% rename from plugins/events-backend-module-azure/alpha-api-report.md rename to plugins/events-backend-module-azure/api-report-alpha.md diff --git a/plugins/events-backend-module-azure/package.json b/plugins/events-backend-module-azure/package.json index c4df3cc562..7908478ba7 100644 --- a/plugins/events-backend-module-azure/package.json +++ b/plugins/events-backend-module-azure/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-backend-module-azure", - "version": "0.1.18-next.0", + "version": "0.1.18-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/events-backend-module-azure/src/service/eventsModuleAzureDevOpsEventRouter.ts b/plugins/events-backend-module-azure/src/service/eventsModuleAzureDevOpsEventRouter.ts index 36f3acada0..50bc384502 100644 --- a/plugins/events-backend-module-azure/src/service/eventsModuleAzureDevOpsEventRouter.ts +++ b/plugins/events-backend-module-azure/src/service/eventsModuleAzureDevOpsEventRouter.ts @@ -27,7 +27,7 @@ import { AzureDevOpsEventRouter } from '../router/AzureDevOpsEventRouter'; */ export const eventsModuleAzureDevOpsEventRouter = createBackendModule({ pluginId: 'events', - moduleId: 'azureDevOpsEventRouter', + moduleId: 'azure-dev-ops-event-router', register(env) { env.registerInit({ deps: { diff --git a/plugins/events-backend-module-bitbucket-cloud/CHANGELOG.md b/plugins/events-backend-module-bitbucket-cloud/CHANGELOG.md index 0a74f17134..d33369f7b5 100644 --- a/plugins/events-backend-module-bitbucket-cloud/CHANGELOG.md +++ b/plugins/events-backend-module-bitbucket-cloud/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-events-backend-module-bitbucket-cloud +## 0.1.18-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/plugin-events-node@0.2.17-next.1 + ## 0.1.18-next.0 ### Patch Changes diff --git a/plugins/events-backend-module-bitbucket-cloud/alpha-api-report.md b/plugins/events-backend-module-bitbucket-cloud/api-report-alpha.md similarity index 100% rename from plugins/events-backend-module-bitbucket-cloud/alpha-api-report.md rename to plugins/events-backend-module-bitbucket-cloud/api-report-alpha.md diff --git a/plugins/events-backend-module-bitbucket-cloud/package.json b/plugins/events-backend-module-bitbucket-cloud/package.json index 68783b5478..ed1c10f433 100644 --- a/plugins/events-backend-module-bitbucket-cloud/package.json +++ b/plugins/events-backend-module-bitbucket-cloud/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-backend-module-bitbucket-cloud", - "version": "0.1.18-next.0", + "version": "0.1.18-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/events-backend-module-bitbucket-cloud/src/service/eventsModuleBitbucketCloudEventRouter.ts b/plugins/events-backend-module-bitbucket-cloud/src/service/eventsModuleBitbucketCloudEventRouter.ts index 60a8cc31c8..841a463001 100644 --- a/plugins/events-backend-module-bitbucket-cloud/src/service/eventsModuleBitbucketCloudEventRouter.ts +++ b/plugins/events-backend-module-bitbucket-cloud/src/service/eventsModuleBitbucketCloudEventRouter.ts @@ -27,7 +27,7 @@ import { BitbucketCloudEventRouter } from '../router/BitbucketCloudEventRouter'; */ export const eventsModuleBitbucketCloudEventRouter = createBackendModule({ pluginId: 'events', - moduleId: 'bitbucketCloudEventRouter', + moduleId: 'bitbucket-cloud-event-router', register(env) { env.registerInit({ deps: { diff --git a/plugins/events-backend-module-gerrit/CHANGELOG.md b/plugins/events-backend-module-gerrit/CHANGELOG.md index 300f445915..156a8f24d8 100644 --- a/plugins/events-backend-module-gerrit/CHANGELOG.md +++ b/plugins/events-backend-module-gerrit/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-events-backend-module-gerrit +## 0.1.18-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/plugin-events-node@0.2.17-next.1 + ## 0.1.18-next.0 ### Patch Changes diff --git a/plugins/events-backend-module-gerrit/alpha-api-report.md b/plugins/events-backend-module-gerrit/api-report-alpha.md similarity index 100% rename from plugins/events-backend-module-gerrit/alpha-api-report.md rename to plugins/events-backend-module-gerrit/api-report-alpha.md diff --git a/plugins/events-backend-module-gerrit/package.json b/plugins/events-backend-module-gerrit/package.json index 882aa89f5e..24a70d19bf 100644 --- a/plugins/events-backend-module-gerrit/package.json +++ b/plugins/events-backend-module-gerrit/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-backend-module-gerrit", - "version": "0.1.18-next.0", + "version": "0.1.18-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/events-backend-module-gerrit/src/service/eventsModuleGerritEventRouter.ts b/plugins/events-backend-module-gerrit/src/service/eventsModuleGerritEventRouter.ts index 8d490ea0b2..780ff7f878 100644 --- a/plugins/events-backend-module-gerrit/src/service/eventsModuleGerritEventRouter.ts +++ b/plugins/events-backend-module-gerrit/src/service/eventsModuleGerritEventRouter.ts @@ -27,7 +27,7 @@ import { GerritEventRouter } from '../router/GerritEventRouter'; */ export const eventsModuleGerritEventRouter = createBackendModule({ pluginId: 'events', - moduleId: 'gerritEventRouter', + moduleId: 'gerrit-event-router', register(env) { env.registerInit({ deps: { diff --git a/plugins/events-backend-module-github/CHANGELOG.md b/plugins/events-backend-module-github/CHANGELOG.md index 0d68ab29f1..725de56c46 100644 --- a/plugins/events-backend-module-github/CHANGELOG.md +++ b/plugins/events-backend-module-github/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-events-backend-module-github +## 0.1.18-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/config@1.1.1 + - @backstage/plugin-events-node@0.2.17-next.1 + ## 0.1.18-next.0 ### Patch Changes diff --git a/plugins/events-backend-module-github/alpha-api-report.md b/plugins/events-backend-module-github/api-report-alpha.md similarity index 100% rename from plugins/events-backend-module-github/alpha-api-report.md rename to plugins/events-backend-module-github/api-report-alpha.md diff --git a/plugins/events-backend-module-github/package.json b/plugins/events-backend-module-github/package.json index acc0a7fcf9..fe57a4ffbd 100644 --- a/plugins/events-backend-module-github/package.json +++ b/plugins/events-backend-module-github/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-backend-module-github", - "version": "0.1.18-next.0", + "version": "0.1.18-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/events-backend-module-github/src/service/eventsModuleGithubEventRouter.ts b/plugins/events-backend-module-github/src/service/eventsModuleGithubEventRouter.ts index 17485098c2..093307dfaf 100644 --- a/plugins/events-backend-module-github/src/service/eventsModuleGithubEventRouter.ts +++ b/plugins/events-backend-module-github/src/service/eventsModuleGithubEventRouter.ts @@ -27,7 +27,7 @@ import { GithubEventRouter } from '../router/GithubEventRouter'; */ export const eventsModuleGithubEventRouter = createBackendModule({ pluginId: 'events', - moduleId: 'githubEventRouter', + moduleId: 'github-event-router', register(env) { env.registerInit({ deps: { diff --git a/plugins/events-backend-module-github/src/service/eventsModuleGithubWebhook.ts b/plugins/events-backend-module-github/src/service/eventsModuleGithubWebhook.ts index b4bd666036..647b21c364 100644 --- a/plugins/events-backend-module-github/src/service/eventsModuleGithubWebhook.ts +++ b/plugins/events-backend-module-github/src/service/eventsModuleGithubWebhook.ts @@ -30,7 +30,7 @@ import { createGithubSignatureValidator } from '../http/createGithubSignatureVal */ export const eventsModuleGithubWebhook = createBackendModule({ pluginId: 'events', - moduleId: 'githubWebhook', + moduleId: 'github-webhook', register(env) { env.registerInit({ deps: { diff --git a/plugins/events-backend-module-gitlab/CHANGELOG.md b/plugins/events-backend-module-gitlab/CHANGELOG.md index ce34e6fe75..c13f8865d5 100644 --- a/plugins/events-backend-module-gitlab/CHANGELOG.md +++ b/plugins/events-backend-module-gitlab/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-events-backend-module-gitlab +## 0.1.18-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/config@1.1.1 + - @backstage/plugin-events-node@0.2.17-next.1 + ## 0.1.18-next.0 ### Patch Changes diff --git a/plugins/events-backend-module-gitlab/alpha-api-report.md b/plugins/events-backend-module-gitlab/api-report-alpha.md similarity index 100% rename from plugins/events-backend-module-gitlab/alpha-api-report.md rename to plugins/events-backend-module-gitlab/api-report-alpha.md diff --git a/plugins/events-backend-module-gitlab/package.json b/plugins/events-backend-module-gitlab/package.json index 794e6cba27..b8000bbe2e 100644 --- a/plugins/events-backend-module-gitlab/package.json +++ b/plugins/events-backend-module-gitlab/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-backend-module-gitlab", - "version": "0.1.18-next.0", + "version": "0.1.18-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/events-backend-module-gitlab/src/service/eventsModuleGitlabEventRouter.ts b/plugins/events-backend-module-gitlab/src/service/eventsModuleGitlabEventRouter.ts index 3425601529..fc44e95057 100644 --- a/plugins/events-backend-module-gitlab/src/service/eventsModuleGitlabEventRouter.ts +++ b/plugins/events-backend-module-gitlab/src/service/eventsModuleGitlabEventRouter.ts @@ -27,7 +27,7 @@ import { GitlabEventRouter } from '../router/GitlabEventRouter'; */ export const eventsModuleGitlabEventRouter = createBackendModule({ pluginId: 'events', - moduleId: 'gitlabEventRouter', + moduleId: 'gitlab-event-router', register(env) { env.registerInit({ deps: { diff --git a/plugins/events-backend-module-gitlab/src/service/eventsModuleGitlabWebhook.ts b/plugins/events-backend-module-gitlab/src/service/eventsModuleGitlabWebhook.ts index bd596b4baf..77bca7236b 100644 --- a/plugins/events-backend-module-gitlab/src/service/eventsModuleGitlabWebhook.ts +++ b/plugins/events-backend-module-gitlab/src/service/eventsModuleGitlabWebhook.ts @@ -32,7 +32,7 @@ import { createGitlabTokenValidator } from '../http/createGitlabTokenValidator'; */ export const eventsModuleGitlabWebhook = createBackendModule({ pluginId: 'events', - moduleId: 'gitlabWebhook', + moduleId: 'gitlab-webhook', register(env) { env.registerInit({ deps: { diff --git a/plugins/events-backend-test-utils/CHANGELOG.md b/plugins/events-backend-test-utils/CHANGELOG.md index 505bdcd256..96b8d616ea 100644 --- a/plugins/events-backend-test-utils/CHANGELOG.md +++ b/plugins/events-backend-test-utils/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-events-backend-test-utils +## 0.1.18-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.2.17-next.1 + ## 0.1.18-next.0 ### Patch Changes diff --git a/plugins/events-backend-test-utils/package.json b/plugins/events-backend-test-utils/package.json index e82047e483..ddb5ab31d1 100644 --- a/plugins/events-backend-test-utils/package.json +++ b/plugins/events-backend-test-utils/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-events-backend-test-utils", "description": "The plugin-events-backend-test-utils for @backstage/plugin-events-node", - "version": "0.1.18-next.0", + "version": "0.1.18-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/events-backend/CHANGELOG.md b/plugins/events-backend/CHANGELOG.md index 2c0c2cbf70..80cfe97cc2 100644 --- a/plugins/events-backend/CHANGELOG.md +++ b/plugins/events-backend/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-events-backend +## 0.2.17-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/config@1.1.1 + - @backstage/plugin-events-node@0.2.17-next.1 + ## 0.2.17-next.0 ### Patch Changes diff --git a/plugins/events-backend/README.md b/plugins/events-backend/README.md index 969e1bbcb2..29c420cef2 100644 --- a/plugins/events-backend/README.md +++ b/plugins/events-backend/README.md @@ -201,7 +201,7 @@ import { eventsExtensionPoint } from '@backstage/plugin-events-node'; export const yourModuleEventsModule = createBackendModule({ pluginId: 'events', - moduleId: 'yourModule', + moduleId: 'your-module', register(env) { // [...] env.registerInit({ @@ -252,7 +252,7 @@ import { eventsExtensionPoint } from '@backstage/plugin-events-node'; export const eventsModuleYourFeature = createBackendModule({ pluginId: 'events', - moduleId: 'yourFeature', + moduleId: 'your-feature', register(env) { // [...] env.registerInit({ diff --git a/plugins/events-backend/alpha-api-report.md b/plugins/events-backend/api-report-alpha.md similarity index 100% rename from plugins/events-backend/alpha-api-report.md rename to plugins/events-backend/api-report-alpha.md diff --git a/plugins/events-backend/package.json b/plugins/events-backend/package.json index 1288abf0a3..f6c3398952 100644 --- a/plugins/events-backend/package.json +++ b/plugins/events-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-backend", - "version": "0.2.17-next.0", + "version": "0.2.17-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/events-node/CHANGELOG.md b/plugins/events-node/CHANGELOG.md index 787e3daa7b..4c2b466971 100644 --- a/plugins/events-node/CHANGELOG.md +++ b/plugins/events-node/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-events-node +## 0.2.17-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.8-next.1 + ## 0.2.17-next.0 ### Patch Changes diff --git a/plugins/events-node/alpha-api-report.md b/plugins/events-node/api-report-alpha.md similarity index 100% rename from plugins/events-node/alpha-api-report.md rename to plugins/events-node/api-report-alpha.md diff --git a/plugins/events-node/package.json b/plugins/events-node/package.json index 531138ff69..b56c54125f 100644 --- a/plugins/events-node/package.json +++ b/plugins/events-node/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-events-node", "description": "The plugin-events-node module for @backstage/plugin-events-backend", - "version": "0.2.17-next.0", + "version": "0.2.17-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/example-todo-list-backend/CHANGELOG.md b/plugins/example-todo-list-backend/CHANGELOG.md index 1cc13d252a..36106c9b4b 100644 --- a/plugins/example-todo-list-backend/CHANGELOG.md +++ b/plugins/example-todo-list-backend/CHANGELOG.md @@ -1,5 +1,16 @@ # @internal/plugin-todo-list-backend +## 1.0.20-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-auth-node@0.4.2-next.1 + ## 1.0.20-next.0 ### Patch Changes diff --git a/plugins/example-todo-list-backend/package.json b/plugins/example-todo-list-backend/package.json index a04643c4c2..ad2997423a 100644 --- a/plugins/example-todo-list-backend/package.json +++ b/plugins/example-todo-list-backend/package.json @@ -1,6 +1,6 @@ { "name": "@internal/plugin-todo-list-backend", - "version": "1.0.20-next.0", + "version": "1.0.20-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/example-todo-list/CHANGELOG.md b/plugins/example-todo-list/CHANGELOG.md index b3a8a8f93d..0e7e213137 100644 --- a/plugins/example-todo-list/CHANGELOG.md +++ b/plugins/example-todo-list/CHANGELOG.md @@ -1,5 +1,14 @@ # @internal/plugin-todo-list +## 1.0.20-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/theme@0.5.0-next.0 + ## 1.0.20-next.0 ### Patch Changes diff --git a/plugins/example-todo-list/package.json b/plugins/example-todo-list/package.json index d08efb9d22..9538280e94 100644 --- a/plugins/example-todo-list/package.json +++ b/plugins/example-todo-list/package.json @@ -1,6 +1,6 @@ { "name": "@internal/plugin-todo-list", - "version": "1.0.20-next.0", + "version": "1.0.20-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/explore-backend/CHANGELOG.md b/plugins/explore-backend/CHANGELOG.md index 9e7c38e8f8..c880ce5374 100644 --- a/plugins/explore-backend/CHANGELOG.md +++ b/plugins/explore-backend/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-explore-backend +## 0.0.18-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + - @backstage/plugin-explore-common@0.0.2 + - @backstage/plugin-search-backend-module-explore@0.1.12-next.1 + - @backstage/plugin-search-common@1.2.8 + ## 0.0.18-next.0 ### Patch Changes diff --git a/plugins/explore-backend/package.json b/plugins/explore-backend/package.json index ac94ef4c05..8f53a106d6 100644 --- a/plugins/explore-backend/package.json +++ b/plugins/explore-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-explore-backend", - "version": "0.0.18-next.0", + "version": "0.0.18-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/explore-react/CHANGELOG.md b/plugins/explore-react/CHANGELOG.md index 59a6c265d0..08217fd6de 100644 --- a/plugins/explore-react/CHANGELOG.md +++ b/plugins/explore-react/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-explore-react +## 0.0.34-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-explore-common@0.0.2 + ## 0.0.34-next.0 ### Patch Changes diff --git a/plugins/explore-react/package.json b/plugins/explore-react/package.json index 65e554db77..aee56ab3fd 100644 --- a/plugins/explore-react/package.json +++ b/plugins/explore-react/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-explore-react", "description": "A frontend library for Backstage plugins that want to interact with the explore plugin", - "version": "0.0.34-next.0", + "version": "0.0.34-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/explore/CHANGELOG.md b/plugins/explore/CHANGELOG.md index 091216334f..8a42d288fe 100644 --- a/plugins/explore/CHANGELOG.md +++ b/plugins/explore/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-explore +## 0.4.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.4.0-next.1 + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/plugin-search-react@1.7.4-next.1 + - @backstage/plugin-explore-react@0.0.34-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + - @backstage/plugin-explore-common@0.0.2 + - @backstage/plugin-search-common@1.2.8 + ## 0.4.14-next.0 ### Patch Changes diff --git a/plugins/explore/alpha-api-report.md b/plugins/explore/api-report-alpha.md similarity index 75% rename from plugins/explore/alpha-api-report.md rename to plugins/explore/api-report-alpha.md index 8670338f3e..4d1afa89bf 100644 --- a/plugins/explore/alpha-api-report.md +++ b/plugins/explore/api-report-alpha.md @@ -4,14 +4,14 @@ ```ts import { BackstagePlugin } from '@backstage/frontend-plugin-api'; -import { Extension } from '@backstage/frontend-plugin-api'; +import { ExtensionDefinition } from '@backstage/frontend-plugin-api'; // @alpha (undocumented) const _default: BackstagePlugin<{}, {}>; export default _default; // @alpha (undocumented) -export const ExploreSearchResultListItemExtension: Extension<{ +export const ExploreSearchResultListItemExtension: ExtensionDefinition<{ noTrack?: boolean | undefined; }>; diff --git a/plugins/explore/package.json b/plugins/explore/package.json index b1169ae515..382b3b6691 100644 --- a/plugins/explore/package.json +++ b/plugins/explore/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-explore", "description": "A Backstage plugin for building an exploration page of your software ecosystem", - "version": "0.4.14-next.0", + "version": "0.4.14-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/explore/src/alpha.tsx b/plugins/explore/src/alpha.tsx index 1825af5ddb..f06379c84b 100644 --- a/plugins/explore/src/alpha.tsx +++ b/plugins/explore/src/alpha.tsx @@ -20,7 +20,6 @@ import { createSearchResultListItemExtension } from '@backstage/plugin-search-re /** @alpha */ export const ExploreSearchResultListItemExtension = createSearchResultListItemExtension({ - id: 'explore', predicate: result => result.type === 'tools', component: () => import('./components/ToolSearchResultListItem').then( diff --git a/plugins/firehydrant/CHANGELOG.md b/plugins/firehydrant/CHANGELOG.md index 53b350f331..fff099f63c 100644 --- a/plugins/firehydrant/CHANGELOG.md +++ b/plugins/firehydrant/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-firehydrant +## 0.2.12-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/theme@0.5.0-next.0 + ## 0.2.12-next.0 ### Patch Changes diff --git a/plugins/firehydrant/package.json b/plugins/firehydrant/package.json index 19f4ed58d6..953bebce22 100644 --- a/plugins/firehydrant/package.json +++ b/plugins/firehydrant/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-firehydrant", "description": "A Backstage plugin that integrates towards FireHydrant", - "version": "0.2.12-next.0", + "version": "0.2.12-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/fossa/CHANGELOG.md b/plugins/fossa/CHANGELOG.md index fea4e21868..26e1272aa8 100644 --- a/plugins/fossa/CHANGELOG.md +++ b/plugins/fossa/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-fossa +## 0.2.60-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + ## 0.2.60-next.0 ### Patch Changes diff --git a/plugins/fossa/package.json b/plugins/fossa/package.json index be34560e1b..cca6446a46 100644 --- a/plugins/fossa/package.json +++ b/plugins/fossa/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-fossa", "description": "A Backstage plugin that integrates towards FOSSA", - "version": "0.2.60-next.0", + "version": "0.2.60-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/gcalendar/CHANGELOG.md b/plugins/gcalendar/CHANGELOG.md index e8663cb365..627f65b156 100644 --- a/plugins/gcalendar/CHANGELOG.md +++ b/plugins/gcalendar/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-gcalendar +## 0.3.21-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + ## 0.3.21-next.0 ### Patch Changes diff --git a/plugins/gcalendar/package.json b/plugins/gcalendar/package.json index c75c431f18..8bd2d578e2 100644 --- a/plugins/gcalendar/package.json +++ b/plugins/gcalendar/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-gcalendar", - "version": "0.3.21-next.0", + "version": "0.3.21-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/gcp-projects/CHANGELOG.md b/plugins/gcp-projects/CHANGELOG.md index c1a3f6ca6d..65d1fe430e 100644 --- a/plugins/gcp-projects/CHANGELOG.md +++ b/plugins/gcp-projects/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-gcp-projects +## 0.3.44-next.1 + +### Patch Changes + +- a518c5a25b: Updated dependency `@react-hookz/web` to `^23.0.0`. +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/theme@0.5.0-next.0 + ## 0.3.44-next.0 ### Patch Changes diff --git a/plugins/gcp-projects/package.json b/plugins/gcp-projects/package.json index 1fde4908fa..004309ebb6 100644 --- a/plugins/gcp-projects/package.json +++ b/plugins/gcp-projects/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-gcp-projects", "description": "A Backstage plugin that helps you manage projects in GCP", - "version": "0.3.44-next.0", + "version": "0.3.44-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -40,7 +40,7 @@ "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.61", - "@react-hookz/web": "^20.0.0", + "@react-hookz/web": "^23.0.0", "@types/react": "^16.13.1 || ^17.0.0" }, "peerDependencies": { diff --git a/plugins/git-release-manager/CHANGELOG.md b/plugins/git-release-manager/CHANGELOG.md index 3c4beb18b0..e4737858c3 100644 --- a/plugins/git-release-manager/CHANGELOG.md +++ b/plugins/git-release-manager/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-git-release-manager +## 0.3.40-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/integration@1.8.0-next.1 + - @backstage/theme@0.5.0-next.0 + ## 0.3.40-next.0 ### Patch Changes diff --git a/plugins/git-release-manager/package.json b/plugins/git-release-manager/package.json index c0454d5b3b..b0380670e4 100644 --- a/plugins/git-release-manager/package.json +++ b/plugins/git-release-manager/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-git-release-manager", "description": "A Backstage plugin that helps you manage releases in git", - "version": "0.3.40-next.0", + "version": "0.3.40-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/github-actions/CHANGELOG.md b/plugins/github-actions/CHANGELOG.md index 4466f730c3..78f9b7a699 100644 --- a/plugins/github-actions/CHANGELOG.md +++ b/plugins/github-actions/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-github-actions +## 0.6.9-next.1 + +### Patch Changes + +- 08d7e4676a: Github Workflow Runs UI is modified to show in optional Card view instead of table, with branch selection option +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/integration@1.8.0-next.1 + - @backstage/integration-react@1.1.22-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/theme@0.5.0-next.0 + ## 0.6.9-next.0 ### Patch Changes diff --git a/plugins/github-actions/README.md b/plugins/github-actions/README.md index b94862db2b..d0b5ceb54d 100644 --- a/plugins/github-actions/README.md +++ b/plugins/github-actions/README.md @@ -92,3 +92,19 @@ integrations: - There is a limit of 100 apps for one OAuth client/token pair - The OAuth application must be at the GitHub organization level in order to display the workflows. If you do not see any workflows, confirm the OAuth application was created in the organization and not a specific user account. + +## Optional Workflow Runs Card View + +Github Workflow Runs optional UI to show in Card view instead of table, with branch selection option + +```tsx + +// You can add the tab to any number of pages, the service page is shown as an +// example given here +const serviceEntityPage = ( + + {/* other tabs... */} + + + +``` diff --git a/plugins/github-actions/api-report.md b/plugins/github-actions/api-report.md index 887556f431..7a7eb4e972 100644 --- a/plugins/github-actions/api-report.md +++ b/plugins/github-actions/api-report.md @@ -16,6 +16,17 @@ import { RestEndpointMethodTypes } from '@octokit/rest'; import { RouteRef } from '@backstage/core-plugin-api'; import { ScmAuthApi } from '@backstage/integration-react'; +// @public (undocumented) +export type Branch = { + name: string; +}; + +// @public (undocumented) +export type Branches = { + default_branch: string; + branches: Branch[]; +}; + // @public (undocumented) export enum BuildStatus { // (undocumented) @@ -29,7 +40,7 @@ export enum BuildStatus { } // @public (undocumented) -export const EntityGithubActionsContent: () => JSX_2.Element; +export const EntityGithubActionsContent: (props: RouterProps) => JSX_2.Element; // @public (undocumented) export const EntityLatestGithubActionRunCard: (props: { @@ -106,6 +117,21 @@ export type GithubActionsApi = { }) => Promise< RestEndpointMethodTypes['actions']['downloadJobLogsForWorkflowRun']['response']['data'] >; + listBranches: (options: { + hostname?: string; + owner: string; + repo: string; + page: number; + }) => Promise< + RestEndpointMethodTypes['repos']['listBranches']['response']['data'] + >; + getDefaultBranch: (options: { + hostname?: string; + owner: string; + repo: string; + }) => Promise< + RestEndpointMethodTypes['repos']['get']['response']['data']['default_branch'] + >; }; // @public (undocumented) @@ -124,6 +150,14 @@ export class GithubActionsClient implements GithubActionsApi { RestEndpointMethodTypes['actions']['downloadJobLogsForWorkflowRun']['response']['data'] >; // (undocumented) + getDefaultBranch(options: { + hostname?: string; + owner: string; + repo: string; + }): Promise< + RestEndpointMethodTypes['repos']['get']['response']['data']['default_branch'] + >; + // (undocumented) getWorkflow(options: { hostname?: string; owner: string; @@ -142,6 +176,15 @@ export class GithubActionsClient implements GithubActionsApi { RestEndpointMethodTypes['actions']['getWorkflowRun']['response']['data'] >; // (undocumented) + listBranches(options: { + hostname?: string; + owner: string; + repo: string; + page?: number; + }): Promise< + RestEndpointMethodTypes['repos']['listBranches']['response']['data'] + >; + // (undocumented) listJobsForWorkflowRun(options: { hostname?: string; owner: string; @@ -226,7 +269,13 @@ export const RecentWorkflowRunsCard: (props: { }) => React_2.JSX.Element; // @public (undocumented) -export const Router: () => React_2.JSX.Element; +export const Router: (props: RouterProps) => React_2.JSX.Element; + +// @public (undocumented) +export interface RouterProps { + // (undocumented) + view?: 'cards' | 'table'; +} // @public (undocumented) export type Step = { diff --git a/plugins/github-actions/dev/index.tsx b/plugins/github-actions/dev/index.tsx index 153478ddbc..8321d33930 100644 --- a/plugins/github-actions/dev/index.tsx +++ b/plugins/github-actions/dev/index.tsx @@ -63,6 +63,12 @@ const mockGithubActionsApi: GithubActionsApi = { async reRunWorkflow() { return {} as any; }, + async listBranches() { + return {} as any; + }, + async getDefaultBranch() { + return {} as any; + }, }; createDevApp() diff --git a/plugins/github-actions/docs/card-view.png b/plugins/github-actions/docs/card-view.png new file mode 100644 index 0000000000..ef59c294d4 Binary files /dev/null and b/plugins/github-actions/docs/card-view.png differ diff --git a/plugins/github-actions/package.json b/plugins/github-actions/package.json index fe419c47b7..3d47fa4ed8 100644 --- a/plugins/github-actions/package.json +++ b/plugins/github-actions/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-github-actions", "description": "A Backstage plugin that integrates towards GitHub Actions", - "version": "0.6.9-next.0", + "version": "0.6.9-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/github-actions/src/api/GithubActionsApi.ts b/plugins/github-actions/src/api/GithubActionsApi.ts index 14dbc3210e..6d3b25f93d 100644 --- a/plugins/github-actions/src/api/GithubActionsApi.ts +++ b/plugins/github-actions/src/api/GithubActionsApi.ts @@ -83,4 +83,21 @@ export type GithubActionsApi = { }) => Promise< RestEndpointMethodTypes['actions']['downloadJobLogsForWorkflowRun']['response']['data'] >; + + listBranches: (options: { + hostname?: string; + owner: string; + repo: string; + page: number; + }) => Promise< + RestEndpointMethodTypes['repos']['listBranches']['response']['data'] + >; + + getDefaultBranch: (options: { + hostname?: string; + owner: string; + repo: string; + }) => Promise< + RestEndpointMethodTypes['repos']['get']['response']['data']['default_branch'] + >; }; diff --git a/plugins/github-actions/src/api/GithubActionsClient.ts b/plugins/github-actions/src/api/GithubActionsClient.ts index e427c343e2..734326e61b 100644 --- a/plugins/github-actions/src/api/GithubActionsClient.ts +++ b/plugins/github-actions/src/api/GithubActionsClient.ts @@ -174,4 +174,41 @@ export class GithubActionsClient implements GithubActionsApi { return workflow.data; } + + async listBranches(options: { + hostname?: string; + owner: string; + repo: string; + page?: number; + }): Promise< + RestEndpointMethodTypes['repos']['listBranches']['response']['data'] + > { + const { hostname, owner, repo, page = 0 } = options; + const octokit = await this.getOctokit(hostname); + const response = await octokit.rest.repos.listBranches({ + owner, + repo, + per_page: 100, + page, + }); + + return response.data; + } + + async getDefaultBranch(options: { + hostname?: string; + owner: string; + repo: string; + }): Promise< + RestEndpointMethodTypes['repos']['get']['response']['data']['default_branch'] + > { + const { hostname, owner, repo } = options; + const octokit = await this.getOctokit(hostname); + const response = await octokit.rest.repos.get({ + owner, + repo, + }); + + return response.data.default_branch; + } } diff --git a/plugins/github-actions/src/api/types.ts b/plugins/github-actions/src/api/types.ts index 2e997d9bf7..394289149b 100644 --- a/plugins/github-actions/src/api/types.ts +++ b/plugins/github-actions/src/api/types.ts @@ -49,3 +49,19 @@ export enum BuildStatus { 'pending', 'running', } + +/** @public */ +export type Branch = { + name: string; +}; + +/** @public */ +export type Branches = { + default_branch: string; + branches: Branch[]; +}; + +/** @public */ +export interface RouterProps { + view?: 'cards' | 'table'; +} diff --git a/plugins/github-actions/src/components/Router.tsx b/plugins/github-actions/src/components/Router.tsx index e9a40f13f4..ff4e6bfa44 100644 --- a/plugins/github-actions/src/components/Router.tsx +++ b/plugins/github-actions/src/components/Router.tsx @@ -23,15 +23,18 @@ import { import { Routes, Route } from 'react-router-dom'; import { buildRouteRef } from '../routes'; import { WorkflowRunDetails } from './WorkflowRunDetails'; +import { WorkflowRunsCard } from './WorkflowRunsCard'; import { WorkflowRunsTable } from './WorkflowRunsTable'; import { GITHUB_ACTIONS_ANNOTATION } from './getProjectNameFromEntity'; +import { RouterProps } from '../api/types'; /** @public */ export const isGithubActionsAvailable = (entity: Entity) => Boolean(entity.metadata.annotations?.[GITHUB_ACTIONS_ANNOTATION]); /** @public */ -export const Router = () => { +export const Router = (props: RouterProps) => { + const { view = 'table' } = props; const { entity } = useEntity(); if (!isGithubActionsAvailable(entity)) { @@ -39,14 +42,21 @@ export const Router = () => { ); } + + const workflowRunsComponent = + view === 'cards' ? ( + + ) : ( + + ); + return ( - } /> + } /> - ) ); }; diff --git a/plugins/github-actions/src/components/WorkflowRunsCard/WorkflowRunsCard.tsx b/plugins/github-actions/src/components/WorkflowRunsCard/WorkflowRunsCard.tsx new file mode 100644 index 0000000000..dc97720736 --- /dev/null +++ b/plugins/github-actions/src/components/WorkflowRunsCard/WorkflowRunsCard.tsx @@ -0,0 +1,412 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React, { ChangeEvent, useEffect, useState } from 'react'; +import { + Typography, + Box, + IconButton, + Tooltip, + Button, + Chip, + ButtonGroup, + Grid, + makeStyles, + createStyles, + Theme, + TablePagination, + Select, + MenuItem, + TextField, + CircularProgress, +} from '@material-ui/core'; +import { + EmptyState, + Link, + MarkdownContent, + InfoCard, +} from '@backstage/core-components'; +import GitHubIcon from '@material-ui/icons/GitHub'; +import RetryIcon from '@material-ui/icons/Replay'; +import SyncIcon from '@material-ui/icons/Sync'; +import ExternalLinkIcon from '@material-ui/icons/Launch'; +import { useRouteRef } from '@backstage/core-plugin-api'; +import { useWorkflowRuns, WorkflowRun } from '../useWorkflowRuns'; +import { WorkflowRunStatus } from '../WorkflowRunStatus'; +import { buildRouteRef } from '../../routes'; +import { getProjectNameFromEntity } from '../getProjectNameFromEntity'; +import { getHostnameFromEntity } from '../getHostnameFromEntity'; + +import { Alert, Color } from '@material-ui/lab'; +import { Entity } from '@backstage/catalog-model'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + card: { + border: `1px solid ${theme.palette.divider}`, + boxShadow: theme.shadows[2], + borderRadius: '4px', + overflow: 'visible', + position: 'relative', + margin: theme.spacing(4, 1, 1), + flex: '1', + minWidth: '0px', + }, + externalLinkIcon: { + fontSize: 'inherit', + verticalAlign: 'middle', + }, + bottomline: { + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', + marginTop: '-5px', + }, + pagination: { + width: '100%', + }, + }), +); + +type WorkflowRunsCardViewProps = { + runs?: WorkflowRun[]; + searchTerm: string; + loading: boolean; + onChangePageSize: (pageSize: number) => void; + onChangePage: (page: number) => void; + page: number; + total: number; + pageSize: number; + projectName: string; +}; + +const statusColors: Record = { + skipped: 'warning', + canceled: 'info', + timed_out: 'error', + failure: 'error', + success: 'success', +}; + +const matchesSearchTerm = (run: WorkflowRun, searchTerm: string) => { + const lowerCaseSearchTerm = searchTerm.toLocaleLowerCase(); + return ( + run.workflowName?.toLocaleLowerCase().includes(lowerCaseSearchTerm) || + run.source.branchName?.toLocaleLowerCase().includes(lowerCaseSearchTerm) || + run.status?.toLocaleLowerCase().includes(lowerCaseSearchTerm) || + run.id?.toLocaleLowerCase().includes(lowerCaseSearchTerm) + ); +}; + +export const WorkflowRunsCardView = ({ + runs, + searchTerm, + loading, + onChangePageSize, + onChangePage, + page, + total, + pageSize, +}: WorkflowRunsCardViewProps) => { + const classes = useStyles(); + const routeLink = useRouteRef(buildRouteRef); + + const filteredRuns = runs?.filter(run => matchesSearchTerm(run, searchTerm)); + + return ( + + {filteredRuns && runs?.length !== 0 ? ( + filteredRuns.map(run => ( + + + + + + + + + + {run.workflowName} + + + + + + + + + + + + {run.source.branchName && ( + + )} + + + + + } + /> +
+ {run.githubUrl && ( + + Workflow runs on GitHub{' '} + + + )} + + + + + + + +
+
+
+
+ + )) + ) : ( + + {loading ? : 'No matching runs found.'} + + )} +
+ onChangePage(newPage)} + onRowsPerPageChange={event => + onChangePageSize(parseInt(event.target.value, 6)) + } + labelRowsPerPage="Workflows per page" + rowsPerPageOptions={[6, 12, 18, { label: 'All', value: -1 }]} + /> +
+ + ); +}; + +type WorkflowRunsCardProps = { + entity: Entity; +}; + +const WorkflowRunsCardSearch = ({ + searchTerm, + handleSearch, + retry, +}: { + searchTerm: string; + handleSearch: (event: ChangeEvent) => void; + retry: () => void; +}) => { + return ( + <> + + + + + + + + + + + ); +}; + +export const WorkflowRunsCard = ({ entity }: WorkflowRunsCardProps) => { + const projectName = getProjectNameFromEntity(entity); + const hostname = getHostnameFromEntity(entity); + const [owner, repo] = (projectName ?? '/').split('/'); + const [branch, setBranch] = useState('default'); + const [runs, setRuns] = useState([]); + const [searchTerm, setSearchTerm] = useState(''); + + const handleSearch = (event: ChangeEvent) => { + setSearchTerm(event.target.value); + }; + + const [ + { runs: runsData, branches, defaultBranch, ...cardProps }, + { retry, setPage, setPageSize }, + ] = useWorkflowRuns({ + hostname, + owner, + repo, + branch: branch === 'all' ? undefined : branch, + }); + + const githubHost = hostname || 'github.com'; + const hasNoRuns = !cardProps.loading && !runsData; + + const handleMenuChange = ( + event: ChangeEvent<{ name?: string; value: unknown }>, + ) => { + const selectedValue = event.target.value as string; + setBranch(selectedValue); + setPage(0); + retry(); + }; + + useEffect(() => { + setRuns(runsData); + }, [runsData, branch]); + + useEffect(() => { + setBranch(defaultBranch); + }, [defaultBranch]); + + return ( + + {hasNoRuns ? ( + + Create new Workflow + + } + /> + ) : ( + + + + {projectName} + + + + + + } + > + + + )} + + ); +}; + +export default WorkflowRunsCard; diff --git a/plugins/catalog-backend/src/catalog/index.ts b/plugins/github-actions/src/components/WorkflowRunsCard/index.ts similarity index 88% rename from plugins/catalog-backend/src/catalog/index.ts rename to plugins/github-actions/src/components/WorkflowRunsCard/index.ts index ba34673bba..77b4c85664 100644 --- a/plugins/catalog-backend/src/catalog/index.ts +++ b/plugins/github-actions/src/components/WorkflowRunsCard/index.ts @@ -13,5 +13,4 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -export type { EntitiesSearchFilter, EntityFilter } from './types'; +export { WorkflowRunsCard, WorkflowRunsCardView } from './WorkflowRunsCard'; diff --git a/plugins/github-actions/src/components/useWorkflowRuns.ts b/plugins/github-actions/src/components/useWorkflowRuns.ts index 9ffbdad452..aacfabbe2f 100644 --- a/plugins/github-actions/src/components/useWorkflowRuns.ts +++ b/plugins/github-actions/src/components/useWorkflowRuns.ts @@ -17,6 +17,7 @@ import { useState } from 'react'; import useAsyncRetry from 'react-use/lib/useAsyncRetry'; import { githubActionsApiRef } from '../api/GithubActionsApi'; import { useApi, errorApiRef } from '@backstage/core-plugin-api'; +import { Branch } from '../api'; export type WorkflowRun = { workflowName?: string; @@ -41,12 +42,12 @@ export function useWorkflowRuns({ owner, repo, branch, - initialPageSize = 5, + initialPageSize = 6, }: { hostname?: string; owner: string; repo: string; - branch?: string; + branch?: string | undefined; initialPageSize?: number; }) { const api = useApi(githubActionsApiRef); @@ -56,6 +57,8 @@ export function useWorkflowRuns({ const [total, setTotal] = useState(0); const [page, setPage] = useState(0); const [pageSize, setPageSize] = useState(initialPageSize); + const [branches, setBranches] = useState([]); + const [defaultBranch, setDefaultBranch] = useState(''); const { loading, @@ -63,6 +66,43 @@ export function useWorkflowRuns({ retry, error, } = useAsyncRetry(async () => { + const fetchedDefaultBranch = await api.getDefaultBranch({ + hostname, + owner, + repo, + }); + + setDefaultBranch(fetchedDefaultBranch); + let selectedBranch = branch; + if (branch === 'default') { + selectedBranch = fetchedDefaultBranch; + } + + const fetchBranches = async () => { + let next = true; + let iteratePage = 0; + const branchSet: Branch[] = []; + + while (next) { + const branchesData = await api.listBranches({ + hostname, + owner, + repo, + page: iteratePage, + }); + if (branchesData.length === 0) { + next = false; + } + iteratePage++; + branchSet.push(...branchesData); + } + + return branchSet; + }; + + const branchSet = await fetchBranches(); + setBranches(branchSet); + // GitHub API pagination count starts from 1 const workflowRunsData = await api.listWorkflowRuns({ hostname, @@ -70,7 +110,7 @@ export function useWorkflowRuns({ repo, pageSize, page: page + 1, - branch, + branch: selectedBranch, }); setTotal(workflowRunsData.total_count); // Transformation here @@ -88,7 +128,7 @@ export function useWorkflowRuns({ }); } catch (e) { errorApi.post( - new Error(`Failed to rerun the workflow: ${e.message}`), + new Error(`Failed to rerun the workflow: ${(e as Error).message}`), ); } }, @@ -115,6 +155,8 @@ export function useWorkflowRuns({ pageSize, loading, runs, + branches, + defaultBranch, projectName: `${owner}/${repo}`, total, error, diff --git a/plugins/github-deployments/CHANGELOG.md b/plugins/github-deployments/CHANGELOG.md index 7945e4810d..cab881189f 100644 --- a/plugins/github-deployments/CHANGELOG.md +++ b/plugins/github-deployments/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-github-deployments +## 0.1.59-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/integration@1.8.0-next.1 + - @backstage/integration-react@1.1.22-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + ## 0.1.59-next.0 ### Patch Changes diff --git a/plugins/github-deployments/package.json b/plugins/github-deployments/package.json index a3a7383151..8db532b20e 100644 --- a/plugins/github-deployments/package.json +++ b/plugins/github-deployments/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-github-deployments", "description": "A Backstage plugin that integrates towards GitHub Deployments", - "version": "0.1.59-next.0", + "version": "0.1.59-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/github-issues/CHANGELOG.md b/plugins/github-issues/CHANGELOG.md index 2f6ab11e34..473c325dd4 100644 --- a/plugins/github-issues/CHANGELOG.md +++ b/plugins/github-issues/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-github-issues +## 0.2.17-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/integration@1.8.0-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + ## 0.2.17-next.0 ### Patch Changes diff --git a/plugins/github-issues/package.json b/plugins/github-issues/package.json index bf6b8a39fd..d8b19cf5df 100644 --- a/plugins/github-issues/package.json +++ b/plugins/github-issues/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-github-issues", - "version": "0.2.17-next.0", + "version": "0.2.17-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/github-pull-requests-board/CHANGELOG.md b/plugins/github-pull-requests-board/CHANGELOG.md index 6dfb8de0c8..2e697a4771 100644 --- a/plugins/github-pull-requests-board/CHANGELOG.md +++ b/plugins/github-pull-requests-board/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-github-pull-requests-board +## 0.1.22-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/integration@1.8.0-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/theme@0.5.0-next.0 + ## 0.1.22-next.0 ### Patch Changes diff --git a/plugins/github-pull-requests-board/package.json b/plugins/github-pull-requests-board/package.json index fe792bd7ab..b160ae27e8 100644 --- a/plugins/github-pull-requests-board/package.json +++ b/plugins/github-pull-requests-board/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-github-pull-requests-board", "description": "A Backstage plugin that allows you to see all open Pull Requests for all the repositories owned by your team", - "version": "0.1.22-next.0", + "version": "0.1.22-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/gitops-profiles/CHANGELOG.md b/plugins/gitops-profiles/CHANGELOG.md index 5cbca6f5d7..24aa989eff 100644 --- a/plugins/gitops-profiles/CHANGELOG.md +++ b/plugins/gitops-profiles/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-gitops-profiles +## 0.3.43-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/config@1.1.1 + - @backstage/theme@0.5.0-next.0 + ## 0.3.43-next.0 ### Patch Changes diff --git a/plugins/gitops-profiles/package.json b/plugins/gitops-profiles/package.json index e5befa4e8e..d599d328be 100644 --- a/plugins/gitops-profiles/package.json +++ b/plugins/gitops-profiles/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-gitops-profiles", "description": "A Backstage plugin that helps you manage GitOps profiles", - "version": "0.3.43-next.0", + "version": "0.3.43-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/gocd/CHANGELOG.md b/plugins/gocd/CHANGELOG.md index e0d44dfe91..54be67883c 100644 --- a/plugins/gocd/CHANGELOG.md +++ b/plugins/gocd/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-gocd +## 0.1.34-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + ## 0.1.34-next.0 ### Patch Changes diff --git a/plugins/gocd/package.json b/plugins/gocd/package.json index 5e54063738..54358233fd 100644 --- a/plugins/gocd/package.json +++ b/plugins/gocd/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-gocd", "description": "A Backstage plugin that integrates towards GoCD", - "version": "0.1.34-next.0", + "version": "0.1.34-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/graphiql/CHANGELOG.md b/plugins/graphiql/CHANGELOG.md index 3066f9fa22..4d339dea27 100644 --- a/plugins/graphiql/CHANGELOG.md +++ b/plugins/graphiql/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-graphiql +## 0.3.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.4.0-next.1 + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/core-compat-api@0.0.1-next.1 + - @backstage/theme@0.5.0-next.0 + ## 0.3.1-next.1 ### Patch Changes diff --git a/plugins/graphiql/alpha-api-report.md b/plugins/graphiql/api-report-alpha.md similarity index 69% rename from plugins/graphiql/alpha-api-report.md rename to plugins/graphiql/api-report-alpha.md index 3362cf0a59..c5895067df 100644 --- a/plugins/graphiql/alpha-api-report.md +++ b/plugins/graphiql/api-report-alpha.md @@ -4,20 +4,21 @@ ```ts import { BackstagePlugin } from '@backstage/frontend-plugin-api'; -import { Extension } from '@backstage/frontend-plugin-api'; +import { ExtensionDefinition } from '@backstage/frontend-plugin-api'; import { GraphQLEndpoint } from '@backstage/plugin-graphiql'; import { PortableSchema } from '@backstage/frontend-plugin-api'; import { RouteRef } from '@backstage/frontend-plugin-api'; // @alpha (undocumented) -export function createEndpointExtension(options: { - id: string; +export function createGraphiQLEndpointExtension(options: { + namespace?: string; + name?: string; configSchema?: PortableSchema; disabled?: boolean; factory: (options: { config: TConfig }) => { endpoint: GraphQLEndpoint; }; -}): Extension; +}): ExtensionDefinition; // @alpha (undocumented) const _default: BackstagePlugin< @@ -29,15 +30,15 @@ const _default: BackstagePlugin< export default _default; // @alpha (undocumented) -export const graphiqlBrowseApi: Extension<{}>; +export const graphiqlBrowseApi: ExtensionDefinition<{}>; // @alpha (undocumented) -export const GraphiqlPage: Extension<{ +export const GraphiqlPage: ExtensionDefinition<{ path: string; }>; // @alpha (undocumented) -export const graphiqlPageSidebarItem: Extension<{ +export const graphiqlPageSidebarItem: ExtensionDefinition<{ title: string; }>; diff --git a/plugins/graphiql/package.json b/plugins/graphiql/package.json index eab8f6c141..7ed0f98285 100644 --- a/plugins/graphiql/package.json +++ b/plugins/graphiql/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-graphiql", "description": "Backstage plugin for browsing GraphQL APIs", - "version": "0.3.1-next.1", + "version": "0.3.1-next.2", "publishConfig": { "access": "public" }, diff --git a/plugins/graphiql/src/alpha.tsx b/plugins/graphiql/src/alpha.tsx index 760bc77d8e..f2c23f14e4 100644 --- a/plugins/graphiql/src/alpha.tsx +++ b/plugins/graphiql/src/alpha.tsx @@ -38,7 +38,6 @@ import { convertLegacyRouteRef } from '@backstage/core-compat-api'; /** @alpha */ export const GraphiqlPage = createPageExtension({ - id: 'plugin.graphiql.page', defaultPath: '/graphiql', routeRef: convertLegacyRouteRef(graphiQLRouteRef), loader: () => import('./components').then(m => ), @@ -46,7 +45,6 @@ export const GraphiqlPage = createPageExtension({ /** @alpha */ export const graphiqlPageSidebarItem = createNavItemExtension({ - id: 'plugin.graphiql.nav.index', title: 'GraphiQL', icon: GraphiQLIcon as IconComponent, routeRef: convertLegacyRouteRef(graphiQLRouteRef), @@ -59,7 +57,7 @@ const endpointDataRef = createExtensionDataRef( /** @alpha */ export const graphiqlBrowseApi = createApiExtension({ - api: graphQlBrowseApiRef, // apis.plugin.graphiql.browse + api: graphQlBrowseApiRef, inputs: { endpoints: createExtensionInput({ endpoint: endpointDataRef, @@ -68,21 +66,24 @@ export const graphiqlBrowseApi = createApiExtension({ factory({ inputs }) { return createApiFactory( graphQlBrowseApiRef, - GraphQLEndpoints.from(inputs.endpoints.map(i => i.endpoint)), + GraphQLEndpoints.from(inputs.endpoints.map(i => i.output.endpoint)), ); }, }); /** @alpha */ -export function createEndpointExtension(options: { - id: string; +export function createGraphiQLEndpointExtension(options: { + namespace?: string; + name?: string; configSchema?: PortableSchema; disabled?: boolean; factory: (options: { config: TConfig }) => { endpoint: GraphQLEndpoint }; }) { return createExtension({ - id: `apis.plugin.graphiql.browse.${options.id}`, - attachTo: { id: 'apis.plugin.graphiql.browse', input: 'endpoints' }, + kind: 'graphiql-endpoint', + namespace: options.namespace, + name: options.name, + attachTo: { id: 'api:graphiql/browse', input: 'endpoints' }, configSchema: options.configSchema, disabled: options.disabled ?? false, output: { @@ -97,8 +98,8 @@ export function createEndpointExtension(options: { } /** @alpha */ -const gitlabGraphiQLBrowseExtension = createEndpointExtension({ - id: 'gitlab', +const gitlabGraphiQLBrowseExtension = createGraphiQLEndpointExtension({ + name: 'gitlab', disabled: true, configSchema: createSchemaFromZod(z => z diff --git a/plugins/graphql-voyager/CHANGELOG.md b/plugins/graphql-voyager/CHANGELOG.md index d3ea493357..033e22a800 100644 --- a/plugins/graphql-voyager/CHANGELOG.md +++ b/plugins/graphql-voyager/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-graphql-voyager +## 0.1.10-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/theme@0.5.0-next.0 + ## 0.1.10-next.0 ### Patch Changes diff --git a/plugins/graphql-voyager/package.json b/plugins/graphql-voyager/package.json index 904f388234..88ba1bd92b 100644 --- a/plugins/graphql-voyager/package.json +++ b/plugins/graphql-voyager/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-graphql-voyager", "description": "Backstage plugin for GraphQL Voyager", - "version": "0.1.10-next.0", + "version": "0.1.10-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/home-react/CHANGELOG.md b/plugins/home-react/CHANGELOG.md index bfe53d9bff..989c43dafe 100644 --- a/plugins/home-react/CHANGELOG.md +++ b/plugins/home-react/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-home-react +## 0.1.6-next.1 + +### Patch Changes + +- 2b725913c1: Updated dependency `@rjsf/utils` to `5.14.3`. + Updated dependency `@rjsf/core` to `5.14.3`. + Updated dependency `@rjsf/material-ui` to `5.14.3`. + Updated dependency `@rjsf/validator-ajv8` to `5.14.3`. +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + ## 0.1.6-next.0 ### Patch Changes diff --git a/plugins/home-react/package.json b/plugins/home-react/package.json index cb9a0cd388..60dc92c3e7 100644 --- a/plugins/home-react/package.json +++ b/plugins/home-react/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-home-react", "description": "A Backstage plugin that contains react components helps you build a home page", - "version": "0.1.6-next.0", + "version": "0.1.6-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -38,7 +38,7 @@ "@backstage/core-plugin-api": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", - "@rjsf/utils": "5.14.2", + "@rjsf/utils": "5.14.3", "@types/react": "^16.13.1 || ^17.0.0" }, "peerDependencies": { diff --git a/plugins/home/CHANGELOG.md b/plugins/home/CHANGELOG.md index 28528b9793..dc8f69358f 100644 --- a/plugins/home/CHANGELOG.md +++ b/plugins/home/CHANGELOG.md @@ -1,5 +1,30 @@ # @backstage/plugin-home +## 0.6.0-next.1 + +### Minor Changes + +- 5a317f59c0: Added view of entities grouped by kind to make it easier to distinguish entities with different kind but same name + +### Patch Changes + +- 2b725913c1: Updated dependency `@rjsf/utils` to `5.14.3`. + Updated dependency `@rjsf/core` to `5.14.3`. + Updated dependency `@rjsf/material-ui` to `5.14.3`. + Updated dependency `@rjsf/validator-ajv8` to `5.14.3`. +- Updated dependencies + - @backstage/frontend-plugin-api@0.4.0-next.1 + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-client@1.5.0-next.0 + - @backstage/core-app-api@1.11.2-next.1 + - @backstage/plugin-home-react@0.1.6-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/theme@0.5.0-next.0 + - @backstage/types@1.1.1 + ## 0.5.12-next.0 ### Patch Changes diff --git a/plugins/home/alpha-api-report.md b/plugins/home/api-report-alpha.md similarity index 100% rename from plugins/home/alpha-api-report.md rename to plugins/home/api-report-alpha.md diff --git a/plugins/home/api-report.md b/plugins/home/api-report.md index 18049cae84..dd6e22c57d 100644 --- a/plugins/home/api-report.md +++ b/plugins/home/api-report.md @@ -148,7 +148,7 @@ export const HomePageRecentlyVisited: ( // @public export const HomePageStarredEntities: ( - props: CardExtensionProps_2, + props: CardExtensionProps_2>, ) => JSX_2.Element; // @public @@ -192,6 +192,12 @@ export const SettingsModal: (props: { children: JSX.Element; }) => JSX_2.Element; +// @public +export type StarredEntitiesProps = { + noStarredEntitiesMessage?: React_2.ReactNode | undefined; + groupByKind?: boolean; +}; + // @public (undocumented) export const TemplateBackstageLogo: (props: { classes: { diff --git a/plugins/home/package.json b/plugins/home/package.json index 78eaf80fe3..57a5189fc9 100644 --- a/plugins/home/package.json +++ b/plugins/home/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-home", "description": "A Backstage plugin that helps you build a home page", - "version": "0.5.12-next.0", + "version": "0.6.0-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -61,10 +61,10 @@ "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.61", - "@rjsf/core": "5.14.2", - "@rjsf/material-ui": "5.14.2", - "@rjsf/utils": "5.14.2", - "@rjsf/validator-ajv8": "5.14.2", + "@rjsf/core": "5.14.3", + "@rjsf/material-ui": "5.14.3", + "@rjsf/utils": "5.14.3", + "@rjsf/validator-ajv8": "5.14.3", "@types/react": "^16.13.1 || ^17.0.0", "lodash": "^4.17.21", "luxon": "^3.4.3", diff --git a/plugins/home/src/alpha.tsx b/plugins/home/src/alpha.tsx index c8308f7c0a..f16881de95 100644 --- a/plugins/home/src/alpha.tsx +++ b/plugins/home/src/alpha.tsx @@ -33,7 +33,6 @@ const rootRouteRef = createRouteRef(); export const titleExtensionDataRef = createExtensionDataRef('title'); const HomepageCompositionRootExtension = createPageExtension({ - id: 'home', defaultPath: '/home', routeRef: rootRouteRef, inputs: { @@ -52,8 +51,8 @@ const HomepageCompositionRootExtension = createPageExtension({ loader: ({ inputs }) => import('./components/').then(m => ( )), }); diff --git a/plugins/home/src/components/StarredEntityListItem/StarredEntityListItem.tsx b/plugins/home/src/components/StarredEntityListItem/StarredEntityListItem.tsx new file mode 100644 index 0000000000..2a8960e469 --- /dev/null +++ b/plugins/home/src/components/StarredEntityListItem/StarredEntityListItem.tsx @@ -0,0 +1,60 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { Entity, stringifyEntityRef } from '@backstage/catalog-model'; +import { entityRouteParams } from '@backstage/plugin-catalog-react'; +import { + ListItem, + ListItemIcon, + Tooltip, + IconButton, + ListItemText, +} from '@material-ui/core'; +import React from 'react'; +import { Link } from 'react-router-dom'; +import { entityRouteRef } from '@backstage/plugin-catalog-react'; +import { useRouteRef } from '@backstage/core-plugin-api'; +import StarIcon from '@material-ui/icons/Star'; + +type EntityListItemProps = { + entity: Entity; + onToggleStarredEntity: (entity: Entity) => void; +}; + +export const StarredEntityListItem = ({ + entity, + onToggleStarredEntity, +}: EntityListItemProps) => { + const catalogEntityRoute = useRouteRef(entityRouteRef); + + return ( + + + + onToggleStarredEntity(entity)} + > + + + + + + + + + ); +}; diff --git a/plugins/home/src/components/StarredEntityListItem/index.ts b/plugins/home/src/components/StarredEntityListItem/index.ts new file mode 100644 index 0000000000..5419e0cdf7 --- /dev/null +++ b/plugins/home/src/components/StarredEntityListItem/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { StarredEntityListItem } from './StarredEntityListItem'; diff --git a/plugins/home/src/homePageComponents/StarredEntities/Content.tsx b/plugins/home/src/homePageComponents/StarredEntities/Content.tsx index 4654a7f860..ed5eec857b 100644 --- a/plugins/home/src/homePageComponents/StarredEntities/Content.tsx +++ b/plugins/home/src/homePageComponents/StarredEntities/Content.tsx @@ -17,24 +17,18 @@ import { catalogApiRef, useStarredEntities, - entityRouteParams, - entityRouteRef, } from '@backstage/plugin-catalog-react'; -import { parseEntityRef, stringifyEntityRef } from '@backstage/catalog-model'; -import { useApi, useRouteRef } from '@backstage/core-plugin-api'; -import { Link, Progress, ResponseErrorPanel } from '@backstage/core-components'; import { - List, - ListItem, - ListItemSecondaryAction, - IconButton, - ListItemText, - Tooltip, - Typography, -} from '@material-ui/core'; -import StarIcon from '@material-ui/icons/Star'; + Entity, + parseEntityRef, + stringifyEntityRef, +} from '@backstage/catalog-model'; +import { useApi } from '@backstage/core-plugin-api'; +import { Progress, ResponseErrorPanel } from '@backstage/core-components'; +import { List, Typography, Tabs, Tab } from '@material-ui/core'; import React from 'react'; import useAsync from 'react-use/lib/useAsync'; +import { StarredEntityListItem } from '../../components/StarredEntityListItem/StarredEntityListItem'; /** * A component to display a list of starred entities for the user. @@ -42,12 +36,18 @@ import useAsync from 'react-use/lib/useAsync'; * @public */ -export const Content = (props: { +export type StarredEntitiesProps = { noStarredEntitiesMessage?: React.ReactNode | undefined; -}) => { + groupByKind?: boolean; +}; + +export const Content = ({ + noStarredEntitiesMessage, + groupByKind, +}: StarredEntitiesProps) => { const catalogApi = useApi(catalogApiRef); - const catalogEntityRoute = useRouteRef(entityRouteRef); const { starredEntities, toggleStarredEntity } = useStarredEntities(); + const [activeTab, setActiveTab] = React.useState(0); // Grab starred entities from catalog to ensure they still exist and also retrieve display titles const entities = useAsync(async () => { @@ -79,7 +79,7 @@ export const Content = (props: { if (starredEntities.size === 0) return ( - {props.noStarredEntitiesMessage || + {noStarredEntitiesMessage || 'Click the star beside an entity name to add it to this list!'} ); @@ -88,36 +88,73 @@ export const Content = (props: { return ; } + const groupedEntities: { [kind: string]: Entity[] } = {}; + entities.value?.forEach(entity => { + const kind = entity.kind; + if (!groupedEntities[kind]) { + groupedEntities[kind] = []; + } + groupedEntities[kind].push(entity); + }); + + const groupByKindEntries = Object.entries(groupedEntities); + return entities.error ? ( ) : ( - - {entities.value - ?.sort((a, b) => - (a.metadata.title ?? a.metadata.name).localeCompare( - b.metadata.title ?? b.metadata.name, - ), - ) - .map(entity => ( - - - + {!groupByKind && ( + + {entities.value + ?.sort((a, b) => + (a.metadata.title ?? a.metadata.name).localeCompare( + b.metadata.title ?? b.metadata.name, + ), + ) + .map(entity => ( + - - - - toggleStarredEntity(entity)} - > - - - - - + ))} + + )} + + {groupByKind && ( + setActiveTab(newValue)} + variant="scrollable" + scrollButtons="auto" + aria-label="entity-tabs" + > + {groupByKindEntries.map(([kind]) => ( + + ))} + + )} + + {groupByKind && + groupByKindEntries.map(([kind, entitiesByKind], index) => ( + ))} - +
); }; diff --git a/plugins/home/src/homePageComponents/StarredEntities/index.ts b/plugins/home/src/homePageComponents/StarredEntities/index.ts index 1faa9a2426..32e9213ea6 100644 --- a/plugins/home/src/homePageComponents/StarredEntities/index.ts +++ b/plugins/home/src/homePageComponents/StarredEntities/index.ts @@ -13,5 +13,5 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - export { Content } from './Content'; +export type { StarredEntitiesProps } from './Content'; diff --git a/plugins/home/src/homePageComponents/index.ts b/plugins/home/src/homePageComponents/index.ts index dc611291fb..e260982e8e 100644 --- a/plugins/home/src/homePageComponents/index.ts +++ b/plugins/home/src/homePageComponents/index.ts @@ -19,3 +19,4 @@ export type { ClockConfig } from './HeaderWorldClock'; export type { WelcomeTitleLanguageProps } from './WelcomeTitle'; export type { VisitedByTypeProps, VisitedByTypeKind } from './VisitedByType'; export type { FeaturedDocsCardProps } from './FeaturedDocsCard'; +export type { StarredEntitiesProps } from './StarredEntities'; diff --git a/plugins/home/src/plugin.ts b/plugins/home/src/plugin.ts index 5361ecd9c0..cb9f129a72 100644 --- a/plugins/home/src/plugin.ts +++ b/plugins/home/src/plugin.ts @@ -30,6 +30,7 @@ import { } from './homePageComponents'; import { rootRouteRef } from './routes'; import { VisitsStorageApi, visitsApiRef } from './api'; +import { StarredEntitiesProps } from './homePageComponents/StarredEntities/Content'; /** @public */ export const homePlugin = createPlugin({ @@ -168,7 +169,7 @@ export const HomePageToolkit = homePlugin.provide( * @public */ export const HomePageStarredEntities = homePlugin.provide( - createCardExtension({ + createCardExtension>({ name: 'HomePageStarredEntities', title: 'Your Starred Entities', components: () => import('./homePageComponents/StarredEntities'), diff --git a/plugins/ilert/CHANGELOG.md b/plugins/ilert/CHANGELOG.md index a410255f8d..30203b9bb7 100644 --- a/plugins/ilert/CHANGELOG.md +++ b/plugins/ilert/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-ilert +## 0.2.17-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + ## 0.2.17-next.0 ### Patch Changes diff --git a/plugins/ilert/package.json b/plugins/ilert/package.json index 7123186991..567f60f281 100644 --- a/plugins/ilert/package.json +++ b/plugins/ilert/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-ilert", "description": "A Backstage plugin that integrates towards iLert", - "version": "0.2.17-next.0", + "version": "0.2.17-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/jenkins-backend/CHANGELOG.md b/plugins/jenkins-backend/CHANGELOG.md index af36fa4d88..63337d737c 100644 --- a/plugins/jenkins-backend/CHANGELOG.md +++ b/plugins/jenkins-backend/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-jenkins-backend +## 0.3.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.5.0-next.0 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-auth-node@0.4.2-next.1 + - @backstage/plugin-catalog-node@1.5.1-next.1 + - @backstage/plugin-jenkins-common@0.1.21 + - @backstage/plugin-permission-common@0.7.10 + - @backstage/plugin-permission-node@0.7.19-next.1 + ## 0.3.2-next.0 ### Patch Changes diff --git a/plugins/jenkins-backend/package.json b/plugins/jenkins-backend/package.json index 41739ad4eb..2e8b9ab3dc 100644 --- a/plugins/jenkins-backend/package.json +++ b/plugins/jenkins-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-jenkins-backend", "description": "A Backstage backend plugin that integrates towards Jenkins", - "version": "0.3.2-next.0", + "version": "0.3.2-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/jenkins/CHANGELOG.md b/plugins/jenkins/CHANGELOG.md index f98c22df3e..d8c722d101 100644 --- a/plugins/jenkins/CHANGELOG.md +++ b/plugins/jenkins/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-jenkins +## 0.9.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + - @backstage/plugin-jenkins-common@0.1.21 + ## 0.9.3-next.0 ### Patch Changes diff --git a/plugins/jenkins/package.json b/plugins/jenkins/package.json index cc45d43590..c372414a03 100644 --- a/plugins/jenkins/package.json +++ b/plugins/jenkins/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-jenkins", "description": "A Backstage plugin that integrates towards Jenkins", - "version": "0.9.3-next.0", + "version": "0.9.3-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/kafka-backend/CHANGELOG.md b/plugins/kafka-backend/CHANGELOG.md index 076b38956b..f56a155e2c 100644 --- a/plugins/kafka-backend/CHANGELOG.md +++ b/plugins/kafka-backend/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-kafka-backend +## 0.3.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + ## 0.3.6-next.0 ### Patch Changes diff --git a/plugins/kafka-backend/alpha-api-report.md b/plugins/kafka-backend/api-report-alpha.md similarity index 100% rename from plugins/kafka-backend/alpha-api-report.md rename to plugins/kafka-backend/api-report-alpha.md diff --git a/plugins/kafka-backend/package.json b/plugins/kafka-backend/package.json index 93c817cd65..b61139f4ce 100644 --- a/plugins/kafka-backend/package.json +++ b/plugins/kafka-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-kafka-backend", "description": "A Backstage backend plugin that integrates towards Kafka", - "version": "0.3.6-next.0", + "version": "0.3.6-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/kafka/CHANGELOG.md b/plugins/kafka/CHANGELOG.md index 9dd1951c1d..c81f09daef 100644 --- a/plugins/kafka/CHANGELOG.md +++ b/plugins/kafka/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-kafka +## 0.3.28-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/theme@0.5.0-next.0 + ## 0.3.28-next.0 ### Patch Changes diff --git a/plugins/kafka/package.json b/plugins/kafka/package.json index 7e7a1bbbd7..b4f827f11a 100644 --- a/plugins/kafka/package.json +++ b/plugins/kafka/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-kafka", "description": "A Backstage plugin that integrates towards Kafka", - "version": "0.3.28-next.0", + "version": "0.3.28-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/kubernetes-backend/CHANGELOG.md b/plugins/kubernetes-backend/CHANGELOG.md index 8a2f9b49bf..6938eed660 100644 --- a/plugins/kubernetes-backend/CHANGELOG.md +++ b/plugins/kubernetes-backend/CHANGELOG.md @@ -1,5 +1,27 @@ # @backstage/plugin-kubernetes-backend +## 0.14.0-next.1 + +### Patch Changes + +- ae94d3ce6f: Updated dependency `@aws-crypto/sha256-js` to `^5.0.0`. +- 99fb54183b: Updated dependency `@azure/identity` to `^4.0.0`. +- Updated dependencies + - @backstage/catalog-client@1.5.0-next.0 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/plugin-kubernetes-common@0.7.2-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/integration-aws-node@0.1.8 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.2-next.1 + - @backstage/plugin-catalog-node@1.5.1-next.1 + - @backstage/plugin-kubernetes-node@0.1.2-next.1 + - @backstage/plugin-permission-common@0.7.10 + - @backstage/plugin-permission-node@0.7.19-next.1 + ## 0.14.0-next.0 ### Minor Changes diff --git a/plugins/kubernetes-backend/alpha-api-report.md b/plugins/kubernetes-backend/api-report-alpha.md similarity index 100% rename from plugins/kubernetes-backend/alpha-api-report.md rename to plugins/kubernetes-backend/api-report-alpha.md diff --git a/plugins/kubernetes-backend/package.json b/plugins/kubernetes-backend/package.json index b4fdc103a9..d485a1163a 100644 --- a/plugins/kubernetes-backend/package.json +++ b/plugins/kubernetes-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-kubernetes-backend", "description": "A Backstage backend plugin that integrates towards Kubernetes", - "version": "0.14.0-next.0", + "version": "0.14.0-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/kubernetes-backend/src/routes/resourceRoutes.test.ts b/plugins/kubernetes-backend/src/routes/resourceRoutes.test.ts index b8254d781c..6d69606316 100644 --- a/plugins/kubernetes-backend/src/routes/resourceRoutes.test.ts +++ b/plugins/kubernetes-backend/src/routes/resourceRoutes.test.ts @@ -107,7 +107,7 @@ describe('resourcesRoutes', () => { import('@backstage/plugin-kubernetes-backend/alpha'), createBackendModule({ pluginId: 'kubernetes', - moduleId: 'testObjectsProvider', + moduleId: 'test-objects-provider', register(env) { env.registerInit({ deps: { extension: kubernetesObjectsProviderExtensionPoint }, diff --git a/plugins/kubernetes-cluster/CHANGELOG.md b/plugins/kubernetes-cluster/CHANGELOG.md index 969996da08..4ae5fcf0ae 100644 --- a/plugins/kubernetes-cluster/CHANGELOG.md +++ b/plugins/kubernetes-cluster/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-kubernetes-cluster +## 0.0.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/plugin-kubernetes-common@0.7.2-next.1 + - @backstage/plugin-kubernetes-react@0.1.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + ## 0.0.4-next.0 ### Patch Changes diff --git a/plugins/kubernetes-cluster/package.json b/plugins/kubernetes-cluster/package.json index 6429f99d3f..f113325644 100644 --- a/plugins/kubernetes-cluster/package.json +++ b/plugins/kubernetes-cluster/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-kubernetes-cluster", "description": "A Backstage plugin that shows details of Kubernetes clusters", - "version": "0.0.4-next.0", + "version": "0.0.4-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/kubernetes-common/CHANGELOG.md b/plugins/kubernetes-common/CHANGELOG.md index 7eeb148d94..b46fdd9a64 100644 --- a/plugins/kubernetes-common/CHANGELOG.md +++ b/plugins/kubernetes-common/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-kubernetes-common +## 0.7.2-next.1 + +### Patch Changes + +- 5d796829bb: Remove unused dependency +- Updated dependencies + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-permission-common@0.7.10 + ## 0.7.2-next.0 ### Patch Changes diff --git a/plugins/kubernetes-common/package.json b/plugins/kubernetes-common/package.json index c66ea6606e..318024535f 100644 --- a/plugins/kubernetes-common/package.json +++ b/plugins/kubernetes-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-kubernetes-common", "description": "Common functionalities for kubernetes, to be shared between kubernetes and kubernetes-backend plugin", - "version": "0.7.2-next.0", + "version": "0.7.2-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/kubernetes-node/CHANGELOG.md b/plugins/kubernetes-node/CHANGELOG.md index 28619caaf9..d87d7a3cc8 100644 --- a/plugins/kubernetes-node/CHANGELOG.md +++ b/plugins/kubernetes-node/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-kubernetes-node +## 0.1.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-kubernetes-common@0.7.2-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/catalog-model@1.4.3 + ## 0.1.2-next.0 ### Patch Changes diff --git a/plugins/kubernetes-node/package.json b/plugins/kubernetes-node/package.json index 2f8b3237fc..852ce00d83 100644 --- a/plugins/kubernetes-node/package.json +++ b/plugins/kubernetes-node/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-kubernetes-node", "description": "Node.js library for the kubernetes plugin", - "version": "0.1.2-next.0", + "version": "0.1.2-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/kubernetes-react/CHANGELOG.md b/plugins/kubernetes-react/CHANGELOG.md index fbc09b0b90..03ef769a99 100644 --- a/plugins/kubernetes-react/CHANGELOG.md +++ b/plugins/kubernetes-react/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-kubernetes-react +## 0.1.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-kubernetes-common@0.7.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + ## 0.1.2-next.0 ### Patch Changes diff --git a/plugins/kubernetes-react/api-report.md b/plugins/kubernetes-react/api-report.md index ca1df34b40..cf9f6de664 100644 --- a/plugins/kubernetes-react/api-report.md +++ b/plugins/kubernetes-react/api-report.md @@ -22,7 +22,7 @@ import { IContainerStatus } from 'kubernetes-models/v1'; import { IdentityApi } from '@backstage/core-plugin-api'; import { IIoK8sApimachineryPkgApisMetaV1ObjectMeta } from '@kubernetes-models/apimachinery/apis/meta/v1/ObjectMeta'; import { IObjectMeta } from '@kubernetes-models/apimachinery/apis/meta/v1/ObjectMeta'; -import type { JsonObject } from '@backstage/types'; +import { JsonObject } from '@backstage/types'; import { KubernetesRequestBody } from '@backstage/plugin-kubernetes-common'; import { OAuthApi } from '@backstage/core-plugin-api'; import { ObjectsByEntityResponse } from '@backstage/plugin-kubernetes-common'; @@ -38,6 +38,12 @@ import { V1ObjectMeta } from '@kubernetes/client-node'; import { V1Pod } from '@kubernetes/client-node'; import { WorkloadsByEntityRequest } from '@backstage/plugin-kubernetes-common'; +// @public (undocumented) +export class AksClusterLinksFormatter implements ClusterLinksFormatter { + // (undocumented) + formatClusterLink(options: ClusterLinksFormatterOptions): Promise; +} + // @public (undocumented) export class AksKubernetesAuthProvider implements KubernetesAuthProvider { constructor(microsoftAuthApi: OAuthApi); @@ -61,9 +67,10 @@ export const Cluster: ({ export const ClusterContext: React_2.Context; // @public (undocumented) -export type ClusterLinksFormatter = ( - options: ClusterLinksFormatterOptions, -) => URL; +export interface ClusterLinksFormatter { + // (undocumented) + formatClusterLink(options: ClusterLinksFormatterOptions): Promise; +} // @public (undocumented) export interface ClusterLinksFormatterOptions { @@ -77,9 +84,6 @@ export interface ClusterLinksFormatterOptions { object: any; } -// @public (undocumented) -export const clusterLinksFormatters: Record; - // @public export type ClusterProps = { clusterObjects: ClusterObjects; @@ -125,9 +129,18 @@ export interface CustomResourcesProps { children?: React_2.ReactNode; } +// @public (undocumented) +export const DEFAULT_FORMATTER_NAME = 'standard'; + // @public export const DetectedErrorsContext: React_2.Context; +// @public (undocumented) +export class EksClusterLinksFormatter implements ClusterLinksFormatter { + // (undocumented) + formatClusterLink(_options: ClusterLinksFormatterOptions): Promise; +} + // @public export const ErrorList: ({ podAndErrors, @@ -228,11 +241,6 @@ export interface FixDialogProps { pod: Pod_2; } -// @public (undocumented) -export function formatClusterLink( - options: FormatClusterLinkOptions, -): string | undefined; - // @public (undocumented) export type FormatClusterLinkOptions = { dashboardUrl?: string; @@ -242,6 +250,18 @@ export type FormatClusterLinkOptions = { kind: string; }; +// @public (undocumented) +export function getDefaultFormatters(_deps: {}): Record< + string, + ClusterLinksFormatter +>; + +// @public (undocumented) +export class GkeClusterLinksFormatter implements ClusterLinksFormatter { + // (undocumented) + formatClusterLink(options: ClusterLinksFormatterOptions): Promise; +} + // @public (undocumented) export class GoogleKubernetesAuthProvider implements KubernetesAuthProvider { constructor(authProvider: OAuthApi); @@ -415,6 +435,31 @@ export class KubernetesBackendClient implements KubernetesApi { }): Promise; } +// @public (undocumented) +export class KubernetesClusterLinkFormatter + implements KubernetesClusterLinkFormatterApi +{ + constructor(options: { + formatters: Record; + defaultFormatterName: string; + }); + // (undocumented) + formatClusterLink( + options: FormatClusterLinkOptions, + ): Promise; +} + +// @public (undocumented) +export interface KubernetesClusterLinkFormatterApi { + // (undocumented) + formatClusterLink( + options: FormatClusterLinkOptions, + ): Promise; +} + +// @public (undocumented) +export const kubernetesClusterLinkFormatterApiRef: ApiRef; + // @public export const KubernetesDrawer: ({ open, @@ -587,6 +632,12 @@ export class OidcKubernetesAuthProvider implements KubernetesAuthProvider { providerName: string; } +// @public (undocumented) +export class OpenshiftClusterLinksFormatter { + // (undocumented) + formatClusterLink(options: ClusterLinksFormatterOptions): Promise; +} + // @public export const PendingPodContent: ({ pod, @@ -716,6 +767,12 @@ export type PodsTablesProps = { children?: React_2.ReactNode; }; +// @public (undocumented) +export class RancherClusterLinksFormatter implements ClusterLinksFormatter { + // (undocumented) + formatClusterLink(options: ClusterLinksFormatterOptions): Promise; +} + // @public (undocumented) export const READY_COLUMNS: PodColumns; @@ -763,6 +820,12 @@ export const ServicesAccordions: ({}: ServicesAccordionsProps) => React_2.JSX.El // @public (undocumented) export type ServicesAccordionsProps = {}; +// @public (undocumented) +export class StandardClusterLinksFormatter implements ClusterLinksFormatter { + // (undocumented) + formatClusterLink(options: ClusterLinksFormatterOptions): Promise; +} + // @public export const useCustomResources: ( entity: Entity, diff --git a/plugins/kubernetes-react/package.json b/plugins/kubernetes-react/package.json index 0b707da172..bacf82acd6 100644 --- a/plugins/kubernetes-react/package.json +++ b/plugins/kubernetes-react/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-kubernetes-react", "description": "Web library for the kubernetes-react plugin", - "version": "0.1.2-next.0", + "version": "0.1.2-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/kubernetes-react/src/api/KubernetesClusterLinkFormatter.test.ts b/plugins/kubernetes-react/src/api/KubernetesClusterLinkFormatter.test.ts new file mode 100644 index 0000000000..05b1709c75 --- /dev/null +++ b/plugins/kubernetes-react/src/api/KubernetesClusterLinkFormatter.test.ts @@ -0,0 +1,76 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { KubernetesClusterLinkFormatter } from './KubernetesClusterLinkFormatter'; + +const dashboardUrl = new URL('http://dashboard/url'); +describe('KubernetesClusterLinkFormatter', () => { + const formatter = new KubernetesClusterLinkFormatter({ + formatters: { + standard: { formatClusterLink: async _ => dashboardUrl }, + }, + defaultFormatterName: 'standard', + }); + describe('formatter.formatClusterLink', () => { + it('should not return an url when there is no dashboard url', async () => { + const url = await formatter.formatClusterLink({ + object: {}, + kind: 'foo', + }); + expect(url).toBeUndefined(); + }); + it('should return an url even when there is no object', async () => { + const url = await formatter.formatClusterLink({ + dashboardUrl: 'https://k8s.foo.com', + object: undefined, + kind: 'foo', + }); + expect(url).toBe('https://k8s.foo.com'); + }); + it('should throw when the app is not recognized', async () => { + await expect( + formatter.formatClusterLink({ + dashboardUrl: 'https://k8s.foo.com', + dashboardApp: 'unknownapp', + object: { + metadata: { + name: 'foobar', + namespace: 'bar', + }, + }, + kind: 'Deployment', + }), + ).rejects.toThrow( + "Could not find Kubernetes dashboard app named 'unknownapp'", + ); + }); + + describe('default app', () => { + it('should use default app', async () => { + const url = await formatter.formatClusterLink({ + dashboardUrl: 'https://k8s.foo.com/', + object: { + metadata: { + name: 'foobar', + namespace: 'bar', + }, + }, + kind: 'Deployment', + }); + expect(url).toBe(dashboardUrl.toString()); + }); + }); + }); +}); diff --git a/plugins/kubernetes-react/src/api/KubernetesClusterLinkFormatter.ts b/plugins/kubernetes-react/src/api/KubernetesClusterLinkFormatter.ts new file mode 100644 index 0000000000..45dd6e6fa1 --- /dev/null +++ b/plugins/kubernetes-react/src/api/KubernetesClusterLinkFormatter.ts @@ -0,0 +1,59 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + KubernetesClusterLinkFormatterApi, + FormatClusterLinkOptions, +} from './types'; +import { ClusterLinksFormatter } from '../types'; + +/** @public */ +export class KubernetesClusterLinkFormatter + implements KubernetesClusterLinkFormatterApi +{ + private readonly formatters: Record; + private readonly defaultFormatterName: string; + + constructor(options: { + formatters: Record; + defaultFormatterName: string; + }) { + this.formatters = options.formatters; + this.defaultFormatterName = options.defaultFormatterName; + } + async formatClusterLink(options: FormatClusterLinkOptions) { + if (!options.dashboardUrl && !options.dashboardParameters) { + return undefined; + } + if (options.dashboardUrl && !options.object) { + return options.dashboardUrl; + } + const app = options.dashboardApp ?? this.defaultFormatterName; + const formatter = this.formatters[app]; + if (!formatter) { + throw new Error(`Could not find Kubernetes dashboard app named '${app}'`); + } + const url = await formatter.formatClusterLink({ + dashboardUrl: options.dashboardUrl + ? new URL(options.dashboardUrl) + : undefined, + dashboardParameters: options.dashboardParameters, + object: options.object, + kind: options.kind, + }); + return url.toString(); + } +} diff --git a/plugins/kubernetes-react/src/utils/clusterLinks/formatters/aks.test.ts b/plugins/kubernetes-react/src/api/formatters/AksClusterLinksFormatter.test.ts similarity index 82% rename from plugins/kubernetes-react/src/utils/clusterLinks/formatters/aks.test.ts rename to plugins/kubernetes-react/src/api/formatters/AksClusterLinksFormatter.test.ts index 18db618686..b7bdc3d7e8 100644 --- a/plugins/kubernetes-react/src/utils/clusterLinks/formatters/aks.test.ts +++ b/plugins/kubernetes-react/src/api/formatters/AksClusterLinksFormatter.test.ts @@ -13,12 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { aksFormatter } from './aks'; +import { AksClusterLinksFormatter } from './AksClusterLinksFormatter'; describe('clusterLinks - AKS formatter', () => { - it('should provide a dashboardParameters in the options', () => { - expect(() => - aksFormatter({ + const formatter = new AksClusterLinksFormatter(); + it('should provide a dashboardParameters in the options', async () => { + await expect(() => + formatter.formatClusterLink({ object: { metadata: { name: 'foobar', @@ -27,11 +28,11 @@ describe('clusterLinks - AKS formatter', () => { }, kind: 'Deployment', }), - ).toThrow('AKS dashboard requires a dashboardParameters option'); + ).rejects.toThrow('AKS dashboard requires a dashboardParameters option'); }); - it('should provide a subscriptionId in the dashboardParameters options', () => { - expect(() => - aksFormatter({ + it('should provide a subscriptionId in the dashboardParameters options', async () => { + await expect( + formatter.formatClusterLink({ dashboardParameters: { resourceGroup: 'rg-1', clusterName: 'cluster-1', @@ -44,13 +45,13 @@ describe('clusterLinks - AKS formatter', () => { }, kind: 'Deployment', }), - ).toThrow( + ).rejects.toThrow( 'AKS dashboard requires a "subscriptionId" of type string in the dashboardParameters option', ); }); - it('should provide a resourceGroup in the dashboardParameters options', () => { - expect(() => - aksFormatter({ + it('should provide a resourceGroup in the dashboardParameters options', async () => { + await expect( + formatter.formatClusterLink({ dashboardParameters: { subscriptionId: '1234-GUID-5678', clusterName: 'cluster-1', @@ -63,13 +64,13 @@ describe('clusterLinks - AKS formatter', () => { }, kind: 'Deployment', }), - ).toThrow( + ).rejects.toThrow( 'AKS dashboard requires a "resourceGroup" of type string in the dashboardParameters option', ); }); - it('should provide a clusterName in the dashboardParameters options', () => { - expect(() => - aksFormatter({ + it('should provide a clusterName in the dashboardParameters options', async () => { + await expect( + formatter.formatClusterLink({ dashboardParameters: { subscriptionId: '1234-GUID-5678', resourceGroup: 'us-east1-c', @@ -82,12 +83,12 @@ describe('clusterLinks - AKS formatter', () => { }, kind: 'Deployment', }), - ).toThrow( + ).rejects.toThrow( 'AKS dashboard requires a "clusterName" of type string in the dashboardParameters option', ); }); - it('should return an url on the cluster with object details', () => { - const url = aksFormatter({ + it('should return an url on the cluster with object details', async () => { + const url = await formatter.formatClusterLink({ dashboardParameters: { subscriptionId: '1234-GUID-5678', resourceGroup: 'rg-1', diff --git a/plugins/kubernetes-react/src/api/formatters/AksClusterLinksFormatter.ts b/plugins/kubernetes-react/src/api/formatters/AksClusterLinksFormatter.ts new file mode 100644 index 0000000000..1e7bb0ff9e --- /dev/null +++ b/plugins/kubernetes-react/src/api/formatters/AksClusterLinksFormatter.ts @@ -0,0 +1,59 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { + ClusterLinksFormatter, + ClusterLinksFormatterOptions, +} from '../../types'; + +const basePath = + 'https://portal.azure.com/#blade/Microsoft_Azure_ContainerService/AksK8ResourceMenuBlade/overview-Deployment/aksClusterId'; + +const requiredParams = ['subscriptionId', 'resourceGroup', 'clusterName']; + +/** @public */ +export class AksClusterLinksFormatter implements ClusterLinksFormatter { + async formatClusterLink(options: ClusterLinksFormatterOptions) { + if (!options.dashboardParameters) { + throw new Error('AKS dashboard requires a dashboardParameters option'); + } + const args = options.dashboardParameters; + for (const param of requiredParams) { + if (typeof args[param] !== 'string') { + throw new Error( + `AKS dashboard requires a "${param}" of type string in the dashboardParameters option`, + ); + } + } + + const path = `/subscriptions/${args.subscriptionId}/resourceGroups/${args.resourceGroup}/providers/Microsoft.ContainerService/managedClusters/${args.clusterName}`; + + const { name, namespace, uid } = options.object.metadata; + const { selector } = options.object.spec; + const params = { + kind: options.kind, + metadata: { name, namespace, uid }, + spec: { + selector, + }, + }; + + return new URL( + `${basePath}/${encodeURIComponent(path)}/resource/${encodeURIComponent( + JSON.stringify(params), + )}`, + ); + } +} diff --git a/plugins/kubernetes-react/src/utils/clusterLinks/formatters/eks.test.ts b/plugins/kubernetes-react/src/api/formatters/EksClusterLinksFormatter.test.ts similarity index 75% rename from plugins/kubernetes-react/src/utils/clusterLinks/formatters/eks.test.ts rename to plugins/kubernetes-react/src/api/formatters/EksClusterLinksFormatter.test.ts index d0ae73753f..738da9fb94 100644 --- a/plugins/kubernetes-react/src/utils/clusterLinks/formatters/eks.test.ts +++ b/plugins/kubernetes-react/src/api/formatters/EksClusterLinksFormatter.test.ts @@ -13,12 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { eksFormatter } from './eks'; + +import { EksClusterLinksFormatter } from './EksClusterLinksFormatter'; describe('clusterLinks - EKS formatter', () => { - it('should return an url on the workloads when there is a namespace only', () => { - expect(() => - eksFormatter({ + const formatter = new EksClusterLinksFormatter(); + it('should return an url on the workloads when there is a namespace only', async () => { + await expect( + formatter.formatClusterLink({ dashboardUrl: new URL('https://k8s.foo.com'), object: { metadata: { @@ -28,6 +30,8 @@ describe('clusterLinks - EKS formatter', () => { }, kind: 'Deployment', }), - ).toThrow('EKS formatter is not yet implemented. Please, contribute!'); + ).rejects.toThrow( + 'EKS formatter is not yet implemented. Please, contribute!', + ); }); }); diff --git a/plugins/kubernetes-react/src/api/formatters/EksClusterLinksFormatter.ts b/plugins/kubernetes-react/src/api/formatters/EksClusterLinksFormatter.ts new file mode 100644 index 0000000000..d65b619639 --- /dev/null +++ b/plugins/kubernetes-react/src/api/formatters/EksClusterLinksFormatter.ts @@ -0,0 +1,30 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { + ClusterLinksFormatter, + ClusterLinksFormatterOptions, +} from '../../types'; + +/** @public */ +export class EksClusterLinksFormatter implements ClusterLinksFormatter { + async formatClusterLink( + _options: ClusterLinksFormatterOptions, + ): Promise { + throw new Error( + 'EKS formatter is not yet implemented. Please, contribute!', + ); + } +} diff --git a/plugins/kubernetes-react/src/utils/clusterLinks/formatters/gke.test.ts b/plugins/kubernetes-react/src/api/formatters/GkeClusterLinksFormatter.test.ts similarity index 80% rename from plugins/kubernetes-react/src/utils/clusterLinks/formatters/gke.test.ts rename to plugins/kubernetes-react/src/api/formatters/GkeClusterLinksFormatter.test.ts index 7fd0814267..250be75214 100644 --- a/plugins/kubernetes-react/src/utils/clusterLinks/formatters/gke.test.ts +++ b/plugins/kubernetes-react/src/api/formatters/GkeClusterLinksFormatter.test.ts @@ -13,12 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { gkeFormatter } from './gke'; + +import { GkeClusterLinksFormatter } from './GkeClusterLinksFormatter'; describe('clusterLinks - GKE formatter', () => { - it('should provide a dashboardParameters in the options', () => { - expect(() => - gkeFormatter({ + const formatter = new GkeClusterLinksFormatter(); + + it('should provide a dashboardParameters in the options', async () => { + await expect( + formatter.formatClusterLink({ object: { metadata: { name: 'foobar', @@ -27,11 +30,11 @@ describe('clusterLinks - GKE formatter', () => { }, kind: 'Deployment', }), - ).toThrow('GKE dashboard requires a dashboardParameters option'); + ).rejects.toThrow('GKE dashboard requires a dashboardParameters option'); }); - it('should provide a projectId in the dashboardParameters options', () => { - expect(() => - gkeFormatter({ + it('should provide a projectId in the dashboardParameters options', async () => { + await expect( + formatter.formatClusterLink({ dashboardParameters: { region: 'us-east1-c', clusterName: 'cluster-1', @@ -44,13 +47,13 @@ describe('clusterLinks - GKE formatter', () => { }, kind: 'Deployment', }), - ).toThrow( + ).rejects.toThrow( 'GKE dashboard requires a "projectId" of type string in the dashboardParameters option', ); }); - it('should provide a region in the dashboardParameters options', () => { - expect(() => - gkeFormatter({ + it('should provide a region in the dashboardParameters options', async () => { + await expect( + formatter.formatClusterLink({ dashboardParameters: { projectId: 'foobar-333614', clusterName: 'cluster-1', @@ -63,13 +66,13 @@ describe('clusterLinks - GKE formatter', () => { }, kind: 'Deployment', }), - ).toThrow( + ).rejects.toThrow( 'GKE dashboard requires a "region" of type string in the dashboardParameters option', ); }); - it('should provide a clusterName in the dashboardParameters options', () => { - expect(() => - gkeFormatter({ + it('should provide a clusterName in the dashboardParameters options', async () => { + await expect(() => + formatter.formatClusterLink({ dashboardParameters: { projectId: 'foobar-333614', region: 'us-east1-c', @@ -82,12 +85,12 @@ describe('clusterLinks - GKE formatter', () => { }, kind: 'Deployment', }), - ).toThrow( + ).rejects.toThrow( 'GKE dashboard requires a "clusterName" of type string in the dashboardParameters option', ); }); - it('should return an url on the cluster when there is a namespace only', () => { - const url = gkeFormatter({ + it('should return an url on the cluster when there is a namespace only', async () => { + const url = await formatter.formatClusterLink({ dashboardParameters: { projectId: 'foobar-333614', region: 'us-east1-c', @@ -104,8 +107,8 @@ describe('clusterLinks - GKE formatter', () => { 'https://console.cloud.google.com/kubernetes/clusters/details/us-east1-c/cluster-1/details?project=foobar-333614', ); }); - it('should return an url on the cluster when the kind is not recognizeed', () => { - const url = gkeFormatter({ + it('should return an url on the cluster when the kind is not recognizeed', async () => { + const url = await formatter.formatClusterLink({ dashboardParameters: { projectId: 'foobar-333614', region: 'us-east1-c', @@ -123,8 +126,8 @@ describe('clusterLinks - GKE formatter', () => { 'https://console.cloud.google.com/kubernetes/clusters/details/us-east1-c/cluster-1/details?project=foobar-333614', ); }); - it('should return an url on the deployment', () => { - const url = gkeFormatter({ + it('should return an url on the deployment', async () => { + const url = await formatter.formatClusterLink({ dashboardParameters: { projectId: 'foobar-333614', region: 'us-east1-c', @@ -143,8 +146,8 @@ describe('clusterLinks - GKE formatter', () => { ); }); - it('should return an url on the service', () => { - const url = gkeFormatter({ + it('should return an url on the service', async () => { + const url = await formatter.formatClusterLink({ dashboardParameters: { projectId: 'foobar-333614', region: 'us-east1-c', @@ -162,8 +165,8 @@ describe('clusterLinks - GKE formatter', () => { 'https://console.cloud.google.com/kubernetes/service/us-east1-c/cluster-1/bar/foobar/overview?project=foobar-333614', ); }); - it('should return an url on the pod', () => { - const url = gkeFormatter({ + it('should return an url on the pod', async () => { + const url = await formatter.formatClusterLink({ dashboardParameters: { projectId: 'foobar-333614', region: 'us-east1-c', @@ -181,8 +184,8 @@ describe('clusterLinks - GKE formatter', () => { 'https://console.cloud.google.com/kubernetes/pod/us-east1-c/cluster-1/bar/foobar/details?project=foobar-333614', ); }); - it('should return an url on the ingress', () => { - const url = gkeFormatter({ + it('should return an url on the ingress', async () => { + const url = await formatter.formatClusterLink({ dashboardParameters: { projectId: 'foobar-333614', region: 'us-east1-c', @@ -200,8 +203,8 @@ describe('clusterLinks - GKE formatter', () => { 'https://console.cloud.google.com/kubernetes/ingress/us-east1-c/cluster-1/bar/foobar/details?project=foobar-333614', ); }); - it('should return an url on the deployment for a hpa', () => { - const url = gkeFormatter({ + it('should return an url on the deployment for a hpa', async () => { + const url = await formatter.formatClusterLink({ dashboardParameters: { projectId: 'foobar-333614', region: 'us-east1-c', diff --git a/plugins/kubernetes-react/src/api/formatters/GkeClusterLinksFormatter.ts b/plugins/kubernetes-react/src/api/formatters/GkeClusterLinksFormatter.ts new file mode 100644 index 0000000000..c8dec85f71 --- /dev/null +++ b/plugins/kubernetes-react/src/api/formatters/GkeClusterLinksFormatter.ts @@ -0,0 +1,73 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { + ClusterLinksFormatter, + ClusterLinksFormatterOptions, +} from '../../types'; + +const kindMappings: Record = { + deployment: 'deployment', + pod: 'pod', + ingress: 'ingress', + service: 'service', + horizontalpodautoscaler: 'deployment', +}; + +/** @public */ +export class GkeClusterLinksFormatter implements ClusterLinksFormatter { + async formatClusterLink(options: ClusterLinksFormatterOptions): Promise { + if (!options.dashboardParameters) { + throw new Error('GKE dashboard requires a dashboardParameters option'); + } + const args = options.dashboardParameters; + if (typeof args.projectId !== 'string') { + throw new Error( + 'GKE dashboard requires a "projectId" of type string in the dashboardParameters option', + ); + } + if (typeof args.region !== 'string') { + throw new Error( + 'GKE dashboard requires a "region" of type string in the dashboardParameters option', + ); + } + if (typeof args.clusterName !== 'string') { + throw new Error( + 'GKE dashboard requires a "clusterName" of type string in the dashboardParameters option', + ); + } + const basePath = new URL('https://console.cloud.google.com/'); + const region = encodeURIComponent(args.region); + const clusterName = encodeURIComponent(args.clusterName); + const name = encodeURIComponent(options.object.metadata?.name ?? ''); + const namespace = encodeURIComponent( + options.object.metadata?.namespace ?? '', + ); + const validKind = kindMappings[options.kind.toLocaleLowerCase('en-US')]; + let path: string; + if (namespace && name && validKind) { + const kindsWithDetails = ['ingress', 'pod']; + const landingPage = kindsWithDetails.includes(validKind) + ? 'details' + : 'overview'; + path = `kubernetes/${validKind}/${region}/${clusterName}/${namespace}/${name}/${landingPage}`; + } else { + path = `kubernetes/clusters/details/${region}/${clusterName}/details`; + } + const result = new URL(path, basePath); + result.searchParams.set('project', args.projectId); + return result; + } +} diff --git a/plugins/kubernetes-react/src/utils/clusterLinks/formatters/openshift.test.ts b/plugins/kubernetes-react/src/api/formatters/OpenshiftClusterLinksFormatter.test.ts similarity index 75% rename from plugins/kubernetes-react/src/utils/clusterLinks/formatters/openshift.test.ts rename to plugins/kubernetes-react/src/api/formatters/OpenshiftClusterLinksFormatter.test.ts index 945553e340..1adb688757 100644 --- a/plugins/kubernetes-react/src/utils/clusterLinks/formatters/openshift.test.ts +++ b/plugins/kubernetes-react/src/api/formatters/OpenshiftClusterLinksFormatter.test.ts @@ -13,12 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { openshiftFormatter } from './openshift'; + +import { OpenshiftClusterLinksFormatter } from './OpenshiftClusterLinksFormatter'; describe('clusterLinks - OpenShift formatter', () => { - it('should provide a dashboardUrl in the options', () => { - expect(() => - openshiftFormatter({ + const formatter = new OpenshiftClusterLinksFormatter(); + it('should provide a dashboardUrl in the options', async () => { + await expect(() => + formatter.formatClusterLink({ object: { metadata: { name: 'foobar', @@ -27,10 +29,10 @@ describe('clusterLinks - OpenShift formatter', () => { }, kind: 'Deployment', }), - ).toThrow('OpenShift dashboard requires a dashboardUrl option'); + ).rejects.toThrow('OpenShift dashboard requires a dashboardUrl option'); }); - it('should return an url on the workloads when there is a namespace only', () => { - const url = openshiftFormatter({ + it('should return an url on the workloads when there is a namespace only', async () => { + const url = await formatter.formatClusterLink({ dashboardUrl: new URL('https://k8s.foo.com'), object: { metadata: { @@ -41,8 +43,8 @@ describe('clusterLinks - OpenShift formatter', () => { }); expect(url.href).toBe('https://k8s.foo.com/k8s/cluster/projects/bar'); }); - it('should return an url on the workloads when the kind is not recognizeed', () => { - const url = openshiftFormatter({ + it('should return an url on the workloads when the kind is not recognizeed', async () => { + const url = await formatter.formatClusterLink({ dashboardUrl: new URL('https://k8s.foo.com'), object: { metadata: { @@ -54,8 +56,8 @@ describe('clusterLinks - OpenShift formatter', () => { }); expect(url.href).toBe('https://k8s.foo.com/k8s/cluster/projects/bar'); }); - it('should return an url on the deployment', () => { - const url = openshiftFormatter({ + it('should return an url on the deployment', async () => { + const url = await formatter.formatClusterLink({ dashboardUrl: new URL('https://k8s.foo.com/'), object: { metadata: { @@ -67,8 +69,8 @@ describe('clusterLinks - OpenShift formatter', () => { }); expect(url.href).toBe('https://k8s.foo.com/k8s/ns/bar/deployments/foobar'); }); - it('should return an url on the deployment and keep the path prefix 1', () => { - const url = openshiftFormatter({ + it('should return an url on the deployment and keep the path prefix 1', async () => { + const url = await formatter.formatClusterLink({ dashboardUrl: new URL('https://k8s.foo.com/some/prefix/'), object: { metadata: { @@ -82,8 +84,8 @@ describe('clusterLinks - OpenShift formatter', () => { 'https://k8s.foo.com/some/prefix/k8s/ns/bar/deployments/foobar', ); }); - it('should return an url on the deployment and keep the path prefix 2', () => { - const url = openshiftFormatter({ + it('should return an url on the deployment and keep the path prefix 2', async () => { + const url = await formatter.formatClusterLink({ dashboardUrl: new URL('https://k8s.foo.com/some/prefix'), object: { metadata: { @@ -97,8 +99,8 @@ describe('clusterLinks - OpenShift formatter', () => { 'https://k8s.foo.com/some/prefix/k8s/ns/bar/deployments/foobar', ); }); - it('should return an url on the service', () => { - const url = openshiftFormatter({ + it('should return an url on the service', async () => { + const url = await formatter.formatClusterLink({ dashboardUrl: new URL('https://k8s.foo.com/'), object: { metadata: { @@ -110,8 +112,8 @@ describe('clusterLinks - OpenShift formatter', () => { }); expect(url.href).toBe('https://k8s.foo.com/k8s/ns/bar/services/foobar'); }); - it('should return an url on the ingress', () => { - const url = openshiftFormatter({ + it('should return an url on the ingress', async () => { + const url = await formatter.formatClusterLink({ dashboardUrl: new URL('https://k8s.foo.com/'), object: { metadata: { @@ -123,8 +125,8 @@ describe('clusterLinks - OpenShift formatter', () => { }); expect(url.href).toBe('https://k8s.foo.com/k8s/ns/bar/ingresses/foobar'); }); - it('should return an url on the deployment for a hpa', () => { - const url = openshiftFormatter({ + it('should return an url on the deployment for a hpa', async () => { + const url = await formatter.formatClusterLink({ dashboardUrl: new URL('https://k8s.foo.com/'), object: { metadata: { @@ -138,8 +140,8 @@ describe('clusterLinks - OpenShift formatter', () => { 'https://k8s.foo.com/k8s/ns/bar/horizontalpodautoscalers/foobar', ); }); - it('should return an url on the PV', () => { - const url = openshiftFormatter({ + it('should return an url on the PV', async () => { + const url = await formatter.formatClusterLink({ dashboardUrl: new URL('https://k8s.foo.com/'), object: { metadata: { diff --git a/plugins/kubernetes-react/src/api/formatters/OpenshiftClusterLinksFormatter.ts b/plugins/kubernetes-react/src/api/formatters/OpenshiftClusterLinksFormatter.ts new file mode 100644 index 0000000000..228ff3d1ac --- /dev/null +++ b/plugins/kubernetes-react/src/api/formatters/OpenshiftClusterLinksFormatter.ts @@ -0,0 +1,60 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { ClusterLinksFormatterOptions } from '../../types'; + +const kindMappings: Record = { + deployment: 'deployments', + ingress: 'ingresses', + service: 'services', + horizontalpodautoscaler: 'horizontalpodautoscalers', + persistentvolume: 'persistentvolumes', +}; + +/** @public */ +export class OpenshiftClusterLinksFormatter { + async formatClusterLink(options: ClusterLinksFormatterOptions): Promise { + if (!options.dashboardUrl) { + throw new Error('OpenShift dashboard requires a dashboardUrl option'); + } + const basePath = new URL(options.dashboardUrl.href); + const name = encodeURIComponent(options.object.metadata?.name ?? ''); + const namespace = encodeURIComponent( + options.object.metadata?.namespace ?? '', + ); + const validKind = kindMappings[options.kind.toLocaleLowerCase('en-US')]; + if (!basePath.pathname.endsWith('/')) { + // a dashboard url with a path should end with a slash otherwise + // the new combined URL will replace the last segment with the appended path! + // https://foobar.com/abc/def + k8s/cluster/projects/test --> https://foobar.com/abc/k8s/cluster/projects/test + // https://foobar.com/abc/def/ + k8s/cluster/projects/test --> https://foobar.com/abc/def/k8s/cluster/projects/test + basePath.pathname += '/'; + } + let path = ''; + if (namespace) { + if (name && validKind) { + path = `k8s/ns/${namespace}/${validKind}/${name}`; + } else { + path = `k8s/cluster/projects/${namespace}`; + } + } else if (validKind) { + path = `k8s/cluster/${validKind}`; + if (name) { + path += `/${name}`; + } + } + return new URL(path, basePath); + } +} diff --git a/plugins/kubernetes-react/src/utils/clusterLinks/formatters/rancher.test.ts b/plugins/kubernetes-react/src/api/formatters/RancherClusterLinksFormatter.test.ts similarity index 74% rename from plugins/kubernetes-react/src/utils/clusterLinks/formatters/rancher.test.ts rename to plugins/kubernetes-react/src/api/formatters/RancherClusterLinksFormatter.test.ts index bb26da3ab0..228340eb16 100644 --- a/plugins/kubernetes-react/src/utils/clusterLinks/formatters/rancher.test.ts +++ b/plugins/kubernetes-react/src/api/formatters/RancherClusterLinksFormatter.test.ts @@ -13,12 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { rancherFormatter } from './rancher'; + +import { RancherClusterLinksFormatter } from './RancherClusterLinksFormatter'; describe('clusterLinks - rancher formatter', () => { - it('should provide a dashboardUrl in the options', () => { - expect(() => - rancherFormatter({ + const formatter = new RancherClusterLinksFormatter(); + it('should provide a dashboardUrl in the options', async () => { + await expect(() => + formatter.formatClusterLink({ object: { metadata: { name: 'foobar', @@ -27,10 +29,10 @@ describe('clusterLinks - rancher formatter', () => { }, kind: 'Deployment', }), - ).toThrow('Rancher dashboard requires a dashboardUrl option'); + ).rejects.toThrow('Rancher dashboard requires a dashboardUrl option'); }); - it('should return a url on the workloads when there is a namespace only', () => { - const url = rancherFormatter({ + it('should return a url on the workloads when there is a namespace only', async () => { + const url = await formatter.formatClusterLink({ dashboardUrl: new URL('https://k8s.foo.com'), object: { metadata: { @@ -41,8 +43,8 @@ describe('clusterLinks - rancher formatter', () => { }); expect(url.href).toBe('https://k8s.foo.com/explorer/workload'); }); - it('should return a url on the workloads when the kind is not recognized', () => { - const url = rancherFormatter({ + it('should return a url on the workloads when the kind is not recognized', async () => { + const url = await formatter.formatClusterLink({ dashboardUrl: new URL('https://k8s.foo.com'), object: { metadata: { @@ -54,8 +56,8 @@ describe('clusterLinks - rancher formatter', () => { }); expect(url.href).toBe('https://k8s.foo.com/explorer/workload'); }); - it('should return a url on the deployment', () => { - const url = rancherFormatter({ + it('should return a url on the deployment', async () => { + const url = await formatter.formatClusterLink({ dashboardUrl: new URL('https://k8s.foo.com/'), object: { metadata: { @@ -69,8 +71,8 @@ describe('clusterLinks - rancher formatter', () => { 'https://k8s.foo.com/explorer/apps.deployment/bar/foobar', ); }); - it('should return a url on the service', () => { - const url = rancherFormatter({ + it('should return a url on the service', async () => { + const url = await formatter.formatClusterLink({ dashboardUrl: new URL('https://k8s.foo.com/'), object: { metadata: { @@ -82,8 +84,8 @@ describe('clusterLinks - rancher formatter', () => { }); expect(url.href).toBe('https://k8s.foo.com/explorer/service/bar/foobar'); }); - it('should return a url on the ingress', () => { - const url = rancherFormatter({ + it('should return a url on the ingress', async () => { + const url = await formatter.formatClusterLink({ dashboardUrl: new URL('https://k8s.foo.com/'), object: { metadata: { @@ -97,8 +99,8 @@ describe('clusterLinks - rancher formatter', () => { 'https://k8s.foo.com/explorer/networking.k8s.io.ingress/bar/foobar', ); }); - it('should return a url on the deployment for a hpa', () => { - const url = rancherFormatter({ + it('should return a url on the deployment for a hpa', async () => { + const url = await formatter.formatClusterLink({ dashboardUrl: new URL('https://k8s.foo.com/'), object: { metadata: { @@ -112,8 +114,8 @@ describe('clusterLinks - rancher formatter', () => { 'https://k8s.foo.com/explorer/autoscaling.horizontalpodautoscaler/bar/foobar', ); }); - it('should support subpaths in dashboardUrl', () => { - const url = rancherFormatter({ + it('should support subpaths in dashboardUrl', async () => { + const url = await formatter.formatClusterLink({ dashboardUrl: new URL('https://k8s.foo.com/dashboard/c/c-28a4b/'), object: { metadata: { diff --git a/plugins/kubernetes-react/src/api/formatters/RancherClusterLinksFormatter.ts b/plugins/kubernetes-react/src/api/formatters/RancherClusterLinksFormatter.ts new file mode 100644 index 0000000000..e607d98972 --- /dev/null +++ b/plugins/kubernetes-react/src/api/formatters/RancherClusterLinksFormatter.ts @@ -0,0 +1,55 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { + ClusterLinksFormatter, + ClusterLinksFormatterOptions, +} from '../../types'; + +const kindMappings: Record = { + deployment: 'apps.deployment', + ingress: 'networking.k8s.io.ingress', + service: 'service', + horizontalpodautoscaler: 'autoscaling.horizontalpodautoscaler', +}; + +/** @public */ +export class RancherClusterLinksFormatter implements ClusterLinksFormatter { + async formatClusterLink(options: ClusterLinksFormatterOptions): Promise { + if (!options.dashboardUrl) { + throw new Error('Rancher dashboard requires a dashboardUrl option'); + } + const basePath = new URL(options.dashboardUrl.href); + const name = encodeURIComponent(options.object.metadata?.name ?? ''); + const namespace = encodeURIComponent( + options.object.metadata?.namespace ?? '', + ); + const validKind = kindMappings[options.kind.toLocaleLowerCase('en-US')]; + if (!basePath.pathname.endsWith('/')) { + // a dashboard url with a path should end with a slash otherwise + // the new combined URL will replace the last segment with the appended path! + // https://foobar.com/abc/def + explorer/service/test --> https://foobar.com/abc/explorer/service/test + // https://foobar.com/abc/def/ + explorer/service/test --> https://foobar.com/abc/def/explorer/service/test + basePath.pathname += '/'; + } + let path = ''; + if (validKind && name && namespace) { + path = `explorer/${validKind}/${namespace}/${name}`; + } else if (namespace) { + path = 'explorer/workload'; + } + return new URL(path, basePath); + } +} diff --git a/plugins/kubernetes-react/src/utils/clusterLinks/formatters/standard.test.ts b/plugins/kubernetes-react/src/api/formatters/StandardClusterLinksFormatter.test.ts similarity index 76% rename from plugins/kubernetes-react/src/utils/clusterLinks/formatters/standard.test.ts rename to plugins/kubernetes-react/src/api/formatters/StandardClusterLinksFormatter.test.ts index 280de6bfde..fe1b2be4f5 100644 --- a/plugins/kubernetes-react/src/utils/clusterLinks/formatters/standard.test.ts +++ b/plugins/kubernetes-react/src/api/formatters/StandardClusterLinksFormatter.test.ts @@ -13,7 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { standardFormatter } from './standard'; + +import { StandardClusterLinksFormatter } from './StandardClusterLinksFormatter'; function formatUrl(url: URL) { // Note that we can't rely on 'url.href' since it will put the search before the hash @@ -23,9 +24,10 @@ function formatUrl(url: URL) { } describe('clusterLinks - standard formatter', () => { - it('should provide a dashboardUrl in the options', () => { - expect(() => - standardFormatter({ + const formatter = new StandardClusterLinksFormatter(); + it('should provide a dashboardUrl in the options', async () => { + await expect(() => + formatter.formatClusterLink({ object: { metadata: { name: 'foobar', @@ -34,10 +36,10 @@ describe('clusterLinks - standard formatter', () => { }, kind: 'Deployment', }), - ).toThrow('standard dashboard requires a dashboardUrl option'); + ).rejects.toThrow('standard dashboard requires a dashboardUrl option'); }); - it('should return an url on the workloads when there is a namespace only', () => { - const url = standardFormatter({ + it('should return an url on the workloads when there is a namespace only', async () => { + const url = await formatter.formatClusterLink({ dashboardUrl: new URL('https://k8s.foo.com'), object: { metadata: { @@ -50,8 +52,8 @@ describe('clusterLinks - standard formatter', () => { 'https://k8s.foo.com/#/workloads?namespace=bar', ); }); - it('should return an url on the workloads when the kind is not recognizeed', () => { - const url = standardFormatter({ + it('should return an url on the workloads when the kind is not recognizeed', async () => { + const url = await formatter.formatClusterLink({ dashboardUrl: new URL('https://k8s.foo.com'), object: { metadata: { @@ -65,8 +67,8 @@ describe('clusterLinks - standard formatter', () => { 'https://k8s.foo.com/#/workloads?namespace=bar', ); }); - it('should return an url on the deployment', () => { - const url = standardFormatter({ + it('should return an url on the deployment', async () => { + const url = await formatter.formatClusterLink({ dashboardUrl: new URL('https://k8s.foo.com/'), object: { metadata: { @@ -80,8 +82,8 @@ describe('clusterLinks - standard formatter', () => { 'https://k8s.foo.com/#/deployment/bar/foobar?namespace=bar', ); }); - it('should return an url on the pod', () => { - const url = standardFormatter({ + it('should return an url on the pod', async () => { + const url = await formatter.formatClusterLink({ dashboardUrl: new URL('https://k8s.foo.com/'), object: { metadata: { @@ -95,8 +97,8 @@ describe('clusterLinks - standard formatter', () => { 'https://k8s.foo.com/#/pod/bar/foobar?namespace=bar', ); }); - it('should return an url on the deployment with a prefix 1', () => { - const url = standardFormatter({ + it('should return an url on the deployment with a prefix 1', async () => { + const url = await formatter.formatClusterLink({ dashboardUrl: new URL('https://k8s.foo.com/some/prefix'), object: { metadata: { @@ -110,8 +112,8 @@ describe('clusterLinks - standard formatter', () => { 'https://k8s.foo.com/some/prefix/#/deployment/bar/foobar?namespace=bar', ); }); - it('should return an url on the deployment with a prefix 2', () => { - const url = standardFormatter({ + it('should return an url on the deployment with a prefix 2', async () => { + const url = await formatter.formatClusterLink({ dashboardUrl: new URL('https://k8s.foo.com/some/prefix/'), object: { metadata: { @@ -125,8 +127,8 @@ describe('clusterLinks - standard formatter', () => { 'https://k8s.foo.com/some/prefix/#/deployment/bar/foobar?namespace=bar', ); }); - it('should return an url on the deployment properly url encoded', () => { - const url = standardFormatter({ + it('should return an url on the deployment properly url encoded', async () => { + const url = await formatter.formatClusterLink({ dashboardUrl: new URL('https://k8s.foo.com/'), object: { metadata: { @@ -140,8 +142,8 @@ describe('clusterLinks - standard formatter', () => { 'https://k8s.foo.com/#/deployment/bar%20bar/foobar?namespace=bar%20bar', ); }); - it('should return an url on the service', () => { - const url = standardFormatter({ + it('should return an url on the service', async () => { + const url = await formatter.formatClusterLink({ dashboardUrl: new URL('https://k8s.foo.com/'), object: { metadata: { @@ -155,8 +157,8 @@ describe('clusterLinks - standard formatter', () => { 'https://k8s.foo.com/#/service/bar/foobar?namespace=bar', ); }); - it('should return an url on the ingress', () => { - const url = standardFormatter({ + it('should return an url on the ingress', async () => { + const url = await formatter.formatClusterLink({ dashboardUrl: new URL('https://k8s.foo.com/'), object: { metadata: { @@ -170,8 +172,8 @@ describe('clusterLinks - standard formatter', () => { 'https://k8s.foo.com/#/ingress/bar/foobar?namespace=bar', ); }); - it('should return an url on the deployment for a hpa', () => { - const url = standardFormatter({ + it('should return an url on the deployment for a hpa', async () => { + const url = await formatter.formatClusterLink({ dashboardUrl: new URL('https://k8s.foo.com/'), object: { metadata: { diff --git a/plugins/kubernetes-react/src/api/formatters/StandardClusterLinksFormatter.ts b/plugins/kubernetes-react/src/api/formatters/StandardClusterLinksFormatter.ts new file mode 100644 index 0000000000..76537c5a4e --- /dev/null +++ b/plugins/kubernetes-react/src/api/formatters/StandardClusterLinksFormatter.ts @@ -0,0 +1,56 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { + ClusterLinksFormatter, + ClusterLinksFormatterOptions, +} from '../../types'; + +const kindMappings: Record = { + deployment: 'deployment', + pod: 'pod', + ingress: 'ingress', + service: 'service', + horizontalpodautoscaler: 'deployment', + statefulset: 'statefulset', +}; + +/** @public */ +export class StandardClusterLinksFormatter implements ClusterLinksFormatter { + async formatClusterLink(options: ClusterLinksFormatterOptions): Promise { + if (!options.dashboardUrl) { + throw new Error('standard dashboard requires a dashboardUrl option'); + } + const result = new URL(options.dashboardUrl.href); + const name = encodeURIComponent(options.object.metadata?.name ?? ''); + const namespace = encodeURIComponent( + options.object.metadata?.namespace ?? '', + ); + const validKind = kindMappings[options.kind.toLocaleLowerCase('en-US')]; + if (!result.pathname.endsWith('/')) { + result.pathname += '/'; + } + if (validKind && name && namespace) { + result.hash = `/${validKind}/${namespace}/${name}`; + } else if (namespace) { + result.hash = '/workloads'; + } + if (namespace) { + // Note that Angular SPA requires a hash and the query parameter should be part of it + result.hash += `?namespace=${namespace}`; + } + return result; + } +} diff --git a/plugins/kubernetes-react/src/api/formatters/index.ts b/plugins/kubernetes-react/src/api/formatters/index.ts new file mode 100644 index 0000000000..97a3140d50 --- /dev/null +++ b/plugins/kubernetes-react/src/api/formatters/index.ts @@ -0,0 +1,50 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { AksClusterLinksFormatter } from './AksClusterLinksFormatter'; +import { ClusterLinksFormatter } from '../../types'; +import { EksClusterLinksFormatter } from './EksClusterLinksFormatter'; +import { GkeClusterLinksFormatter } from './GkeClusterLinksFormatter'; +import { StandardClusterLinksFormatter } from './StandardClusterLinksFormatter'; +import { OpenshiftClusterLinksFormatter } from './OpenshiftClusterLinksFormatter'; +import { RancherClusterLinksFormatter } from './RancherClusterLinksFormatter'; + +export { + StandardClusterLinksFormatter, + AksClusterLinksFormatter, + EksClusterLinksFormatter, + GkeClusterLinksFormatter, + OpenshiftClusterLinksFormatter, + RancherClusterLinksFormatter, +}; + +/** @public */ +export const DEFAULT_FORMATTER_NAME = 'standard'; + +/** @public */ +export function getDefaultFormatters(_deps: {}): Record< + string, + ClusterLinksFormatter +> { + return { + standard: new StandardClusterLinksFormatter(), + aks: new AksClusterLinksFormatter(), + eks: new EksClusterLinksFormatter(), + gke: new GkeClusterLinksFormatter(), + openshift: new OpenshiftClusterLinksFormatter(), + rancher: new RancherClusterLinksFormatter(), + }; +} diff --git a/plugins/kubernetes-react/src/api/index.ts b/plugins/kubernetes-react/src/api/index.ts index ec8843937a..42fbcf1618 100644 --- a/plugins/kubernetes-react/src/api/index.ts +++ b/plugins/kubernetes-react/src/api/index.ts @@ -14,7 +14,18 @@ * limitations under the License. */ -export { kubernetesApiRef, kubernetesProxyApiRef } from './types'; -export type { KubernetesApi, KubernetesProxyApi } from './types'; +export { + kubernetesApiRef, + kubernetesProxyApiRef, + kubernetesClusterLinkFormatterApiRef, +} from './types'; +export type { + KubernetesApi, + KubernetesProxyApi, + FormatClusterLinkOptions, + KubernetesClusterLinkFormatterApi, +} from './types'; export { KubernetesBackendClient } from './KubernetesBackendClient'; +export { KubernetesClusterLinkFormatter } from './KubernetesClusterLinkFormatter'; export { KubernetesProxyClient } from './KubernetesProxyClient'; +export * from './formatters'; diff --git a/plugins/kubernetes-react/src/api/types.ts b/plugins/kubernetes-react/src/api/types.ts index 28cf2dde55..cc27bf9dd4 100644 --- a/plugins/kubernetes-react/src/api/types.ts +++ b/plugins/kubernetes-react/src/api/types.ts @@ -22,6 +22,7 @@ import { } from '@backstage/plugin-kubernetes-common'; import { createApiRef } from '@backstage/core-plugin-api'; import { Event } from 'kubernetes-models/v1'; +import { JsonObject } from '@backstage/types'; /** @public */ export const kubernetesApiRef = createApiRef({ @@ -33,6 +34,12 @@ export const kubernetesProxyApiRef = createApiRef({ id: 'plugin.kubernetes.proxy-service', }); +/** @public */ +export const kubernetesClusterLinkFormatterApiRef = + createApiRef({ + id: 'plugin.kubernetes.cluster-link-formatter-service', + }); + /** @public */ export interface KubernetesApi { getObjectsByEntity( @@ -82,3 +89,21 @@ export interface KubernetesProxyApi { namespace: string; }): Promise; } + +/** + * @public + */ +export type FormatClusterLinkOptions = { + dashboardUrl?: string; + dashboardApp?: string; + dashboardParameters?: JsonObject; + object: any; + kind: string; +}; + +/** @public */ +export interface KubernetesClusterLinkFormatterApi { + formatClusterLink( + options: FormatClusterLinkOptions, + ): Promise; +} diff --git a/plugins/kubernetes-react/src/components/CronJobsAccordions/CronJobsDrawer.test.tsx b/plugins/kubernetes-react/src/components/CronJobsAccordions/CronJobsDrawer.test.tsx index b16e83ee50..75506476ea 100644 --- a/plugins/kubernetes-react/src/components/CronJobsAccordions/CronJobsDrawer.test.tsx +++ b/plugins/kubernetes-react/src/components/CronJobsAccordions/CronJobsDrawer.test.tsx @@ -15,16 +15,20 @@ */ import React from 'react'; import * as oneCronJobsFixture from '../../__fixtures__/1-cronjobs.json'; -import { renderInTestApp } from '@backstage/test-utils'; +import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; import { CronJobDrawer } from './CronJobsDrawer'; +import { kubernetesClusterLinkFormatterApiRef } from '../../api'; describe('CronJobDrawer', () => { it('should render cronJob drawer', async () => { const { getByText, getAllByText } = await renderInTestApp( - , + + + , + , ); expect(getAllByText('dice-roller-cronjob')).toHaveLength(2); diff --git a/plugins/kubernetes-react/src/components/DeploymentsAccordions/DeploymentDrawer.test.tsx b/plugins/kubernetes-react/src/components/DeploymentsAccordions/DeploymentDrawer.test.tsx index f221a36cee..1e8515f81a 100644 --- a/plugins/kubernetes-react/src/components/DeploymentsAccordions/DeploymentDrawer.test.tsx +++ b/plugins/kubernetes-react/src/components/DeploymentsAccordions/DeploymentDrawer.test.tsx @@ -16,16 +16,24 @@ import React from 'react'; import * as deployments from '../../__fixtures__/2-deployments.json'; -import { renderInTestApp, textContentMatcher } from '@backstage/test-utils'; +import { + renderInTestApp, + TestApiProvider, + textContentMatcher, +} from '@backstage/test-utils'; import { DeploymentDrawer } from './DeploymentDrawer'; +import { kubernetesClusterLinkFormatterApiRef } from '../../api'; describe('DeploymentDrawer', () => { it('should render deployment drawer', async () => { const { getByText, getAllByText } = await renderInTestApp( - , + + + , + , ); expect(getAllByText('dice-roller')).toHaveLength(2); @@ -53,13 +61,16 @@ describe('DeploymentDrawer', () => { it('should render deployment drawer without namespace', async () => { const deployment = (deployments as any).deployments[0]; const { queryByText } = await renderInTestApp( - , + + + , + , ); expect(queryByText('namespace: default')).not.toBeInTheDocument(); diff --git a/plugins/kubernetes-react/src/components/HorizontalPodAutoscalers/HorizontalPodAutoscalerDrawer.test.tsx b/plugins/kubernetes-react/src/components/HorizontalPodAutoscalers/HorizontalPodAutoscalerDrawer.test.tsx index beb0db1e2b..6599a2041f 100644 --- a/plugins/kubernetes-react/src/components/HorizontalPodAutoscalers/HorizontalPodAutoscalerDrawer.test.tsx +++ b/plugins/kubernetes-react/src/components/HorizontalPodAutoscalers/HorizontalPodAutoscalerDrawer.test.tsx @@ -17,15 +17,18 @@ import React from 'react'; import { screen } from '@testing-library/react'; import * as hpas from './__fixtures__/horizontalpodautoscalers.json'; -import { renderInTestApp } from '@backstage/test-utils'; +import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; import { HorizontalPodAutoscalerDrawer } from './HorizontalPodAutoscalerDrawer'; +import { kubernetesClusterLinkFormatterApiRef } from '../../api'; describe('HorizontalPodAutoscalersDrawer', () => { it('should render hpa drawer', async () => { await renderInTestApp( - -

CHILD

-
, + + +

CHILD

+
+
, ); expect(screen.getByText('dice-roller')).toBeInTheDocument(); diff --git a/plugins/kubernetes-react/src/components/IngressesAccordions/IngressDrawer.test.tsx b/plugins/kubernetes-react/src/components/IngressesAccordions/IngressDrawer.test.tsx index 8619e362b7..cfacb24ee4 100644 --- a/plugins/kubernetes-react/src/components/IngressesAccordions/IngressDrawer.test.tsx +++ b/plugins/kubernetes-react/src/components/IngressesAccordions/IngressDrawer.test.tsx @@ -14,16 +14,23 @@ * limitations under the License. */ -import { renderInTestApp, textContentMatcher } from '@backstage/test-utils'; +import { + renderInTestApp, + TestApiProvider, + textContentMatcher, +} from '@backstage/test-utils'; import { screen } from '@testing-library/react'; import React from 'react'; import { IngressDrawer } from './IngressDrawer'; import * as ingresses from './__fixtures__/2-ingresses.json'; +import { kubernetesClusterLinkFormatterApiRef } from '../../api'; describe('IngressDrawer', () => { it('should render ingress drawer', async () => { await renderInTestApp( - , + + + , ); expect(screen.getAllByText('awesome-service')).toHaveLength(4); diff --git a/plugins/kubernetes-react/src/components/JobsAccordions/JobsDrawer.test.tsx b/plugins/kubernetes-react/src/components/JobsAccordions/JobsDrawer.test.tsx index 48a4e6fe7e..23f49a2118 100644 --- a/plugins/kubernetes-react/src/components/JobsAccordions/JobsDrawer.test.tsx +++ b/plugins/kubernetes-react/src/components/JobsAccordions/JobsDrawer.test.tsx @@ -15,13 +15,16 @@ */ import React from 'react'; import * as oneCronJobsFixture from '../../__fixtures__/1-cronjobs.json'; -import { renderInTestApp } from '@backstage/test-utils'; +import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; import { JobDrawer } from './JobsDrawer'; +import { kubernetesClusterLinkFormatterApiRef } from '../../api'; describe('JobDrawer', () => { it('should render job drawer', async () => { const { getByText, getAllByText } = await renderInTestApp( - , + + , + , ); expect(getAllByText('dice-roller-cronjob-1637025000')).toHaveLength(2); diff --git a/plugins/kubernetes-react/src/components/KubernetesDrawer/KubernetesStructuredMetadataTableDrawer.tsx b/plugins/kubernetes-react/src/components/KubernetesDrawer/KubernetesStructuredMetadataTableDrawer.tsx index 65001c658e..0b6cfa26b4 100644 --- a/plugins/kubernetes-react/src/components/KubernetesDrawer/KubernetesStructuredMetadataTableDrawer.tsx +++ b/plugins/kubernetes-react/src/components/KubernetesDrawer/KubernetesStructuredMetadataTableDrawer.tsx @@ -37,10 +37,11 @@ import { WarningPanel, } from '@backstage/core-components'; import { ClusterContext } from '../../hooks'; -import { formatClusterLink } from '../../utils/clusterLinks'; import { ClusterAttributes } from '@backstage/plugin-kubernetes-common'; -import { FormatClusterLinkOptions } from '../../utils/clusterLinks/formatClusterLink'; import { ManifestYaml } from './ManifestYaml'; +import { useApi } from '@backstage/core-plugin-api'; +import { kubernetesClusterLinkFormatterApiRef } from '../../api'; +import useAsync from 'react-use/lib/useAsync'; const useDrawerStyles = makeStyles((theme: Theme) => createStyles({ @@ -147,20 +148,6 @@ function replaceNullsWithUndefined(someObj: any) { return JSON.parse(JSON.stringify(someObj, replacer)); } -function tryFormatClusterLink(options: FormatClusterLinkOptions) { - try { - return { - clusterLink: formatClusterLink(options), - errorMessage: '', - }; - } catch (err) { - return { - clusterLink: '', - errorMessage: err.message || err.toString(), - }; - } -} - const KubernetesStructuredMetadataTableDrawerContent = < T extends KubernetesDrawerable, >({ @@ -171,15 +158,20 @@ const KubernetesStructuredMetadataTableDrawerContent = < }: KubernetesStructuredMetadataTableDrawerContentProps) => { const [isYaml, setIsYaml] = useState(false); + const formatter = useApi(kubernetesClusterLinkFormatterApiRef); const classes = useDrawerContentStyles(); const cluster = useContext(ClusterContext); - const { clusterLink, errorMessage } = tryFormatClusterLink({ - dashboardUrl: cluster.dashboardUrl, - dashboardApp: cluster.dashboardApp, - dashboardParameters: cluster.dashboardParameters, - object, - kind, - }); + const { value: clusterLink, error } = useAsync( + async () => + formatter.formatClusterLink({ + dashboardUrl: cluster.dashboardUrl, + dashboardApp: cluster.dashboardApp, + dashboardParameters: cluster.dashboardParameters, + object, + kind, + }), + [cluster, object, kind, formatter], + ); return ( <> @@ -221,9 +213,12 @@ const KubernetesStructuredMetadataTableDrawerContent = <
- {errorMessage && ( + {error && (
- +
)}
diff --git a/plugins/kubernetes-react/src/components/ServicesAccordions/ServiceDrawer.test.tsx b/plugins/kubernetes-react/src/components/ServicesAccordions/ServiceDrawer.test.tsx index dda5e300eb..7eec2926c4 100644 --- a/plugins/kubernetes-react/src/components/ServicesAccordions/ServiceDrawer.test.tsx +++ b/plugins/kubernetes-react/src/components/ServicesAccordions/ServiceDrawer.test.tsx @@ -17,13 +17,20 @@ import React from 'react'; import { screen } from '@testing-library/react'; import * as services from './__fixtures__/2-services.json'; -import { textContentMatcher, renderInTestApp } from '@backstage/test-utils'; +import { + textContentMatcher, + renderInTestApp, + TestApiProvider, +} from '@backstage/test-utils'; import { ServiceDrawer } from './ServiceDrawer'; +import { kubernetesClusterLinkFormatterApiRef } from '../../api'; describe('ServiceDrawer', () => { it('should render deployment drawer', async () => { await renderInTestApp( - , + + , + , ); expect(screen.getAllByText('awesome-service-grpc')).toHaveLength(2); diff --git a/plugins/kubernetes-react/src/components/StatefulSetsAccordions/StatefulSetDrawer.test.tsx b/plugins/kubernetes-react/src/components/StatefulSetsAccordions/StatefulSetDrawer.test.tsx index c187c7d05d..d69ab15f1a 100644 --- a/plugins/kubernetes-react/src/components/StatefulSetsAccordions/StatefulSetDrawer.test.tsx +++ b/plugins/kubernetes-react/src/components/StatefulSetsAccordions/StatefulSetDrawer.test.tsx @@ -16,16 +16,24 @@ import React from 'react'; import * as statefulsets from '../../__fixtures__/2-statefulsets.json'; -import { renderInTestApp, textContentMatcher } from '@backstage/test-utils'; +import { + renderInTestApp, + TestApiProvider, + textContentMatcher, +} from '@backstage/test-utils'; import { StatefulSetDrawer } from './StatefulSetDrawer'; +import { kubernetesClusterLinkFormatterApiRef } from '../../api'; describe('StatefulSetDrawer', () => { it('should render statefulset drawer', async () => { const { getByText, getAllByText } = await renderInTestApp( - , + + + , + , ); expect(getAllByText('dice-roller')).toHaveLength(4); @@ -55,13 +63,16 @@ describe('StatefulSetDrawer', () => { it('should render statefulset drawer without namespace', async () => { const statefulset = (statefulsets as any).statefulsets[0]; const { queryByText } = await renderInTestApp( - , + + + , + , ); expect(queryByText('namespace: default')).not.toBeInTheDocument(); diff --git a/plugins/kubernetes-react/src/index.ts b/plugins/kubernetes-react/src/index.ts index 4fb8614091..326b363b05 100644 --- a/plugins/kubernetes-react/src/index.ts +++ b/plugins/kubernetes-react/src/index.ts @@ -27,5 +27,4 @@ export * from './hooks'; export * from './api'; export * from './kubernetes-auth-provider'; export * from './components'; -export * from './utils'; export * from './types'; diff --git a/plugins/kubernetes-react/src/types/types.ts b/plugins/kubernetes-react/src/types/types.ts index ad8df040ac..5db2e281f4 100644 --- a/plugins/kubernetes-react/src/types/types.ts +++ b/plugins/kubernetes-react/src/types/types.ts @@ -29,6 +29,6 @@ export interface ClusterLinksFormatterOptions { /** * @public */ -export type ClusterLinksFormatter = ( - options: ClusterLinksFormatterOptions, -) => URL; +export interface ClusterLinksFormatter { + formatClusterLink(options: ClusterLinksFormatterOptions): Promise; +} diff --git a/plugins/kubernetes-react/src/utils/clusterLinks/formatClusterLink.test.ts b/plugins/kubernetes-react/src/utils/clusterLinks/formatClusterLink.test.ts deleted file mode 100644 index 254b9fc51b..0000000000 --- a/plugins/kubernetes-react/src/utils/clusterLinks/formatClusterLink.test.ts +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright 2020 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import { formatClusterLink } from './formatClusterLink'; - -describe('clusterLinks', () => { - describe('formatClusterLink', () => { - it('should not return an url when there is no dashboard url', () => { - const url = formatClusterLink({ object: {}, kind: 'foo' }); - expect(url).toBeUndefined(); - }); - it('should return an url even when there is no object', () => { - const url = formatClusterLink({ - dashboardUrl: 'https://k8s.foo.com', - object: undefined, - kind: 'foo', - }); - expect(url).toBe('https://k8s.foo.com'); - }); - it('should throw when the app is not recognized', () => { - expect(() => - formatClusterLink({ - dashboardUrl: 'https://k8s.foo.com', - dashboardApp: 'unknownapp', - object: { - metadata: { - name: 'foobar', - namespace: 'bar', - }, - }, - kind: 'Deployment', - }), - ).toThrow("Could not find Kubernetes dashboard app named 'unknownapp'"); - }); - - describe('default app', () => { - it('should return an url on the deployment', () => { - const url = formatClusterLink({ - dashboardUrl: 'https://k8s.foo.com/', - object: { - metadata: { - name: 'foobar', - namespace: 'bar', - }, - }, - kind: 'Deployment', - }); - expect(url).toBe( - 'https://k8s.foo.com/#/deployment/bar/foobar?namespace=bar', - ); - }); - it('should return an url on the service', () => { - const url = formatClusterLink({ - dashboardUrl: 'https://k8s.foo.com/', - object: { - metadata: { - name: 'foobar', - namespace: 'bar', - }, - }, - kind: 'Service', - }); - expect(url).toBe( - 'https://k8s.foo.com/#/service/bar/foobar?namespace=bar', - ); - }); - }); - - describe('standard app', () => { - it('should return an url on the deployment', () => { - const url = formatClusterLink({ - dashboardUrl: 'https://k8s.foo.com/', - dashboardApp: 'standard', - object: { - metadata: { - name: 'foobar', - namespace: 'bar', - }, - }, - kind: 'Deployment', - }); - expect(url).toBe( - 'https://k8s.foo.com/#/deployment/bar/foobar?namespace=bar', - ); - }); - it('should return an url on the service', () => { - const url = formatClusterLink({ - dashboardUrl: 'https://k8s.foo.com/', - dashboardApp: 'standard', - object: { - metadata: { - name: 'foobar', - namespace: 'bar', - }, - }, - kind: 'Service', - }); - expect(url).toBe( - 'https://k8s.foo.com/#/service/bar/foobar?namespace=bar', - ); - }); - }); - describe('GKE app', () => { - it('should return an url on the deployment', () => { - const url = formatClusterLink({ - dashboardApp: 'gke', - dashboardParameters: { - projectId: 'foobar-333614', - region: 'us-east1-c', - clusterName: 'cluster-1', - }, - object: { - metadata: { - name: 'foobar', - namespace: 'bar', - }, - }, - kind: 'Deployment', - }); - expect(url).toBe( - 'https://console.cloud.google.com/kubernetes/deployment/us-east1-c/cluster-1/bar/foobar/overview?project=foobar-333614', - ); - }); - it('should return an url on the service', () => { - const url = formatClusterLink({ - dashboardApp: 'gke', - dashboardParameters: { - projectId: 'foobar-333614', - region: 'us-east1-c', - clusterName: 'cluster-1', - }, - object: { - metadata: { - name: 'foobar', - namespace: 'bar', - }, - }, - kind: 'Service', - }); - expect(url).toBe( - 'https://console.cloud.google.com/kubernetes/service/us-east1-c/cluster-1/bar/foobar/overview?project=foobar-333614', - ); - }); - }); - }); -}); diff --git a/plugins/kubernetes-react/src/utils/clusterLinks/formatClusterLink.ts b/plugins/kubernetes-react/src/utils/clusterLinks/formatClusterLink.ts deleted file mode 100644 index 8f7e43a121..0000000000 --- a/plugins/kubernetes-react/src/utils/clusterLinks/formatClusterLink.ts +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2020 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type { JsonObject } from '@backstage/types'; -import { defaultFormatterName, clusterLinksFormatters } from './formatters'; - -/** - * @public - */ -export type FormatClusterLinkOptions = { - dashboardUrl?: string; - dashboardApp?: string; - dashboardParameters?: JsonObject; - object: any; - kind: string; -}; - -/** - * @public - */ -export function formatClusterLink(options: FormatClusterLinkOptions) { - if (!options.dashboardUrl && !options.dashboardParameters) { - return undefined; - } - if (options.dashboardUrl && !options.object) { - return options.dashboardUrl; - } - const app = options.dashboardApp || defaultFormatterName; - const formatter = clusterLinksFormatters[app]; - if (!formatter) { - throw new Error(`Could not find Kubernetes dashboard app named '${app}'`); - } - const url = formatter({ - dashboardUrl: options.dashboardUrl - ? new URL(options.dashboardUrl) - : undefined, - dashboardParameters: options.dashboardParameters, - object: options.object, - kind: options.kind, - }); - return url.toString(); -} diff --git a/plugins/kubernetes-react/src/utils/clusterLinks/formatters/aks.ts b/plugins/kubernetes-react/src/utils/clusterLinks/formatters/aks.ts deleted file mode 100644 index 71fffb00e6..0000000000 --- a/plugins/kubernetes-react/src/utils/clusterLinks/formatters/aks.ts +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2020 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import { ClusterLinksFormatterOptions } from '../../../types/types'; - -const basePath = - 'https://portal.azure.com/#blade/Microsoft_Azure_ContainerService/AksK8ResourceMenuBlade/overview-Deployment/aksClusterId'; - -const requiredParams = ['subscriptionId', 'resourceGroup', 'clusterName']; - -export function aksFormatter(options: ClusterLinksFormatterOptions): URL { - if (!options.dashboardParameters) { - throw new Error('AKS dashboard requires a dashboardParameters option'); - } - const args = options.dashboardParameters; - for (const param of requiredParams) { - if (typeof args[param] !== 'string') { - throw new Error( - `AKS dashboard requires a "${param}" of type string in the dashboardParameters option`, - ); - } - } - - const path = `/subscriptions/${args.subscriptionId}/resourceGroups/${args.resourceGroup}/providers/Microsoft.ContainerService/managedClusters/${args.clusterName}`; - - const { name, namespace, uid } = options.object.metadata; - const { selector } = options.object.spec; - const params = { - kind: options.kind, - metadata: { name, namespace, uid }, - spec: { - selector, - }, - }; - - return new URL( - `${basePath}/${encodeURIComponent(path)}/resource/${encodeURIComponent( - JSON.stringify(params), - )}`, - ); -} diff --git a/plugins/kubernetes-react/src/utils/clusterLinks/formatters/gke.ts b/plugins/kubernetes-react/src/utils/clusterLinks/formatters/gke.ts deleted file mode 100644 index 957032e6e2..0000000000 --- a/plugins/kubernetes-react/src/utils/clusterLinks/formatters/gke.ts +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2020 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import { ClusterLinksFormatterOptions } from '../../../types/types'; - -const kindMappings: Record = { - deployment: 'deployment', - pod: 'pod', - ingress: 'ingress', - service: 'service', - horizontalpodautoscaler: 'deployment', -}; - -export function gkeFormatter(options: ClusterLinksFormatterOptions): URL { - if (!options.dashboardParameters) { - throw new Error('GKE dashboard requires a dashboardParameters option'); - } - const args = options.dashboardParameters; - if (typeof args.projectId !== 'string') { - throw new Error( - 'GKE dashboard requires a "projectId" of type string in the dashboardParameters option', - ); - } - if (typeof args.region !== 'string') { - throw new Error( - 'GKE dashboard requires a "region" of type string in the dashboardParameters option', - ); - } - if (typeof args.clusterName !== 'string') { - throw new Error( - 'GKE dashboard requires a "clusterName" of type string in the dashboardParameters option', - ); - } - const basePath = new URL('https://console.cloud.google.com/'); - const region = encodeURIComponent(args.region); - const clusterName = encodeURIComponent(args.clusterName); - const name = encodeURIComponent(options.object.metadata?.name ?? ''); - const namespace = encodeURIComponent( - options.object.metadata?.namespace ?? '', - ); - const validKind = kindMappings[options.kind.toLocaleLowerCase('en-US')]; - let path = ''; - if (namespace && name && validKind) { - const kindsWithDetails = ['ingress', 'pod']; - const landingPage = kindsWithDetails.includes(validKind) - ? 'details' - : 'overview'; - path = `kubernetes/${validKind}/${region}/${clusterName}/${namespace}/${name}/${landingPage}`; - } else { - path = `kubernetes/clusters/details/${region}/${clusterName}/details`; - } - const result = new URL(path, basePath); - result.searchParams.set('project', args.projectId); - return result; -} diff --git a/plugins/kubernetes-react/src/utils/clusterLinks/formatters/index.ts b/plugins/kubernetes-react/src/utils/clusterLinks/formatters/index.ts deleted file mode 100644 index b75e45cc7f..0000000000 --- a/plugins/kubernetes-react/src/utils/clusterLinks/formatters/index.ts +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2020 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import { ClusterLinksFormatter } from '../../../types/types'; -import { standardFormatter } from './standard'; -import { rancherFormatter } from './rancher'; -import { openshiftFormatter } from './openshift'; -import { aksFormatter } from './aks'; -import { eksFormatter } from './eks'; -import { gkeFormatter } from './gke'; - -/** - * @public - */ -export const clusterLinksFormatters: Record = { - standard: standardFormatter, - rancher: rancherFormatter, - openshift: openshiftFormatter, - aks: aksFormatter, - eks: eksFormatter, - gke: gkeFormatter, -}; -export const defaultFormatterName = 'standard'; diff --git a/plugins/kubernetes-react/src/utils/clusterLinks/formatters/openshift.ts b/plugins/kubernetes-react/src/utils/clusterLinks/formatters/openshift.ts deleted file mode 100644 index c5b7313061..0000000000 --- a/plugins/kubernetes-react/src/utils/clusterLinks/formatters/openshift.ts +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2020 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import { ClusterLinksFormatterOptions } from '../../../types/types'; - -const kindMappings: Record = { - deployment: 'deployments', - ingress: 'ingresses', - service: 'services', - horizontalpodautoscaler: 'horizontalpodautoscalers', - persistentvolume: 'persistentvolumes', -}; - -export function openshiftFormatter(options: ClusterLinksFormatterOptions): URL { - if (!options.dashboardUrl) { - throw new Error('OpenShift dashboard requires a dashboardUrl option'); - } - const basePath = new URL(options.dashboardUrl.href); - const name = encodeURIComponent(options.object.metadata?.name ?? ''); - const namespace = encodeURIComponent( - options.object.metadata?.namespace ?? '', - ); - const validKind = kindMappings[options.kind.toLocaleLowerCase('en-US')]; - if (!basePath.pathname.endsWith('/')) { - // a dashboard url with a path should end with a slash otherwise - // the new combined URL will replace the last segment with the appended path! - // https://foobar.com/abc/def + k8s/cluster/projects/test --> https://foobar.com/abc/k8s/cluster/projects/test - // https://foobar.com/abc/def/ + k8s/cluster/projects/test --> https://foobar.com/abc/def/k8s/cluster/projects/test - basePath.pathname += '/'; - } - let path = ''; - if (namespace) { - if (name && validKind) { - path = `k8s/ns/${namespace}/${validKind}/${name}`; - } else { - path = `k8s/cluster/projects/${namespace}`; - } - } else if (validKind) { - path = `k8s/cluster/${validKind}`; - if (name) { - path += `/${name}`; - } - } - return new URL(path, basePath); -} diff --git a/plugins/kubernetes-react/src/utils/clusterLinks/formatters/rancher.ts b/plugins/kubernetes-react/src/utils/clusterLinks/formatters/rancher.ts deleted file mode 100644 index 6c72730181..0000000000 --- a/plugins/kubernetes-react/src/utils/clusterLinks/formatters/rancher.ts +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2020 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import { ClusterLinksFormatterOptions } from '../../../types/types'; - -const kindMappings: Record = { - deployment: 'apps.deployment', - ingress: 'networking.k8s.io.ingress', - service: 'service', - horizontalpodautoscaler: 'autoscaling.horizontalpodautoscaler', -}; - -export function rancherFormatter(options: ClusterLinksFormatterOptions): URL { - if (!options.dashboardUrl) { - throw new Error('Rancher dashboard requires a dashboardUrl option'); - } - const basePath = new URL(options.dashboardUrl.href); - const name = encodeURIComponent(options.object.metadata?.name ?? ''); - const namespace = encodeURIComponent( - options.object.metadata?.namespace ?? '', - ); - const validKind = kindMappings[options.kind.toLocaleLowerCase('en-US')]; - if (!basePath.pathname.endsWith('/')) { - // a dashboard url with a path should end with a slash otherwise - // the new combined URL will replace the last segment with the appended path! - // https://foobar.com/abc/def + explorer/service/test --> https://foobar.com/abc/explorer/service/test - // https://foobar.com/abc/def/ + explorer/service/test --> https://foobar.com/abc/def/explorer/service/test - basePath.pathname += '/'; - } - let path = ''; - if (validKind && name && namespace) { - path = `explorer/${validKind}/${namespace}/${name}`; - } else if (namespace) { - path = 'explorer/workload'; - } - return new URL(path, basePath); -} diff --git a/plugins/kubernetes-react/src/utils/clusterLinks/formatters/standard.ts b/plugins/kubernetes-react/src/utils/clusterLinks/formatters/standard.ts deleted file mode 100644 index e26adf98e6..0000000000 --- a/plugins/kubernetes-react/src/utils/clusterLinks/formatters/standard.ts +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2020 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import { ClusterLinksFormatterOptions } from '../../../types/types'; - -const kindMappings: Record = { - deployment: 'deployment', - pod: 'pod', - ingress: 'ingress', - service: 'service', - horizontalpodautoscaler: 'deployment', - statefulset: 'statefulset', -}; - -export function standardFormatter(options: ClusterLinksFormatterOptions) { - if (!options.dashboardUrl) { - throw new Error('standard dashboard requires a dashboardUrl option'); - } - const result = new URL(options.dashboardUrl.href); - const name = encodeURIComponent(options.object.metadata?.name ?? ''); - const namespace = encodeURIComponent( - options.object.metadata?.namespace ?? '', - ); - const validKind = kindMappings[options.kind.toLocaleLowerCase('en-US')]; - if (!result.pathname.endsWith('/')) { - result.pathname += '/'; - } - if (validKind && name && namespace) { - result.hash = `/${validKind}/${namespace}/${name}`; - } else if (namespace) { - result.hash = '/workloads'; - } - if (namespace) { - // Note that Angular SPA requires a hash and the query parameter should be part of it - result.hash += `?namespace=${namespace}`; - } - return result; -} diff --git a/plugins/kubernetes/CHANGELOG.md b/plugins/kubernetes/CHANGELOG.md index f4a0c0dfd9..0131eca6ce 100644 --- a/plugins/kubernetes/CHANGELOG.md +++ b/plugins/kubernetes/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-kubernetes +## 0.11.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/plugin-kubernetes-common@0.7.2-next.1 + - @backstage/plugin-kubernetes-react@0.1.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + - @backstage/types@1.1.1 + ## 0.11.3-next.0 ### Patch Changes diff --git a/plugins/kubernetes/package.json b/plugins/kubernetes/package.json index 8970a9d19e..897e1abe04 100644 --- a/plugins/kubernetes/package.json +++ b/plugins/kubernetes/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-kubernetes", "description": "A Backstage plugin that integrates towards Kubernetes", - "version": "0.11.3-next.0", + "version": "0.11.3-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/kubernetes/src/plugin.ts b/plugins/kubernetes/src/plugin.ts index ac55dbbf23..c479f99aa0 100644 --- a/plugins/kubernetes/src/plugin.ts +++ b/plugins/kubernetes/src/plugin.ts @@ -20,6 +20,10 @@ import { kubernetesAuthProvidersApiRef, KubernetesAuthProviders, KubernetesProxyClient, + kubernetesClusterLinkFormatterApiRef, + getDefaultFormatters, + KubernetesClusterLinkFormatter, + DEFAULT_FORMATTER_NAME, } from '@backstage/plugin-kubernetes-react'; import { createApiFactory, @@ -97,6 +101,17 @@ export const kubernetesPlugin = createPlugin({ }); }, }), + createApiFactory({ + api: kubernetesClusterLinkFormatterApiRef, + deps: {}, + factory: deps => { + const formatters = getDefaultFormatters(deps); + return new KubernetesClusterLinkFormatter({ + formatters, + defaultFormatterName: DEFAULT_FORMATTER_NAME, + }); + }, + }), ], routes: { entityContent: rootCatalogKubernetesRouteRef, diff --git a/plugins/lighthouse-backend/CHANGELOG.md b/plugins/lighthouse-backend/CHANGELOG.md index 4d13ee7bb9..f5a92f575b 100644 --- a/plugins/lighthouse-backend/CHANGELOG.md +++ b/plugins/lighthouse-backend/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-lighthouse-backend +## 0.3.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.5.0-next.0 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/backend-tasks@0.5.13-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-node@1.5.1-next.1 + - @backstage/plugin-lighthouse-common@0.1.4 + ## 0.3.5-next.0 ### Patch Changes diff --git a/plugins/lighthouse-backend/README.md b/plugins/lighthouse-backend/README.md index 28845d3c73..687467f1d5 100644 --- a/plugins/lighthouse-backend/README.md +++ b/plugins/lighthouse-backend/README.md @@ -19,13 +19,19 @@ import { PluginEnvironment } from '../types'; import { CatalogClient } from '@backstage/catalog-client'; export default async function createPlugin(env: PluginEnvironment) { - const { logger, scheduler, config } = env; + const { logger, scheduler, config, tokenManager } = env; const catalogClient = new CatalogClient({ discoveryApi: env.discovery, }); - await createScheduler({ logger, scheduler, config, catalogClient }); + await createScheduler({ + logger, + scheduler, + config, + catalogClient, + tokenManager, + }); } ``` @@ -80,5 +86,8 @@ You can define how often and when the scheduler should run the audits: ```yaml lighthouse: schedule: - days: 1 + frequency: + hours: 12 # Default: 1 day + timeout: + minutes: 30 # Default: 10 minutes ``` diff --git a/plugins/lighthouse-backend/config.d.ts b/plugins/lighthouse-backend/config.d.ts new file mode 100644 index 0000000000..2d49f188b6 --- /dev/null +++ b/plugins/lighthouse-backend/config.d.ts @@ -0,0 +1,30 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { TaskScheduleDefinitionConfig } from '@backstage/backend-tasks'; +import { HumanDuration } from '@backstage/types'; + +export interface Config { + /** + * Configuration options for the Lighthouse backend plugin. + */ + lighthouse?: { + /** + * Schedule of the audit + */ + schedule?: HumanDuration | TaskScheduleDefinitionConfig; + }; +} diff --git a/plugins/lighthouse-backend/package.json b/plugins/lighthouse-backend/package.json index 84cff5b8eb..a143ec77d9 100644 --- a/plugins/lighthouse-backend/package.json +++ b/plugins/lighthouse-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-lighthouse-backend", "description": "Backend functionalities for lighthouse", - "version": "0.3.5-next.0", + "version": "0.3.5-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -24,8 +24,10 @@ "lighthouse" ], "files": [ - "dist" + "dist", + "config.d.ts" ], + "configSchema": "config.d.ts", "scripts": { "build": "backstage-cli package build", "lint": "backstage-cli package lint", diff --git a/plugins/lighthouse-backend/src/config.ts b/plugins/lighthouse-backend/src/config.ts index bea1c35661..736a4e569d 100644 --- a/plugins/lighthouse-backend/src/config.ts +++ b/plugins/lighthouse-backend/src/config.ts @@ -13,56 +13,66 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { LoggerService } from '@backstage/backend-plugin-api'; +import { + TaskScheduleDefinition, + readTaskScheduleDefinitionFromConfig, +} from '@backstage/backend-tasks'; +import { Config, readDurationFromConfig } from '@backstage/config'; +import { HumanDuration } from '@backstage/types'; -import { Config } from '@backstage/config'; -import { HumanDuration as HumanDuration } from '@backstage/types'; - -export interface LighthouseAuditScheduleConfig { - schedule: HumanDuration; - timeout: HumanDuration; - auditDetail: HumanDuration; -} - -/** @public */ -export type LighthouseAuditSchedule = { - getSchedule: () => HumanDuration; - getTimeout: () => HumanDuration; +type Options = { + logger: LoggerService; }; /** @public */ -export class LighthouseAuditScheduleImpl implements LighthouseAuditSchedule { - static fromConfig(config: Config) { - const lighthouse = config.getOptionalConfig('lighthouse'); +export class LighthouseAuditScheduleImpl implements TaskScheduleDefinition { + /** + * Looks at the `lighthouse.schedule` section in the application configuration + * and returns a TaskScheduleDefinition. + * Defaults to `{ frequency: { days: 1 }, timeout: {}, initialDelay: { minutes; 15 } }` + * + * @returns a TaskScheduleDefinition + */ + static fromConfig(config: Config, options: Options): TaskScheduleDefinition { + const { logger } = options; - let schedule: HumanDuration = { days: 1 }; - let timeout: HumanDuration = {}; + let lighthouse: TaskScheduleDefinition = { + frequency: { days: 1 }, + timeout: { minutes: 10 }, + initialDelay: { minutes: 15 }, + }; - if (lighthouse) { - const scheduleConfig = lighthouse.getOptionalConfig('schedule'); - const timeoutConfig = lighthouse.getOptionalConfig('timeout'); + if (config.has('lighthouse.schedule.frequency')) { + lighthouse = readTaskScheduleDefinitionFromConfig( + config.getConfig('lighthouse.schedule'), + ); + } else if (config.has('lighthouse.schedule')) { + logger.warn( + `[Deprecation] Please migrate the schedule configuration to 'lighthouse.schedule.frequency'`, + ); - if (scheduleConfig) { - schedule = scheduleConfig as HumanDuration; - } - - if (timeoutConfig) { - timeout = timeoutConfig as HumanDuration; - } + lighthouse.frequency = readDurationFromConfig( + config.getConfig('lighthouse.schedule'), + ); } - return new LighthouseAuditScheduleImpl(schedule, timeout); + if (config.has('lighthouse.timeout')) { + logger.warn( + `[Deprecation] Please migrate the timeout configuration to 'lighthouse.schedule.timeout'`, + ); + + lighthouse.timeout = readDurationFromConfig( + config.getConfig('lighthouse.timeout'), + ); + } + + return lighthouse; } constructor( - private schedule: HumanDuration, - private timeout: HumanDuration, + public frequency: HumanDuration, + public timeout: HumanDuration, + public initialDelay: HumanDuration, ) {} - - getSchedule(): HumanDuration { - return this.schedule; - } - - getTimeout(): HumanDuration { - return this.timeout; - } } diff --git a/plugins/lighthouse-backend/src/service/createScheduler.ts b/plugins/lighthouse-backend/src/service/createScheduler.ts index 498e459a09..45a3e88149 100644 --- a/plugins/lighthouse-backend/src/service/createScheduler.ts +++ b/plugins/lighthouse-backend/src/service/createScheduler.ts @@ -39,7 +39,9 @@ export async function createScheduler( const { logger, scheduler, catalogClient, config, tokenManager } = options; const lighthouseApi = LighthouseRestApi.fromConfig(config); - const lighthouseAuditConfig = LighthouseAuditScheduleImpl.fromConfig(config); + const lighthouseAuditConfig = LighthouseAuditScheduleImpl.fromConfig(config, { + logger, + }); const formFactorToScreenEmulationMap = { // the default is mobile, so no need to override mobile: undefined, @@ -56,16 +58,16 @@ export async function createScheduler( logger.info( `Running with Scheduler Config ${JSON.stringify( - lighthouseAuditConfig.getSchedule(), - )} and timeout ${JSON.stringify(lighthouseAuditConfig.getTimeout())}`, + lighthouseAuditConfig.frequency, + )} and timeout ${JSON.stringify(lighthouseAuditConfig.timeout)}`, ); if (scheduler) { await scheduler.scheduleTask({ id: 'lighthouse_audit', - frequency: lighthouseAuditConfig.getSchedule(), - timeout: lighthouseAuditConfig.getTimeout(), - initialDelay: { minutes: 15 }, + frequency: lighthouseAuditConfig.frequency, + timeout: lighthouseAuditConfig.timeout, + initialDelay: lighthouseAuditConfig.initialDelay, fn: async () => { const filter: Record = { kind: 'Component', diff --git a/plugins/lighthouse/CHANGELOG.md b/plugins/lighthouse/CHANGELOG.md index 0a43306d8b..4ebf99dbd3 100644 --- a/plugins/lighthouse/CHANGELOG.md +++ b/plugins/lighthouse/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-lighthouse +## 0.4.13-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/theme@0.5.0-next.0 + - @backstage/plugin-lighthouse-common@0.1.4 + ## 0.4.13-next.0 ### Patch Changes diff --git a/plugins/lighthouse/README.md b/plugins/lighthouse/README.md index 1d2385e4c4..e834ad39c1 100644 --- a/plugins/lighthouse/README.md +++ b/plugins/lighthouse/README.md @@ -47,6 +47,10 @@ const routes = ( Then configure the `lighthouse-audit-service` URL in your [`app-config.yaml`](https://github.com/backstage/backstage/blob/master/app-config.yaml). ```yaml +backend: + csp: + frame-src: + - http://your-service-url lighthouse: baseUrl: http://your-service-url ``` diff --git a/plugins/lighthouse/package.json b/plugins/lighthouse/package.json index d9e1562293..348ae348ff 100644 --- a/plugins/lighthouse/package.json +++ b/plugins/lighthouse/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-lighthouse", "description": "A Backstage plugin that integrates towards Lighthouse", - "version": "0.4.13-next.0", + "version": "0.4.13-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/linguist-backend/CHANGELOG.md b/plugins/linguist-backend/CHANGELOG.md index b6d4eeecba..ff680b0db5 100644 --- a/plugins/linguist-backend/CHANGELOG.md +++ b/plugins/linguist-backend/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-linguist-backend +## 0.5.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.5.0-next.0 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/backend-tasks@0.5.13-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.2-next.1 + - @backstage/plugin-catalog-node@1.5.1-next.1 + - @backstage/plugin-linguist-common@0.1.2 + ## 0.5.5-next.0 ### Patch Changes diff --git a/plugins/linguist-backend/package.json b/plugins/linguist-backend/package.json index 078e771acb..13f27b1f94 100644 --- a/plugins/linguist-backend/package.json +++ b/plugins/linguist-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-linguist-backend", - "version": "0.5.5-next.0", + "version": "0.5.5-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/linguist/CHANGELOG.md b/plugins/linguist/CHANGELOG.md index 9eff98f884..a8177709b6 100644 --- a/plugins/linguist/CHANGELOG.md +++ b/plugins/linguist/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-linguist +## 0.1.13-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + - @backstage/plugin-linguist-common@0.1.2 + ## 0.1.13-next.0 ### Patch Changes diff --git a/plugins/linguist/package.json b/plugins/linguist/package.json index f5c1fc6d16..630357d283 100644 --- a/plugins/linguist/package.json +++ b/plugins/linguist/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-linguist", - "version": "0.1.13-next.0", + "version": "0.1.13-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/microsoft-calendar/CHANGELOG.md b/plugins/microsoft-calendar/CHANGELOG.md index 5197b46b04..9fa9dcd330 100644 --- a/plugins/microsoft-calendar/CHANGELOG.md +++ b/plugins/microsoft-calendar/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-microsoft-calendar +## 0.1.10-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + ## 0.1.10-next.0 ### Patch Changes diff --git a/plugins/microsoft-calendar/package.json b/plugins/microsoft-calendar/package.json index 2f8e24ba7c..c49892a12f 100644 --- a/plugins/microsoft-calendar/package.json +++ b/plugins/microsoft-calendar/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-microsoft-calendar", - "version": "0.1.10-next.0", + "version": "0.1.10-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/newrelic-dashboard/CHANGELOG.md b/plugins/newrelic-dashboard/CHANGELOG.md index d4fc089300..e0b1a555d8 100644 --- a/plugins/newrelic-dashboard/CHANGELOG.md +++ b/plugins/newrelic-dashboard/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-newrelic-dashboard +## 0.3.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + ## 0.3.3-next.0 ### Patch Changes diff --git a/plugins/newrelic-dashboard/package.json b/plugins/newrelic-dashboard/package.json index 35360c23be..bbf7e0db14 100644 --- a/plugins/newrelic-dashboard/package.json +++ b/plugins/newrelic-dashboard/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-newrelic-dashboard", - "version": "0.3.3-next.0", + "version": "0.3.3-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/newrelic/CHANGELOG.md b/plugins/newrelic/CHANGELOG.md index cb8ad604bc..2eacc6f9de 100644 --- a/plugins/newrelic/CHANGELOG.md +++ b/plugins/newrelic/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-newrelic +## 0.3.43-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/theme@0.5.0-next.0 + ## 0.3.43-next.0 ### Patch Changes diff --git a/plugins/newrelic/package.json b/plugins/newrelic/package.json index 3f4ae91bbe..328acc6dc6 100644 --- a/plugins/newrelic/package.json +++ b/plugins/newrelic/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-newrelic", "description": "A Backstage plugin that integrates towards New Relic", - "version": "0.3.43-next.0", + "version": "0.3.43-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/nomad-backend/CHANGELOG.md b/plugins/nomad-backend/CHANGELOG.md index edcee04e86..c9ce70c9bd 100644 --- a/plugins/nomad-backend/CHANGELOG.md +++ b/plugins/nomad-backend/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-nomad-backend +## 0.1.10-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + ## 0.1.10-next.0 ### Patch Changes diff --git a/plugins/nomad-backend/package.json b/plugins/nomad-backend/package.json index 074e35f1db..9b3236a36c 100644 --- a/plugins/nomad-backend/package.json +++ b/plugins/nomad-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-nomad-backend", - "version": "0.1.10-next.0", + "version": "0.1.10-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/nomad/CHANGELOG.md b/plugins/nomad/CHANGELOG.md index 6efac21380..db3028d3be 100644 --- a/plugins/nomad/CHANGELOG.md +++ b/plugins/nomad/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-nomad +## 0.1.9-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/theme@0.5.0-next.0 + ## 0.1.9-next.0 ### Patch Changes diff --git a/plugins/nomad/package.json b/plugins/nomad/package.json index fc8cab5de7..8f6c1bdaa0 100644 --- a/plugins/nomad/package.json +++ b/plugins/nomad/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-nomad", - "version": "0.1.9-next.0", + "version": "0.1.9-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/octopus-deploy/CHANGELOG.md b/plugins/octopus-deploy/CHANGELOG.md index 4855e1964e..5be0493e2e 100644 --- a/plugins/octopus-deploy/CHANGELOG.md +++ b/plugins/octopus-deploy/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-octopus-deploy +## 0.2.10-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/theme@0.5.0-next.0 + ## 0.2.10-next.0 ### Patch Changes diff --git a/plugins/octopus-deploy/package.json b/plugins/octopus-deploy/package.json index 2e28dd8453..29c3bf0e77 100644 --- a/plugins/octopus-deploy/package.json +++ b/plugins/octopus-deploy/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-octopus-deploy", - "version": "0.2.10-next.0", + "version": "0.2.10-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/opencost/CHANGELOG.md b/plugins/opencost/CHANGELOG.md index bc4fcbd562..0bd056f1db 100644 --- a/plugins/opencost/CHANGELOG.md +++ b/plugins/opencost/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-opencost +## 0.2.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/theme@0.5.0-next.0 + ## 0.2.3-next.0 ### Patch Changes diff --git a/plugins/opencost/package.json b/plugins/opencost/package.json index fba53eb9da..7c868ebeb1 100644 --- a/plugins/opencost/package.json +++ b/plugins/opencost/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-opencost", - "version": "0.2.3-next.0", + "version": "0.2.3-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/org-react/CHANGELOG.md b/plugins/org-react/CHANGELOG.md index 80ca996f05..285d048652 100644 --- a/plugins/org-react/CHANGELOG.md +++ b/plugins/org-react/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-org-react +## 0.1.17-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-client@1.5.0-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/theme@0.5.0-next.0 + ## 0.1.17-next.0 ### Patch Changes diff --git a/plugins/org-react/package.json b/plugins/org-react/package.json index 2893e283ee..af1daff510 100644 --- a/plugins/org-react/package.json +++ b/plugins/org-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-org-react", - "version": "0.1.17-next.0", + "version": "0.1.17-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/org/CHANGELOG.md b/plugins/org/CHANGELOG.md index 9157075dd9..e5099316fa 100644 --- a/plugins/org/CHANGELOG.md +++ b/plugins/org/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-org +## 0.6.18-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/theme@0.5.0-next.0 + ## 0.6.18-next.0 ### Patch Changes diff --git a/plugins/org/package.json b/plugins/org/package.json index 15c64bd7b8..2f087eedd5 100644 --- a/plugins/org/package.json +++ b/plugins/org/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-org", "description": "A Backstage plugin that helps you create entity pages for your organization", - "version": "0.6.18-next.0", + "version": "0.6.18-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/pagerduty/CHANGELOG.md b/plugins/pagerduty/CHANGELOG.md index 40c6ff4e02..e62d0366f1 100644 --- a/plugins/pagerduty/CHANGELOG.md +++ b/plugins/pagerduty/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-pagerduty +## 0.7.0-next.1 + +### Minor Changes + +- 5fca16fdf6: This package has been deprecated, consider using [@pagerduty/backstage-plugin](https://github.com/pagerduty/backstage-plugin) instead. + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/plugin-home-react@0.1.6-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + ## 0.6.9-next.0 ### Patch Changes diff --git a/plugins/pagerduty/README.md b/plugins/pagerduty/README.md index bf748439c9..c1079004e3 100644 --- a/plugins/pagerduty/README.md +++ b/plugins/pagerduty/README.md @@ -1,217 +1,3 @@ # PagerDuty + Backstage Integration Benefits -- Display relevant PagerDuty information about an entity within Backstage, such as the escalation policy, if there are any active incidents, and recent changes -- Trigger an incident to the currently on-call responder(s) for a service - -## How it Works - -- The Backstage PagerDuty plugin allows PagerDuty information about a Backstage entity to be displayed within Backstage. This includes active incidents, recent change events, as well as the current on-call responders' names, email addresses, and links to their profiles in PagerDuty. -- Incidents can be manually triggered via the plugin with a user-provided description, which will in turn notify the current on-call responders (Alternatively, the plugin can be configured with an optional `readOnly` property to suppress the ability to trigger incidents from the plugin). - - _Note: This feature is only available when providing the `pagerduty.com/integration-key` annotation_ -- Change events will be displayed in a separate tab. If the change event payload has additional links the first link only will be rendered. - -## Requirements - -- Setup of the PagerDuty plugin for Backstage requires a PagerDuty Admin role in order to generate the necessary authorizations, such as the API token. If you do not have this role, please reach out to an Admin or Account Owner within your organization to request configuration of this plugin. - -## Feature Overview - -### View any open incidents - -![PagerDuty plugin showing no incidents and the on-call rotation](doc/pd1.png) - -### Email link, and view contact information for staff on call - -![PagerDuty plugin showing on-call rotation contact information](doc/pd2.png) - -### Trigger an incident for a service - -![PagerDuty plugin popup modal for creating an incident](doc/pd3.png) - -![PagerDuty plugin showing an active incident](doc/pd4.png) - -## Support - -If you need help with this plugin, please reach out on the [Backstage Discord server](https://discord.gg/backstage-687207715902193673). - -## Integration Walk-through - -### In PagerDuty - -#### Integrating With a PagerDuty Service - -1. From the **Configuration** menu, select **Services**. -2. There are two ways to add an integration to a service: - - **If you are adding your integration to an existing service**: Click the **name** of the service you want to add the integration to. Then, select the **Integrations** tab and click the **New Integration** button. - - **If you are creating a new service for your integration**: Please read the documentation in section [Configuring Services and Integrations](https://support.pagerduty.com/docs/services-and-integrations#section-configuring-services-and-integrations) and follow the steps outlined in the [Create a New Service](https://support.pagerduty.com/docs/services-and-integrations#section-create-a-new-service) section, selecting **Backstage** as the **Integration Type** in step 4. Continue with the **In Backstage** section (below) once you have finished these steps. -3. Enter an **Integration Name** in the format `monitoring-tool-service-name` (e.g. `Backstage-Shopping-Cart`) and select **Backstage** from the Integration Type menu. -4. Click the **Add Integration** button to save your new integration. You will be redirected to the Integrations tab for your service. -5. An **Integration Key** will be generated on this screen. Keep this key saved in a safe place, as it will be used when you configure the integration with **Backstage** in the next section. - ![](https://pdpartner.s3.amazonaws.com/ig-template-copy-integration-key.png) - -### In Backstage - -#### Install the plugin - -The file paths mentioned in the following steps are relative to your app's root directory — for example, the directory created by following the [Getting Started](https://backstage.io/docs/getting-started/) guide and creating your app with `npx @backstage/create-app`. - -First, install the PagerDuty plugin via a CLI: - -```bash -# From your Backstage root directory -yarn add --cwd packages/app @backstage/plugin-pagerduty -``` - -Next, add the plugin to `EntityPage.tsx` in `packages/app/src/components/catalog` by adding the following code snippets. - -Add the following imports to the top of the file: - -```ts -import { - isPluginApplicableToEntity as isPagerDutyAvailable, - EntityPagerDutyCard, -} from '@backstage/plugin-pagerduty'; -``` - -Find `const overviewContent` in `EntityPage.tsx`, and add the following snippet inside the outermost `Grid` defined there, just before the closing `` tag: - -```ts - - - - - - - -``` - -When you're done, the `overviewContent` definition should look something like this: - -```ts -const overviewContent = ( - - ... - - - - - - - - -); -``` - -#### Configure the plugin - -First, [annotate](https://backstage.io/docs/features/software-catalog/descriptor-format#annotations-optional) the appropriate entity with the PagerDuty integration key in its `.yaml` configuration file: - -```yaml -annotations: - pagerduty.com/integration-key: [INTEGRATION_KEY] -``` - -#### The homepage component - -You may also add the component to the homepage of Backstage. Edit the `HomePage.tsx` file, import `PagerDutyHomepageCard` and add it to the home page. e.g. - -```typescript jsx -... -export const homePage = ( - - ... - - - ... - - - - -); -``` - -Next, provide the [API token](https://support.pagerduty.com/docs/generating-api-keys#generating-a-general-access-rest-api-key) that the client will use to make requests to the [PagerDuty API](https://developer.pagerduty.com/docs/rest-api-v2/rest-api/). - -Add the proxy configuration in `app-config.yaml`: - -```yaml -proxy: - ... - '/pagerduty': - target: https://api.pagerduty.com - headers: - Authorization: Token token=${PAGERDUTY_TOKEN} -``` - -Then, start the backend, passing the PagerDuty API token as an environment variable: - -```bash -$ PAGERDUTY_TOKEN='' yarn start -``` - -This will proxy the request by adding an `Authorization` header with the provided token. - -#### Optional configuration - -##### Annotating with Service ID - -If you want to integrate a PagerDuty service with Backstage but don't want to use an integration key, you can also [annotate](https://backstage.io/docs/features/software-catalog/descriptor-format#annotations-optional) the appropriate entity with a PagerDuty Service ID instead - -```yaml -annotations: - pagerduty.com/service-id: [SERVICE_ID] -``` - -This service ID can be found by navigating to a Service within PagerDuty and pulling the ID value out of the URL. - -1. From the **Configuration** menu within PagerDuty, select **Services**. -2. Click the **name** of the service you want to represent for your Entity. - -Your browser URL should now be located at `https://pagerduty.com/service-directory/[SERVICE_ID]`. - -_Note: When annotating with `pagerduty.com/service-id`, the feature to Create Incidents is not currently supported_ - -##### Custom Events URL - -If you want to override the default URL used for events, you can add it to `app-config.yaml`: - -```yaml -pagerDuty: - eventsBaseUrl: 'https://events.pagerduty.com/v2' -``` - -To suppress the rendering of the actionable incident-creation button, the `PagerDutyCard` can also be instantiated in `readOnly` mode: - -```ts - -``` - -**WARNING**: In current implementation, the PagerDuty plugin requires the `/pagerduty` proxy endpoint be exposed by the Backstage backend as an unprotected endpoint, in effect enabling PagerDuty API access using the configured `PAGERDUTY_TOKEN` for any user or process with access to the `/pagerduty` Backstage backend endpoint. If you regard this as problematic, consider using the plugin in `readOnly` mode (``) using the following proxy configuration: - -```yaml -proxy: - '/pagerduty': - target: https://api.pagerduty.com - headers: - Authorization: Token token=${PAGERDUTY_TOKEN} - # prohibit the `/pagerduty` proxy endpoint from servicing non-GET requests - allowedMethods: ['GET'] -``` - -## How to Uninstall - -1. Remove any configuration added in Backstage yaml files, such as the proxy configuration in `app-config.yaml` and the integration key in an entity's annotations. -2. Remove the added code snippets from `EntityPage.tsx` -3. Remove the plugin package: - -```bash -# From your Backstage root directory -yarn remove --cwd packages/app @backstage/plugin-pagerduty -``` - -4. [Delete the integration](https://support.pagerduty.com/docs/services-and-integrations#delete-an-integration-from-a-service) from the service in PagerDuty +This package has been deprecated, consider using [@pagerduty/backstage-plugin](https://github.com/pagerduty/backstage-plugin) instead. diff --git a/plugins/pagerduty/api-report.md b/plugins/pagerduty/api-report.md index db73259dd5..a96cab1af1 100644 --- a/plugins/pagerduty/api-report.md +++ b/plugins/pagerduty/api-report.md @@ -16,7 +16,7 @@ import { JSX as JSX_2 } from 'react'; import { default as React_2 } from 'react'; import { ReactNode } from 'react'; -// @public (undocumented) +// @public @deprecated (undocumented) export const EntityPagerDutyCard: ( props: EntityPagerDutyCardProps, ) => JSX_2.Element; @@ -27,7 +27,7 @@ export type EntityPagerDutyCardProps = { disableChangeEvents?: boolean; }; -// @public (undocumented) +// @public @deprecated (undocumented) export const HomePagePagerDutyCard: ( props: CardExtensionProps, ) => JSX_2.Element; @@ -138,7 +138,7 @@ export type PagerDutyClientApiDependencies = { fetchApi: FetchApi; }; -// @public (undocumented) +// @public @deprecated (undocumented) export type PagerDutyEntity = { integrationKey?: string; serviceId?: string; @@ -176,7 +176,7 @@ export type PagerDutyOnCallsResponse = { oncalls: PagerDutyOnCall[]; }; -// @public (undocumented) +// @public @deprecated (undocumented) const pagerDutyPlugin: BackstagePlugin<{}, {}>; export { pagerDutyPlugin }; export { pagerDutyPlugin as plugin }; diff --git a/plugins/pagerduty/package.json b/plugins/pagerduty/package.json index 726ff91a63..0e6a0667dc 100644 --- a/plugins/pagerduty/package.json +++ b/plugins/pagerduty/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-pagerduty", - "description": "A Backstage plugin that integrates towards PagerDuty", - "version": "0.6.9-next.0", + "description": "This plugin has been deprecated, consider using [@pagerduty/backstage-plugin](https://github.com/pagerduty/backstage-plugin) instead.", + "version": "0.7.0-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/pagerduty/src/plugin.ts b/plugins/pagerduty/src/plugin.ts index 59cc5cd8cc..4144eb5878 100644 --- a/plugins/pagerduty/src/plugin.ts +++ b/plugins/pagerduty/src/plugin.ts @@ -30,7 +30,10 @@ export const rootRouteRef = createRouteRef({ id: 'pagerduty', }); -/** @public */ +/** + * @public + * @deprecated This plugin will be removed in a future release. Please use \@pagerduty/backstage-plugin plugin instead (https://www.npmjs.com/package/\@pagerduty/backstage-plugin). + */ export const pagerDutyPlugin = createPlugin({ id: 'pagerduty', apis: [ @@ -47,7 +50,10 @@ export const pagerDutyPlugin = createPlugin({ ], }); -/** @public */ +/** + * @public + * @deprecated This plugin and it's cards will be removed in a future release. Please use \@pagerduty/backstage-plugin plugin instead (https://www.npmjs.com/package/\@pagerduty/backstage-plugin). + */ export const EntityPagerDutyCard = pagerDutyPlugin.provide( createComponentExtension({ name: 'EntityPagerDutyCard', @@ -60,7 +66,10 @@ export const EntityPagerDutyCard = pagerDutyPlugin.provide( }), ); -/** @public */ +/** + * @public + * @deprecated This plugin and it's cards will be removed in a future release. Please use \@pagerduty/backstage-plugin plugin instead (https://www.npmjs.com/package/\@pagerduty/backstage-plugin). + */ export const HomePagePagerDutyCard = pagerDutyPlugin.provide( createCardExtension({ name: 'HomePagePagerDutyCard', diff --git a/plugins/pagerduty/src/types.ts b/plugins/pagerduty/src/types.ts index 9f38b429ef..e026a6ffbe 100644 --- a/plugins/pagerduty/src/types.ts +++ b/plugins/pagerduty/src/types.ts @@ -14,7 +14,10 @@ * limitations under the License. */ -/** @public */ +/** + * @public + * @deprecated This plugin and it's types will be removed in a future release. Please use \@pagerduty/backstage-plugin plugin instead (https://www.npmjs.com/package/\@pagerduty/backstage-plugin). + */ export type PagerDutyEntity = { integrationKey?: string; serviceId?: string; diff --git a/plugins/periskop-backend/CHANGELOG.md b/plugins/periskop-backend/CHANGELOG.md index 5884a31457..1b98851c12 100644 --- a/plugins/periskop-backend/CHANGELOG.md +++ b/plugins/periskop-backend/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-periskop-backend +## 0.2.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/config@1.1.1 + ## 0.2.6-next.0 ### Patch Changes diff --git a/plugins/periskop-backend/alpha-api-report.md b/plugins/periskop-backend/api-report-alpha.md similarity index 100% rename from plugins/periskop-backend/alpha-api-report.md rename to plugins/periskop-backend/api-report-alpha.md diff --git a/plugins/periskop-backend/package.json b/plugins/periskop-backend/package.json index e1de6e6751..481533c1ed 100644 --- a/plugins/periskop-backend/package.json +++ b/plugins/periskop-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-periskop-backend", - "version": "0.2.6-next.0", + "version": "0.2.6-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/periskop/CHANGELOG.md b/plugins/periskop/CHANGELOG.md index 139ab1c5f7..17573f96b5 100644 --- a/plugins/periskop/CHANGELOG.md +++ b/plugins/periskop/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-periskop +## 0.1.26-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + ## 0.1.26-next.0 ### Patch Changes diff --git a/plugins/periskop/package.json b/plugins/periskop/package.json index 9099c884a7..380f7d5fd7 100644 --- a/plugins/periskop/package.json +++ b/plugins/periskop/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-periskop", - "version": "0.1.26-next.0", + "version": "0.1.26-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/permission-backend-module-policy-allow-all/CHANGELOG.md b/plugins/permission-backend-module-policy-allow-all/CHANGELOG.md index 59488140e0..8b46dfe282 100644 --- a/plugins/permission-backend-module-policy-allow-all/CHANGELOG.md +++ b/plugins/permission-backend-module-policy-allow-all/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-permission-backend-module-allow-all-policy +## 0.1.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/plugin-auth-node@0.4.2-next.1 + - @backstage/plugin-permission-common@0.7.10 + - @backstage/plugin-permission-node@0.7.19-next.1 + ## 0.1.5-next.0 ### Patch Changes diff --git a/plugins/permission-backend-module-policy-allow-all/package.json b/plugins/permission-backend-module-policy-allow-all/package.json index 259186d50b..b65ff60cfb 100644 --- a/plugins/permission-backend-module-policy-allow-all/package.json +++ b/plugins/permission-backend-module-policy-allow-all/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-permission-backend-module-allow-all-policy", "description": "Allow all policy backend module for the permission plugin.", - "version": "0.1.5-next.0", + "version": "0.1.5-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/permission-backend-module-policy-allow-all/src/module.ts b/plugins/permission-backend-module-policy-allow-all/src/module.ts index 47ab202a00..f7a38f293c 100644 --- a/plugins/permission-backend-module-policy-allow-all/src/module.ts +++ b/plugins/permission-backend-module-policy-allow-all/src/module.ts @@ -23,8 +23,8 @@ import { AllowAllPermissionPolicy } from './policy'; * @public */ export const permissionModuleAllowAllPolicy = createBackendModule({ - moduleId: 'allowAllPolicy', pluginId: 'permission', + moduleId: 'allow-all-policy', register(reg) { reg.registerInit({ deps: { policy: policyExtensionPoint }, diff --git a/plugins/permission-backend/CHANGELOG.md b/plugins/permission-backend/CHANGELOG.md index d40c6eff77..85c74ef3cb 100644 --- a/plugins/permission-backend/CHANGELOG.md +++ b/plugins/permission-backend/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-permission-backend +## 0.5.31-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-auth-node@0.4.2-next.1 + - @backstage/plugin-permission-common@0.7.10 + - @backstage/plugin-permission-node@0.7.19-next.1 + ## 0.5.31-next.0 ### Patch Changes diff --git a/plugins/permission-backend/alpha-api-report.md b/plugins/permission-backend/api-report-alpha.md similarity index 100% rename from plugins/permission-backend/alpha-api-report.md rename to plugins/permission-backend/api-report-alpha.md diff --git a/plugins/permission-backend/package.json b/plugins/permission-backend/package.json index c3e8c7a54c..3578b55a81 100644 --- a/plugins/permission-backend/package.json +++ b/plugins/permission-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-permission-backend", - "version": "0.5.31-next.0", + "version": "0.5.31-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/permission-node/CHANGELOG.md b/plugins/permission-node/CHANGELOG.md index a9c27025a9..757264e0fd 100644 --- a/plugins/permission-node/CHANGELOG.md +++ b/plugins/permission-node/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-permission-node +## 0.7.19-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-auth-node@0.4.2-next.1 + - @backstage/plugin-permission-common@0.7.10 + ## 0.7.19-next.0 ### Patch Changes diff --git a/plugins/permission-node/alpha-api-report.md b/plugins/permission-node/api-report-alpha.md similarity index 100% rename from plugins/permission-node/alpha-api-report.md rename to plugins/permission-node/api-report-alpha.md diff --git a/plugins/permission-node/package.json b/plugins/permission-node/package.json index 4191d16a67..9e5197f312 100644 --- a/plugins/permission-node/package.json +++ b/plugins/permission-node/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-permission-node", "description": "Common permission and authorization utilities for backend plugins", - "version": "0.7.19-next.0", + "version": "0.7.19-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/permission-react/CHANGELOG.md b/plugins/permission-react/CHANGELOG.md index c5edb4e8f4..3fedc6e80a 100644 --- a/plugins/permission-react/CHANGELOG.md +++ b/plugins/permission-react/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-permission-react +## 0.4.18-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/config@1.1.1 + - @backstage/plugin-permission-common@0.7.10 + ## 0.4.18-next.0 ### Patch Changes diff --git a/plugins/permission-react/package.json b/plugins/permission-react/package.json index 26f2cde701..433ec8bcb4 100644 --- a/plugins/permission-react/package.json +++ b/plugins/permission-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-permission-react", - "version": "0.4.18-next.0", + "version": "0.4.18-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/playlist-backend/CHANGELOG.md b/plugins/playlist-backend/CHANGELOG.md index 29b12ea597..3961855299 100644 --- a/plugins/playlist-backend/CHANGELOG.md +++ b/plugins/playlist-backend/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-playlist-backend +## 0.3.12-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.5.0-next.0 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-auth-node@0.4.2-next.1 + - @backstage/plugin-permission-common@0.7.10 + - @backstage/plugin-permission-node@0.7.19-next.1 + - @backstage/plugin-playlist-common@0.1.12 + ## 0.3.12-next.0 ### Patch Changes diff --git a/plugins/playlist-backend/package.json b/plugins/playlist-backend/package.json index 28fc5e12da..cc24e9965e 100644 --- a/plugins/playlist-backend/package.json +++ b/plugins/playlist-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-playlist-backend", - "version": "0.3.12-next.0", + "version": "0.3.12-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/playlist/CHANGELOG.md b/plugins/playlist/CHANGELOG.md index 12d289d0bf..477608ee9d 100644 --- a/plugins/playlist/CHANGELOG.md +++ b/plugins/playlist/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-playlist +## 0.2.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/plugin-search-react@1.7.4-next.1 + - @backstage/plugin-permission-react@0.4.18-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + - @backstage/plugin-catalog-common@1.0.18 + - @backstage/plugin-permission-common@0.7.10 + - @backstage/plugin-playlist-common@0.1.12 + ## 0.2.2-next.0 ### Patch Changes diff --git a/plugins/playlist/package.json b/plugins/playlist/package.json index c23be32837..495ab0db5f 100644 --- a/plugins/playlist/package.json +++ b/plugins/playlist/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-playlist", - "version": "0.2.2-next.0", + "version": "0.2.2-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/proxy-backend/CHANGELOG.md b/plugins/proxy-backend/CHANGELOG.md index 49518973c8..c5415d315c 100644 --- a/plugins/proxy-backend/CHANGELOG.md +++ b/plugins/proxy-backend/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-proxy-backend +## 0.4.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/config@1.1.1 + ## 0.4.6-next.0 ### Patch Changes diff --git a/plugins/proxy-backend/alpha-api-report.md b/plugins/proxy-backend/api-report-alpha.md similarity index 100% rename from plugins/proxy-backend/alpha-api-report.md rename to plugins/proxy-backend/api-report-alpha.md diff --git a/plugins/proxy-backend/package.json b/plugins/proxy-backend/package.json index a3bc61540f..48da413397 100644 --- a/plugins/proxy-backend/package.json +++ b/plugins/proxy-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-proxy-backend", "description": "A Backstage backend plugin that helps you set up proxy endpoints in the backend", - "version": "0.4.6-next.0", + "version": "0.4.6-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/puppetdb/CHANGELOG.md b/plugins/puppetdb/CHANGELOG.md index 0147ed7ac7..c3f622c3bd 100644 --- a/plugins/puppetdb/CHANGELOG.md +++ b/plugins/puppetdb/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-puppetdb +## 0.1.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + ## 0.1.11-next.0 ### Patch Changes diff --git a/plugins/puppetdb/package.json b/plugins/puppetdb/package.json index fbddd1a2e4..639c63c1f7 100644 --- a/plugins/puppetdb/package.json +++ b/plugins/puppetdb/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-puppetdb", "description": "Backstage plugin to visualize resource information and Puppet facts from PuppetDB.", - "version": "0.1.11-next.0", + "version": "0.1.11-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/rollbar-backend/CHANGELOG.md b/plugins/rollbar-backend/CHANGELOG.md index 26b858f2ae..548bd5cc2d 100644 --- a/plugins/rollbar-backend/CHANGELOG.md +++ b/plugins/rollbar-backend/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-rollbar-backend +## 0.1.53-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/config@1.1.1 + ## 0.1.53-next.0 ### Patch Changes diff --git a/plugins/rollbar-backend/package.json b/plugins/rollbar-backend/package.json index 6512f32d88..8f352c4ccf 100644 --- a/plugins/rollbar-backend/package.json +++ b/plugins/rollbar-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-rollbar-backend", "description": "A Backstage backend plugin that integrates towards Rollbar", - "version": "0.1.53-next.0", + "version": "0.1.53-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/rollbar/CHANGELOG.md b/plugins/rollbar/CHANGELOG.md index 8d8f35757a..96e3781700 100644 --- a/plugins/rollbar/CHANGELOG.md +++ b/plugins/rollbar/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-rollbar +## 0.4.28-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/theme@0.5.0-next.0 + ## 0.4.28-next.0 ### Patch Changes diff --git a/plugins/rollbar/package.json b/plugins/rollbar/package.json index 5c51bdab2a..3e9b05f360 100644 --- a/plugins/rollbar/package.json +++ b/plugins/rollbar/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-rollbar", "description": "A Backstage plugin that integrates towards Rollbar", - "version": "0.4.28-next.0", + "version": "0.4.28-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/scaffolder-backend-module-confluence-to-markdown/CHANGELOG.md b/plugins/scaffolder-backend-module-confluence-to-markdown/CHANGELOG.md index 96500ac2a5..838edd9202 100644 --- a/plugins/scaffolder-backend-module-confluence-to-markdown/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-confluence-to-markdown/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-scaffolder-backend-module-confluence-to-markdown +## 0.2.9-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.8.0-next.1 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-scaffolder-node@0.2.9-next.1 + ## 0.2.9-next.0 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-confluence-to-markdown/package.json b/plugins/scaffolder-backend-module-confluence-to-markdown/package.json index 0f441ab713..7ffbb59464 100644 --- a/plugins/scaffolder-backend-module-confluence-to-markdown/package.json +++ b/plugins/scaffolder-backend-module-confluence-to-markdown/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-scaffolder-backend-module-confluence-to-markdown", "description": "The confluence-to-markdown module for @backstage/plugin-scaffolder-backend", - "version": "0.2.9-next.0", + "version": "0.2.9-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md b/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md index d7cf6eef65..29bace1808 100644 --- a/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-scaffolder-backend-module-cookiecutter +## 0.2.32-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.8.0-next.1 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-scaffolder-node@0.2.9-next.1 + ## 0.2.32-next.0 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-cookiecutter/package.json b/plugins/scaffolder-backend-module-cookiecutter/package.json index a9d7675198..25b8864b9b 100644 --- a/plugins/scaffolder-backend-module-cookiecutter/package.json +++ b/plugins/scaffolder-backend-module-cookiecutter/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-scaffolder-backend-module-cookiecutter", "description": "A module for the scaffolder backend that lets you template projects using cookiecutter", - "version": "0.2.32-next.0", + "version": "0.2.32-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/scaffolder-backend-module-gitlab/CHANGELOG.md b/plugins/scaffolder-backend-module-gitlab/CHANGELOG.md index 7b9fd3b55d..a9adb44355 100644 --- a/plugins/scaffolder-backend-module-gitlab/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-gitlab/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-scaffolder-backend-module-gitlab +## 0.2.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.8.0-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-scaffolder-node@0.2.9-next.1 + ## 0.2.11-next.0 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-gitlab/package.json b/plugins/scaffolder-backend-module-gitlab/package.json index eccb663b54..94641be81c 100644 --- a/plugins/scaffolder-backend-module-gitlab/package.json +++ b/plugins/scaffolder-backend-module-gitlab/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-gitlab", - "version": "0.2.11-next.0", + "version": "0.2.11-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/scaffolder-backend-module-rails/CHANGELOG.md b/plugins/scaffolder-backend-module-rails/CHANGELOG.md index ebf034b594..20538718eb 100644 --- a/plugins/scaffolder-backend-module-rails/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-rails/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-scaffolder-backend-module-rails +## 0.4.25-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.8.0-next.1 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-scaffolder-node@0.2.9-next.1 + ## 0.4.25-next.0 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-rails/package.json b/plugins/scaffolder-backend-module-rails/package.json index abd4742845..37f26da084 100644 --- a/plugins/scaffolder-backend-module-rails/package.json +++ b/plugins/scaffolder-backend-module-rails/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-scaffolder-backend-module-rails", "description": "A module for the scaffolder backend that lets you template projects using Rails", - "version": "0.4.25-next.0", + "version": "0.4.25-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/scaffolder-backend-module-sentry/CHANGELOG.md b/plugins/scaffolder-backend-module-sentry/CHANGELOG.md index 2ba0251f7e..70b9b57a6b 100644 --- a/plugins/scaffolder-backend-module-sentry/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-sentry/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-scaffolder-backend-module-sentry +## 0.1.16-next.1 + +### Patch Changes + +- 7f8a801e6d: Added examples for `sentry:project:create` scaffolder action and unit tests. +- Updated dependencies + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-scaffolder-node@0.2.9-next.1 + ## 0.1.16-next.0 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-sentry/package.json b/plugins/scaffolder-backend-module-sentry/package.json index 58b39a6b5c..c81996bfd6 100644 --- a/plugins/scaffolder-backend-module-sentry/package.json +++ b/plugins/scaffolder-backend-module-sentry/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-sentry", - "version": "0.1.16-next.0", + "version": "0.1.16-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -30,10 +30,14 @@ "dependencies": { "@backstage/config": "workspace:^", "@backstage/errors": "workspace:^", - "@backstage/plugin-scaffolder-node": "workspace:^" + "@backstage/plugin-scaffolder-node": "workspace:^", + "yaml": "^2.3.3" }, "devDependencies": { - "@backstage/cli": "workspace:^" + "@backstage/backend-test-utils": "workspace:^", + "@backstage/cli": "workspace:^", + "@backstage/types": "workspace:^", + "msw": "^2.0.0" }, "files": [ "dist" diff --git a/plugins/scaffolder-backend-module-sentry/src/actions/createProject.examples.ts b/plugins/scaffolder-backend-module-sentry/src/actions/createProject.examples.ts new file mode 100644 index 0000000000..9d4551c1b5 --- /dev/null +++ b/plugins/scaffolder-backend-module-sentry/src/actions/createProject.examples.ts @@ -0,0 +1,53 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { TemplateExample } from '@backstage/plugin-scaffolder-node'; +import yaml from 'yaml'; + +export const examples: TemplateExample[] = [ + { + description: 'Creates a Sentry project with the specified parameters.', + example: yaml.stringify({ + steps: [ + { + id: 'create-sentry-project', + action: 'sentry:project:create', + name: 'Create a Sentry project with provided project slug.', + input: { + organizationSlug: 'my-org', + teamSlug: 'team-a', + name: 'Scaffolded project A', + slug: 'scaff-proj-a', + authToken: + 'a14711beb516e1e910d2ede554dc1bf725654ef3c75e5a9106de9aec13d5df96', + }, + }, + { + id: 'create-sentry-project', + action: 'sentry:project:create', + name: 'Create a Sentry project without providing a project slug.', + input: { + organizationSlug: 'my-org', + teamSlug: 'team-b', + name: 'Scaffolded project B', + authToken: + 'b15711beb516e1e910d2ede554dc1bf725654ef3c75e5a9106de9aec13d4gf93', + }, + }, + ], + }), + }, +]; diff --git a/plugins/scaffolder-backend-module-sentry/src/actions/createProject.test.ts b/plugins/scaffolder-backend-module-sentry/src/actions/createProject.test.ts new file mode 100644 index 0000000000..972b0f1e8e --- /dev/null +++ b/plugins/scaffolder-backend-module-sentry/src/actions/createProject.test.ts @@ -0,0 +1,217 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { setupRequestMockHandlers } from '@backstage/backend-test-utils'; +import { ConfigReader } from '@backstage/config'; +import { InputError } from '@backstage/errors'; +import { ActionContext } from '@backstage/plugin-scaffolder-node'; +import { JsonObject } from '@backstage/types'; +import { randomBytes } from 'crypto'; +import { setupServer } from 'msw/node'; +import { HttpResponse, http } from 'msw'; +import { createSentryCreateProjectAction } from './createProject'; + +describe('sentry:project:create action', () => { + const worker = setupServer(); + setupRequestMockHandlers(worker); + + const createScaffolderConfig = (configData: JsonObject = {}) => ({ + config: new ConfigReader({ + scaffolder: { + ...configData, + }, + }), + }); + + const getActionContext = (): ActionContext<{ + organizationSlug: string; + teamSlug: string; + name: string; + slug?: string; + authToken?: string; + }> => ({ + workspacePath: './dev/proj', + createTemporaryDirectory: jest.fn(), + logger: jest.createMockFromModule('winston'), + logStream: jest.createMockFromModule('stream'), + input: { + organizationSlug: 'org', + teamSlug: 'team', + name: 'test project', + authToken: randomBytes(5).toString('hex'), + }, + output: jest.fn(), + }); + + it('should request sentry project create with specified parameters.', async () => { + expect.assertions(3); + + const action = createSentryCreateProjectAction(createScaffolderConfig()); + const actionContext = getActionContext(); + + worker.use( + http.post( + `https://sentry.io/api/0/teams/${actionContext.input.organizationSlug}/${actionContext.input.teamSlug}/projects/`, + async ({ request }) => { + expect(request.headers.get('Authorization')).toBe( + `Bearer ${actionContext.input.authToken}`, + ); + expect(request.headers.get('Content-Type')).toBe(`application/json`); + await expect(request.json()).resolves.toEqual({ + name: actionContext.input.name, + }); + return HttpResponse.json( + { detail: 'project creation mocked result' }, + { status: 201 }, + ); + }, + ), + ); + + await action.handler(actionContext); + }); + + it('should request sentry project create with added optional specified project slug', async () => { + expect.assertions(3); + + const action = createSentryCreateProjectAction(createScaffolderConfig()); + const actionContext = getActionContext(); + actionContext.input = { ...actionContext.input, slug: 'project-slug' }; + + worker.use( + http.post( + `https://sentry.io/api/0/teams/${actionContext.input.organizationSlug}/${actionContext.input.teamSlug}/projects/`, + async ({ request }) => { + expect(request.headers.get('Authorization')).toBe( + `Bearer ${actionContext.input.authToken}`, + ); + expect(request.headers.get('Content-Type')).toBe(`application/json`); + await expect(request.json()).resolves.toEqual({ + name: actionContext.input.name, + slug: actionContext.input.slug, + }); + return HttpResponse.json( + { detail: 'project creation mocked result' }, + { status: 201 }, + ); + }, + ), + ); + + await action.handler(actionContext); + }); + + it('should take Sentry auth token from scaffolder config when input authToken is missing.', async () => { + expect.assertions(3); + + const sentryScaffolderConfigToken = randomBytes(5).toString('hex'); + const action = createSentryCreateProjectAction( + createScaffolderConfig({ + sentry: { + token: sentryScaffolderConfigToken, + }, + }), + ); + const actionContext = getActionContext(); + actionContext.input.authToken = undefined; + + worker.use( + http.post( + `https://sentry.io/api/0/teams/${actionContext.input.organizationSlug}/${actionContext.input.teamSlug}/projects/`, + async ({ request }) => { + expect(request.headers.get('Authorization')).toBe( + `Bearer ${sentryScaffolderConfigToken}`, + ); + expect(request.headers.get('Content-Type')).toBe(`application/json`); + await expect(request.json()).resolves.toEqual({ + name: actionContext.input.name, + }); + return HttpResponse.json( + { detail: 'project creation mocked result' }, + { status: 201 }, + ); + }, + ), + ); + + await action.handler(actionContext); + }); + + it('should throw InputError when auth token is missing from input parameters and scaffolder config.', async () => { + const action = createSentryCreateProjectAction(createScaffolderConfig()); + const actionContext = getActionContext(); + actionContext.input.authToken = undefined; + + worker.use( + http.post( + `https://sentry.io/api/0/teams/${actionContext.input.organizationSlug}/${actionContext.input.teamSlug}/projects/`, + async ({ request }) => { + expect(request.headers.get('Authorization')).toBe( + `Bearer ${actionContext.input.authToken}`, + ); + expect(request.headers.get('Content-Type')).toBe(`application/json`); + await expect(request.json()).resolves.toEqual({ + name: actionContext.input.name, + }); + return HttpResponse.json( + { detail: 'project creation mocked result' }, + { status: 201 }, + ); + }, + ), + ); + + await expect(() => action.handler(actionContext)).rejects.toThrow( + new InputError('No valid sentry token given'), + ); + }); + + it('should throw InputError when sentry API returns unexpected content-type.', async () => { + const action = createSentryCreateProjectAction(createScaffolderConfig()); + const actionContext = getActionContext(); + + worker.use( + http.post( + `https://sentry.io/api/0/teams/${actionContext.input.organizationSlug}/${actionContext.input.teamSlug}/projects/`, + async () => { + return HttpResponse.text('Bad response', { status: 201 }); + }, + ), + ); + + await expect(() => action.handler(actionContext)).rejects.toThrow( + new InputError(`Unexpected Sentry Response Type: Bad response`), + ); + }); + + it('should throw InputError when sentry API returns unexpected status code.', async () => { + const action = createSentryCreateProjectAction(createScaffolderConfig()); + const actionContext = getActionContext(); + + worker.use( + http.post( + `https://sentry.io/api/0/teams/${actionContext.input.organizationSlug}/${actionContext.input.teamSlug}/projects/`, + async () => { + return HttpResponse.json({ detail: 'OUCH' }, { status: 400 }); + }, + ), + ); + + await expect(() => action.handler(actionContext)).rejects.toThrow( + new InputError(`Sentry Response was: OUCH`), + ); + }); +}); diff --git a/plugins/scaffolder-backend-module-sentry/src/actions/createProject.ts b/plugins/scaffolder-backend-module-sentry/src/actions/createProject.ts index 85a74eda07..259b670f9e 100644 --- a/plugins/scaffolder-backend-module-sentry/src/actions/createProject.ts +++ b/plugins/scaffolder-backend-module-sentry/src/actions/createProject.ts @@ -19,7 +19,7 @@ import { InputError } from '@backstage/errors'; import { Config } from '@backstage/config'; /** - * Creates the `sentry:craete-project` Scaffolder action. + * Creates the `sentry:project:create` Scaffolder action. * * @remarks * diff --git a/plugins/scaffolder-backend-module-yeoman/CHANGELOG.md b/plugins/scaffolder-backend-module-yeoman/CHANGELOG.md index 0e2c6901c0..53389300e2 100644 --- a/plugins/scaffolder-backend-module-yeoman/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-yeoman/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-scaffolder-backend-module-yeoman +## 0.2.29-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + - @backstage/plugin-scaffolder-node@0.2.9-next.1 + ## 0.2.29-next.0 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-yeoman/package.json b/plugins/scaffolder-backend-module-yeoman/package.json index 400e2e616b..6870b60eb9 100644 --- a/plugins/scaffolder-backend-module-yeoman/package.json +++ b/plugins/scaffolder-backend-module-yeoman/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-yeoman", - "version": "0.2.29-next.0", + "version": "0.2.29-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/scaffolder-backend/CHANGELOG.md b/plugins/scaffolder-backend/CHANGELOG.md index 531f437ca3..30b1516a2d 100644 --- a/plugins/scaffolder-backend/CHANGELOG.md +++ b/plugins/scaffolder-backend/CHANGELOG.md @@ -1,5 +1,29 @@ # @backstage/plugin-scaffolder-backend +## 1.19.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.15.1-next.1 + - @backstage/catalog-client@1.5.0-next.0 + - @backstage/integration@1.8.0-next.1 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/backend-tasks@0.5.13-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.2-next.1 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.1.5-next.1 + - @backstage/plugin-catalog-common@1.0.18 + - @backstage/plugin-catalog-node@1.5.1-next.1 + - @backstage/plugin-permission-common@0.7.10 + - @backstage/plugin-permission-node@0.7.19-next.1 + - @backstage/plugin-scaffolder-common@1.4.3 + - @backstage/plugin-scaffolder-node@0.2.9-next.1 + ## 1.19.2-next.0 ### Patch Changes diff --git a/plugins/scaffolder-backend/alpha-api-report.md b/plugins/scaffolder-backend/api-report-alpha.md similarity index 100% rename from plugins/scaffolder-backend/alpha-api-report.md rename to plugins/scaffolder-backend/api-report-alpha.md diff --git a/plugins/scaffolder-backend/package.json b/plugins/scaffolder-backend/package.json index b1d5b86671..f1ea2a3517 100644 --- a/plugins/scaffolder-backend/package.json +++ b/plugins/scaffolder-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-scaffolder-backend", "description": "The Backstage backend plugin that helps you create new things", - "version": "1.19.2-next.0", + "version": "1.19.2-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/scaffolder-common/alpha-api-report.md b/plugins/scaffolder-common/api-report-alpha.md similarity index 100% rename from plugins/scaffolder-common/alpha-api-report.md rename to plugins/scaffolder-common/api-report-alpha.md diff --git a/plugins/scaffolder-node/CHANGELOG.md b/plugins/scaffolder-node/CHANGELOG.md index 0d2c194e06..7dcb041c2f 100644 --- a/plugins/scaffolder-node/CHANGELOG.md +++ b/plugins/scaffolder-node/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-scaffolder-node +## 0.2.9-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.8.0-next.1 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-scaffolder-common@1.4.3 + ## 0.2.9-next.0 ### Patch Changes diff --git a/plugins/scaffolder-node/alpha-api-report.md b/plugins/scaffolder-node/api-report-alpha.md similarity index 100% rename from plugins/scaffolder-node/alpha-api-report.md rename to plugins/scaffolder-node/api-report-alpha.md diff --git a/plugins/scaffolder-node/package.json b/plugins/scaffolder-node/package.json index f9506dcc96..5f5702a035 100644 --- a/plugins/scaffolder-node/package.json +++ b/plugins/scaffolder-node/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-scaffolder-node", "description": "The plugin-scaffolder-node module for @backstage/plugin-scaffolder-backend", - "version": "0.2.9-next.0", + "version": "0.2.9-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/scaffolder-react/CHANGELOG.md b/plugins/scaffolder-react/CHANGELOG.md index a66eb35b63..b1b2699564 100644 --- a/plugins/scaffolder-react/CHANGELOG.md +++ b/plugins/scaffolder-react/CHANGELOG.md @@ -1,5 +1,28 @@ # @backstage/plugin-scaffolder-react +## 1.6.2-next.1 + +### Patch Changes + +- fa66d1b5b3: Fixed bug in `ReviewState` where `enum` value was displayed in step review instead of the corresponding label when using `enumNames` +- 2aee53bbeb: Add horizontal slider if stepper overflows +- 2b725913c1: Updated dependency `@rjsf/utils` to `5.14.3`. + Updated dependency `@rjsf/core` to `5.14.3`. + Updated dependency `@rjsf/material-ui` to `5.14.3`. + Updated dependency `@rjsf/validator-ajv8` to `5.14.3`. +- a518c5a25b: Updated dependency `@react-hookz/web` to `^23.0.0`. +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-client@1.5.0-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.7 + - @backstage/plugin-scaffolder-common@1.4.3 + ## 1.6.2-next.0 ### Patch Changes diff --git a/plugins/scaffolder-react/alpha-api-report.md b/plugins/scaffolder-react/api-report-alpha.md similarity index 100% rename from plugins/scaffolder-react/alpha-api-report.md rename to plugins/scaffolder-react/api-report-alpha.md diff --git a/plugins/scaffolder-react/package.json b/plugins/scaffolder-react/package.json index 22cf4ba5a2..9849696d19 100644 --- a/plugins/scaffolder-react/package.json +++ b/plugins/scaffolder-react/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-scaffolder-react", "description": "A frontend library that helps other Backstage plugins interact with the Scaffolder", - "version": "1.6.2-next.0", + "version": "1.6.2-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -59,11 +59,11 @@ "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.61", - "@react-hookz/web": "^20.0.0", - "@rjsf/core": "5.14.2", - "@rjsf/material-ui": "5.14.2", - "@rjsf/utils": "5.14.2", - "@rjsf/validator-ajv8": "5.14.2", + "@react-hookz/web": "^23.0.0", + "@rjsf/core": "5.14.3", + "@rjsf/material-ui": "5.14.3", + "@rjsf/utils": "5.14.3", + "@rjsf/validator-ajv8": "5.14.3", "@types/json-schema": "^7.0.9", "@types/react": "^16.13.1 || ^17.0.0", "classnames": "^2.2.6", diff --git a/plugins/scaffolder-react/src/next/components/ReviewState/ReviewState.test.tsx b/plugins/scaffolder-react/src/next/components/ReviewState/ReviewState.test.tsx index 0f56a32d1d..e9277b2230 100644 --- a/plugins/scaffolder-react/src/next/components/ReviewState/ReviewState.test.tsx +++ b/plugins/scaffolder-react/src/next/components/ReviewState/ReviewState.test.tsx @@ -94,10 +94,10 @@ describe('ReviewState', () => { }, ]; - const { queryByRole } = render( + const { queryByRole, getAllByRole } = render( , ); - + expect(getAllByRole('row').length).toEqual(1); expect(queryByRole('row', { name: 'Name ******' })).not.toBeInTheDocument(); }); @@ -136,4 +136,70 @@ describe('ReviewState', () => { expect(getByRole('row', { name: 'Name lols' })).toBeInTheDocument(); }); + + it('should display enum label from enumNames', async () => { + const formState = { + name: 'type2', + }; + + const schemas: ParsedTemplateSchema[] = [ + { + mergedSchema: { + type: 'object', + properties: { + name: { + type: 'string', + default: 'type1', + enum: ['type1', 'type2', 'type3'], + enumNames: ['Label-type1', 'Label-type2', 'Label-type3'], + }, + }, + }, + schema: {}, + title: 'test', + uiSchema: {}, + description: 'asd', + }, + ]; + + const { queryByRole } = render( + , + ); + + expect( + queryByRole('row', { name: 'Name Label-type2' }), + ).toBeInTheDocument(); + }); + + it('should display enum value if no corresponding enumNames', async () => { + const formState = { + name: 'type4', + }; + + const schemas: ParsedTemplateSchema[] = [ + { + mergedSchema: { + type: 'object', + properties: { + name: { + type: 'string', + default: 'type1', + enum: ['type1', 'type2', 'type3', 'type4'], + enumNames: ['Label-type1', 'Label-type2', 'Label-type3'], + }, + }, + }, + schema: {}, + title: 'test', + uiSchema: {}, + description: 'asd', + }, + ]; + + const { queryByRole } = render( + , + ); + + expect(queryByRole('row', { name: 'Name type4' })).toBeInTheDocument(); + }); }); diff --git a/plugins/scaffolder-react/src/next/components/ReviewState/ReviewState.tsx b/plugins/scaffolder-react/src/next/components/ReviewState/ReviewState.tsx index 52ad80c4c2..b601f77bf6 100644 --- a/plugins/scaffolder-react/src/next/components/ReviewState/ReviewState.tsx +++ b/plugins/scaffolder-react/src/next/components/ReviewState/ReviewState.tsx @@ -34,33 +34,44 @@ export type ReviewStateProps = { */ export const ReviewState = (props: ReviewStateProps) => { const reviewData = Object.fromEntries( - Object.entries(props.formState).map(([key, value]) => { - for (const step of props.schemas) { - const parsedSchema = new JSONSchema(step.mergedSchema); - const definitionInSchema = parsedSchema.getSchema( - `#/${key}`, - props.formState, - ); - if (definitionInSchema) { - const backstageReviewOptions = - definitionInSchema['ui:backstage']?.review; + Object.entries(props.formState) + .map(([key, value]) => { + for (const step of props.schemas) { + const parsedSchema = new JSONSchema(step.mergedSchema); + const definitionInSchema = parsedSchema.getSchema( + `#/${key}`, + props.formState, + ); + if (definitionInSchema) { + const backstageReviewOptions = + definitionInSchema['ui:backstage']?.review; - if (backstageReviewOptions) { - if (backstageReviewOptions.mask) { - return [key, backstageReviewOptions.mask]; + if (backstageReviewOptions) { + if (backstageReviewOptions.mask) { + return [key, backstageReviewOptions.mask]; + } + if (backstageReviewOptions.show === false) { + return []; + } } - if (backstageReviewOptions.show === false) { - return []; - } - } - if (definitionInSchema['ui:widget'] === 'password') { - return [key, '******']; + if (definitionInSchema['ui:widget'] === 'password') { + return [key, '******']; + } + + if (definitionInSchema.enum && definitionInSchema.enumNames) { + return [ + key, + definitionInSchema.enumNames[ + definitionInSchema.enum.indexOf(value) + ] || value, + ]; + } } } - } - return [key, value]; - }), + return [key, value]; + }) + .filter(prop => prop.length > 0), ); return ; }; diff --git a/plugins/scaffolder-react/src/next/components/Stepper/Stepper.tsx b/plugins/scaffolder-react/src/next/components/Stepper/Stepper.tsx index 5aaf76316c..a0b06c0e69 100644 --- a/plugins/scaffolder-react/src/next/components/Stepper/Stepper.tsx +++ b/plugins/scaffolder-react/src/next/components/Stepper/Stepper.tsx @@ -190,7 +190,12 @@ export const Stepper = (stepperProps: StepperProps) => { return ( <> {isValidating && } - + {steps.map((step, index) => { const isAllowedLabelClick = activeStep > index; return ( diff --git a/plugins/scaffolder-react/src/next/components/TaskSteps/TaskSteps.tsx b/plugins/scaffolder-react/src/next/components/TaskSteps/TaskSteps.tsx index 96f0d9d865..d74d298778 100644 --- a/plugins/scaffolder-react/src/next/components/TaskSteps/TaskSteps.tsx +++ b/plugins/scaffolder-react/src/next/components/TaskSteps/TaskSteps.tsx @@ -58,6 +58,7 @@ export const TaskSteps = (props: TaskStepsProps) => { activeStep={props.activeStep} alternativeLabel variant="elevation" + style={{ overflowX: 'auto' }} > {props.steps.map(step => { const isCompleted = step.status === 'completed'; diff --git a/plugins/scaffolder/CHANGELOG.md b/plugins/scaffolder/CHANGELOG.md index 547e59f7f3..b1a60d6353 100644 --- a/plugins/scaffolder/CHANGELOG.md +++ b/plugins/scaffolder/CHANGELOG.md @@ -1,5 +1,32 @@ # @backstage/plugin-scaffolder +## 1.16.2-next.1 + +### Patch Changes + +- 2b725913c1: Updated dependency `@rjsf/utils` to `5.14.3`. + Updated dependency `@rjsf/core` to `5.14.3`. + Updated dependency `@rjsf/material-ui` to `5.14.3`. + Updated dependency `@rjsf/validator-ajv8` to `5.14.3`. +- a518c5a25b: Updated dependency `@react-hookz/web` to `^23.0.0`. +- b5fa6918dc: Fixing `headerOptions` not being passed through the `TemplatePage` component +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-client@1.5.0-next.0 + - @backstage/plugin-scaffolder-react@1.6.2-next.1 + - @backstage/integration@1.8.0-next.1 + - @backstage/integration-react@1.1.22-next.1 + - @backstage/plugin-permission-react@0.4.18-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.18 + - @backstage/plugin-scaffolder-common@1.4.3 + ## 1.16.2-next.0 ### Patch Changes diff --git a/plugins/scaffolder/alpha-api-report.md b/plugins/scaffolder/api-report-alpha.md similarity index 96% rename from plugins/scaffolder/alpha-api-report.md rename to plugins/scaffolder/api-report-alpha.md index e659b8f30d..6a1e0648a4 100644 --- a/plugins/scaffolder/alpha-api-report.md +++ b/plugins/scaffolder/api-report-alpha.md @@ -85,6 +85,11 @@ export type TemplateWizardPageProps = { }; layouts?: LayoutOptions[]; formProps?: FormProps_3; + headerOptions?: { + pageTitleOverride?: string; + title?: string; + subtitle?: string; + }; }; // (No @packageDocumentation comment for this package) diff --git a/plugins/scaffolder/package.json b/plugins/scaffolder/package.json index 17d4b131b9..13257e2731 100644 --- a/plugins/scaffolder/package.json +++ b/plugins/scaffolder/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-scaffolder", "description": "The Backstage plugin that helps you create new things", - "version": "1.16.2-next.0", + "version": "1.16.2-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -67,11 +67,11 @@ "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.61", - "@react-hookz/web": "^20.0.0", - "@rjsf/core": "5.14.2", - "@rjsf/material-ui": "5.14.2", - "@rjsf/utils": "5.14.2", - "@rjsf/validator-ajv8": "5.14.2", + "@react-hookz/web": "^23.0.0", + "@rjsf/core": "5.14.3", + "@rjsf/material-ui": "5.14.3", + "@rjsf/utils": "5.14.3", + "@rjsf/validator-ajv8": "5.14.3", "@types/react": "^16.13.1 || ^17.0.0", "@uiw/react-codemirror": "^4.9.3", "classnames": "^2.2.6", diff --git a/plugins/scaffolder/src/components/Router/Router.tsx b/plugins/scaffolder/src/components/Router/Router.tsx index bdda92c0ef..c3d971bd11 100644 --- a/plugins/scaffolder/src/components/Router/Router.tsx +++ b/plugins/scaffolder/src/components/Router/Router.tsx @@ -142,6 +142,7 @@ export const Router = (props: PropsWithChildren) => { element={ { @@ -87,6 +92,7 @@ export const TemplateWizardPage = (props: TemplateWizardPageProps) => { pageTitleOverride="Create a new component" title="Create a new component" subtitle="Create new software components using standard templates in your organization" + {...props.headerOptions} /> import { ConfigurableExtensionDataRef } from '@backstage/frontend-plugin-api'; -import { Extension } from '@backstage/frontend-plugin-api'; +import { ExtensionDefinition } from '@backstage/frontend-plugin-api'; import { ListItemProps } from '@material-ui/core'; import { PortableSchema } from '@backstage/frontend-plugin-api'; import { SearchDocument } from '@backstage/plugin-search-common'; @@ -23,7 +23,9 @@ export function createSearchResultListItemExtension< TConfig extends { noTrack?: boolean; }, ->(options: SearchResultItemExtensionOptions): Extension; +>( + options: SearchResultItemExtensionOptions, +): ExtensionDefinition; // @alpha (undocumented) export type SearchResultItemExtensionComponent = < @@ -47,7 +49,8 @@ export type SearchResultItemExtensionOptions< noTrack?: boolean; }, > = { - id: string; + namespace?: string; + name?: string; attachTo?: { id: string; input: string; diff --git a/plugins/search-react/package.json b/plugins/search-react/package.json index e4dff0421c..e007af3b51 100644 --- a/plugins/search-react/package.json +++ b/plugins/search-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-react", - "version": "1.7.4-next.0", + "version": "1.7.4-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/search-react/src/alpha.test.tsx b/plugins/search-react/src/alpha.test.tsx index e1a8166390..3b05c61eb2 100644 --- a/plugins/search-react/src/alpha.test.tsx +++ b/plugins/search-react/src/alpha.test.tsx @@ -44,8 +44,7 @@ describe('createSearchResultListItemExtension', () => { const TechDocsSearchResultItemExtension = createSearchResultListItemExtension({ - id: 'techdocs', - attachTo: { id: 'plugin.search.page', input: 'items' }, + namespace: 'techdocs', configSchema: createSchemaFromZod(z => z .object({ @@ -67,14 +66,13 @@ describe('createSearchResultListItemExtension', () => { const ExploreSearchResultItemExtension = createSearchResultListItemExtension({ - id: 'explore', - attachTo: { id: 'plugin.search.page', input: 'items' }, + namespace: 'explore', predicate: result => result.type === 'explore', component: async () => ExploreSearchResultItemComponent, }); const SearchPageExtension = createPageExtension({ - id: 'plugin.search.page', + namespace: 'search', defaultPath: '/', inputs: { items: createExtensionInput({ @@ -117,10 +115,10 @@ describe('createSearchResultListItemExtension', () => { ); const getResultItemComponent = (result: SearchResult) => { - const value = inputs.items.find(({ item }) => - item?.predicate?.(result), + const value = inputs.items.find(item => + item?.output.item.predicate?.(result), ); - return value?.item.component ?? DefaultResultItem; + return value?.output.item.component ?? DefaultResultItem; }; const Component = () => { diff --git a/plugins/search-react/src/alpha.tsx b/plugins/search-react/src/alpha.tsx index bb738a2b08..c733e2ee9f 100644 --- a/plugins/search-react/src/alpha.tsx +++ b/plugins/search-react/src/alpha.tsx @@ -58,9 +58,13 @@ export type SearchResultItemExtensionOptions< TConfig extends { noTrack?: boolean }, > = { /** - * The extension id. + * The extension namespace. */ - id: string; + namespace?: string; + /** + * The extension name. + */ + name?: string; /** * The extension attachment point (e.g., search modal or page). */ @@ -86,8 +90,6 @@ export type SearchResultItemExtensionOptions< export function createSearchResultListItemExtension< TConfig extends { noTrack?: boolean }, >(options: SearchResultItemExtensionOptions) { - const id = `plugin.search.result.item.${options.id}`; - const configSchema = 'configSchema' in options ? options.configSchema @@ -98,9 +100,11 @@ export function createSearchResultListItemExtension< ) as PortableSchema); return createExtension({ - id, + kind: 'search-result-list-item', + namespace: options.namespace, + name: options.name, attachTo: options.attachTo ?? { - id: 'plugin.search.page', + id: 'page:search', input: 'items', }, configSchema, diff --git a/plugins/search/CHANGELOG.md b/plugins/search/CHANGELOG.md index cb9e3c9117..03f28975c2 100644 --- a/plugins/search/CHANGELOG.md +++ b/plugins/search/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/plugin-search +## 1.4.4-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.4.0-next.1 + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/plugin-search-react@1.7.4-next.1 + - @backstage/core-compat-api@0.0.1-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.7 + - @backstage/plugin-search-common@1.2.8 + ## 1.4.4-next.1 ### Patch Changes diff --git a/plugins/search/alpha-api-report.md b/plugins/search/api-report-alpha.md similarity index 73% rename from plugins/search/alpha-api-report.md rename to plugins/search/api-report-alpha.md index 99f3d07b57..533734f8e9 100644 --- a/plugins/search/alpha-api-report.md +++ b/plugins/search/api-report-alpha.md @@ -4,7 +4,7 @@ ```ts import { BackstagePlugin } from '@backstage/frontend-plugin-api'; -import { Extension } from '@backstage/frontend-plugin-api'; +import { ExtensionDefinition } from '@backstage/frontend-plugin-api'; import { RouteRef } from '@backstage/frontend-plugin-api'; // @alpha (undocumented) @@ -17,15 +17,15 @@ const _default: BackstagePlugin< export default _default; // @alpha (undocumented) -export const SearchApi: Extension<{}>; +export const SearchApi: ExtensionDefinition<{}>; // @alpha (undocumented) -export const SearchNavItem: Extension<{ +export const SearchNavItem: ExtensionDefinition<{ title: string; }>; // @alpha (undocumented) -export const SearchPage: Extension<{ +export const SearchPage: ExtensionDefinition<{ path: string; noTrack: boolean; }>; diff --git a/plugins/search/package.json b/plugins/search/package.json index 9256b886ab..da5bbcbd33 100644 --- a/plugins/search/package.json +++ b/plugins/search/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-search", "description": "The Backstage plugin that provides your backstage app with search", - "version": "1.4.4-next.1", + "version": "1.4.4-next.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/search/src/alpha.tsx b/plugins/search/src/alpha.tsx index ef11828c71..d18f93391c 100644 --- a/plugins/search/src/alpha.tsx +++ b/plugins/search/src/alpha.tsx @@ -98,7 +98,6 @@ const useSearchPageStyles = makeStyles((theme: Theme) => ({ /** @alpha */ export const SearchPage = createPageExtension({ - id: 'plugin.search.page', routeRef: convertLegacyRouteRef(rootRouteRef), configSchema: createSchemaFromZod(z => z.object({ @@ -113,8 +112,10 @@ export const SearchPage = createPageExtension({ }, loader: async ({ config, inputs }) => { const getResultItemComponent = (result: SearchResult) => { - const value = inputs.items.find(({ item }) => item?.predicate?.(result)); - return value?.item.component ?? DefaultResultListItem; + const value = inputs.items.find(item => + item?.output.item.predicate?.(result), + ); + return value?.output.item.component ?? DefaultResultListItem; }; const Component = () => { @@ -235,7 +236,6 @@ export const SearchPage = createPageExtension({ /** @alpha */ export const SearchNavItem = createNavItemExtension({ - id: 'plugin.search.nav.index', routeRef: convertLegacyRouteRef(rootRouteRef), title: 'Search', icon: SearchIcon, diff --git a/plugins/sentry/CHANGELOG.md b/plugins/sentry/CHANGELOG.md index 1fa91a01ea..18cd09a846 100644 --- a/plugins/sentry/CHANGELOG.md +++ b/plugins/sentry/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-sentry +## 0.5.13-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/theme@0.5.0-next.0 + ## 0.5.13-next.0 ### Patch Changes diff --git a/plugins/sentry/package.json b/plugins/sentry/package.json index 1094ff07c5..aa705c40ca 100644 --- a/plugins/sentry/package.json +++ b/plugins/sentry/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-sentry", "description": "A Backstage plugin that integrates towards Sentry", - "version": "0.5.13-next.0", + "version": "0.5.13-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/shortcuts/CHANGELOG.md b/plugins/shortcuts/CHANGELOG.md index 3247352329..9360aa23a3 100644 --- a/plugins/shortcuts/CHANGELOG.md +++ b/plugins/shortcuts/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-shortcuts +## 0.3.17-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/theme@0.5.0-next.0 + - @backstage/types@1.1.1 + ## 0.3.17-next.0 ### Patch Changes diff --git a/plugins/shortcuts/package.json b/plugins/shortcuts/package.json index 14afb8df00..85002ba918 100644 --- a/plugins/shortcuts/package.json +++ b/plugins/shortcuts/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-shortcuts", "description": "A Backstage plugin that provides a shortcuts feature to the sidebar", - "version": "0.3.17-next.0", + "version": "0.3.17-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/sonarqube-backend/CHANGELOG.md b/plugins/sonarqube-backend/CHANGELOG.md index d3daaf6505..45fd7abc59 100644 --- a/plugins/sonarqube-backend/CHANGELOG.md +++ b/plugins/sonarqube-backend/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-sonarqube-backend +## 0.2.10-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + ## 0.2.10-next.0 ### Patch Changes diff --git a/plugins/sonarqube-backend/package.json b/plugins/sonarqube-backend/package.json index 27b38e77a2..9959075967 100644 --- a/plugins/sonarqube-backend/package.json +++ b/plugins/sonarqube-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-sonarqube-backend", - "version": "0.2.10-next.0", + "version": "0.2.10-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/sonarqube-react/CHANGELOG.md b/plugins/sonarqube-react/CHANGELOG.md index a4ff66eaf1..ff15e247ae 100644 --- a/plugins/sonarqube-react/CHANGELOG.md +++ b/plugins/sonarqube-react/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-sonarqube-react +## 0.1.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/catalog-model@1.4.3 + ## 0.1.11-next.0 ### Patch Changes diff --git a/plugins/sonarqube-react/package.json b/plugins/sonarqube-react/package.json index a3578bec1f..c5c90158a1 100644 --- a/plugins/sonarqube-react/package.json +++ b/plugins/sonarqube-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-sonarqube-react", - "version": "0.1.11-next.0", + "version": "0.1.11-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/sonarqube/CHANGELOG.md b/plugins/sonarqube/CHANGELOG.md index 1dc9f44886..d9750032ea 100644 --- a/plugins/sonarqube/CHANGELOG.md +++ b/plugins/sonarqube/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-sonarqube +## 0.7.10-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/plugin-sonarqube-react@0.1.11-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/theme@0.5.0-next.0 + ## 0.7.10-next.0 ### Patch Changes diff --git a/plugins/sonarqube/package.json b/plugins/sonarqube/package.json index 742a7249a7..ec8ec3f633 100644 --- a/plugins/sonarqube/package.json +++ b/plugins/sonarqube/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-sonarqube", "description": "", - "version": "0.7.10-next.0", + "version": "0.7.10-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/splunk-on-call/CHANGELOG.md b/plugins/splunk-on-call/CHANGELOG.md index 5ebc73d577..6b567af287 100644 --- a/plugins/splunk-on-call/CHANGELOG.md +++ b/plugins/splunk-on-call/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-splunk-on-call +## 0.4.17-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/theme@0.5.0-next.0 + ## 0.4.17-next.0 ### Patch Changes diff --git a/plugins/splunk-on-call/package.json b/plugins/splunk-on-call/package.json index 8fc0fd6397..d612cbbbbd 100644 --- a/plugins/splunk-on-call/package.json +++ b/plugins/splunk-on-call/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-splunk-on-call", "description": "A Backstage plugin that integrates towards Splunk On-Call", - "version": "0.4.17-next.0", + "version": "0.4.17-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/stack-overflow-backend/CHANGELOG.md b/plugins/stack-overflow-backend/CHANGELOG.md index d736c2435a..aec0d6a964 100644 --- a/plugins/stack-overflow-backend/CHANGELOG.md +++ b/plugins/stack-overflow-backend/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-stack-overflow-backend +## 0.2.12-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/config@1.1.1 + - @backstage/plugin-search-backend-module-stack-overflow-collator@0.1.1-next.1 + - @backstage/plugin-search-common@1.2.8 + ## 0.2.12-next.0 ### Patch Changes diff --git a/plugins/stack-overflow-backend/package.json b/plugins/stack-overflow-backend/package.json index e4c1d82265..584f9313d2 100644 --- a/plugins/stack-overflow-backend/package.json +++ b/plugins/stack-overflow-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-stack-overflow-backend", "description": "Deprecated, consider using @backstage/plugin-search-backend-module-stack-overflow-collator instead", - "version": "0.2.12-next.0", + "version": "0.2.12-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/stack-overflow/CHANGELOG.md b/plugins/stack-overflow/CHANGELOG.md index 7f579095b9..503462c664 100644 --- a/plugins/stack-overflow/CHANGELOG.md +++ b/plugins/stack-overflow/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-stack-overflow +## 0.1.23-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.4.0-next.1 + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-search-react@1.7.4-next.1 + - @backstage/plugin-home-react@0.1.6-next.1 + - @backstage/config@1.1.1 + - @backstage/theme@0.5.0-next.0 + - @backstage/plugin-search-common@1.2.8 + ## 0.1.23-next.0 ### Patch Changes diff --git a/plugins/stack-overflow/alpha-api-report.md b/plugins/stack-overflow/api-report-alpha.md similarity index 100% rename from plugins/stack-overflow/alpha-api-report.md rename to plugins/stack-overflow/api-report-alpha.md diff --git a/plugins/stack-overflow/package.json b/plugins/stack-overflow/package.json index 9c55565b0a..2ec939d18c 100644 --- a/plugins/stack-overflow/package.json +++ b/plugins/stack-overflow/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-stack-overflow", - "version": "0.1.23-next.0", + "version": "0.1.23-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/stack-overflow/src/alpha.tsx b/plugins/stack-overflow/src/alpha.tsx index 3708271ef1..f34c91f78b 100644 --- a/plugins/stack-overflow/src/alpha.tsx +++ b/plugins/stack-overflow/src/alpha.tsx @@ -33,7 +33,6 @@ const StackOverflowApi = createApiExtension({ /** @alpha */ const StackOverflowSearchResultListItem = createSearchResultListItemExtension({ - id: 'stack-overflow', predicate: result => result.type === 'stack-overflow', component: () => import('./search/StackOverflowSearchResultListItem').then( diff --git a/plugins/stackstorm/CHANGELOG.md b/plugins/stackstorm/CHANGELOG.md index f1e3f1b9ed..5a1f4c0d1c 100644 --- a/plugins/stackstorm/CHANGELOG.md +++ b/plugins/stackstorm/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-stackstorm +## 0.1.9-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + ## 0.1.9-next.0 ### Patch Changes diff --git a/plugins/stackstorm/package.json b/plugins/stackstorm/package.json index bf2bf2ec8c..3a16c66874 100644 --- a/plugins/stackstorm/package.json +++ b/plugins/stackstorm/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-stackstorm", "description": "A Backstage plugin that integrates towards StackStorm", - "version": "0.1.9-next.0", + "version": "0.1.9-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/tech-insights-backend-module-jsonfc/CHANGELOG.md b/plugins/tech-insights-backend-module-jsonfc/CHANGELOG.md index 2c5dc6d596..dd119052ae 100644 --- a/plugins/tech-insights-backend-module-jsonfc/CHANGELOG.md +++ b/plugins/tech-insights-backend-module-jsonfc/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-tech-insights-backend-module-jsonfc +## 0.1.40-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-tech-insights-common@0.2.12 + - @backstage/plugin-tech-insights-node@0.4.14-next.1 + ## 0.1.40-next.0 ### Patch Changes diff --git a/plugins/tech-insights-backend-module-jsonfc/package.json b/plugins/tech-insights-backend-module-jsonfc/package.json index b9250e979f..3fc9d67aed 100644 --- a/plugins/tech-insights-backend-module-jsonfc/package.json +++ b/plugins/tech-insights-backend-module-jsonfc/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-tech-insights-backend-module-jsonfc", - "version": "0.1.40-next.0", + "version": "0.1.40-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/tech-insights-backend/CHANGELOG.md b/plugins/tech-insights-backend/CHANGELOG.md index fdd358402f..c80a4ea1d7 100644 --- a/plugins/tech-insights-backend/CHANGELOG.md +++ b/plugins/tech-insights-backend/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-tech-insights-backend +## 0.5.22-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.5.0-next.0 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-tasks@0.5.13-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-tech-insights-common@0.2.12 + - @backstage/plugin-tech-insights-node@0.4.14-next.1 + ## 0.5.22-next.0 ### Patch Changes diff --git a/plugins/tech-insights-backend/package.json b/plugins/tech-insights-backend/package.json index 46399ebc06..0fbbcf6463 100644 --- a/plugins/tech-insights-backend/package.json +++ b/plugins/tech-insights-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-tech-insights-backend", - "version": "0.5.22-next.0", + "version": "0.5.22-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/tech-insights-node/CHANGELOG.md b/plugins/tech-insights-node/CHANGELOG.md index 5bc20edd52..634b690d9b 100644 --- a/plugins/tech-insights-node/CHANGELOG.md +++ b/plugins/tech-insights-node/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-tech-insights-node +## 0.4.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-tasks@0.5.13-next.1 + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + - @backstage/plugin-tech-insights-common@0.2.12 + ## 0.4.14-next.0 ### Patch Changes diff --git a/plugins/tech-insights-node/package.json b/plugins/tech-insights-node/package.json index e44a0faa42..8b29e6cceb 100644 --- a/plugins/tech-insights-node/package.json +++ b/plugins/tech-insights-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-tech-insights-node", - "version": "0.4.14-next.0", + "version": "0.4.14-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/tech-insights/CHANGELOG.md b/plugins/tech-insights/CHANGELOG.md index 24a0a87835..a73f3dd78e 100644 --- a/plugins/tech-insights/CHANGELOG.md +++ b/plugins/tech-insights/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-tech-insights +## 0.3.20-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + - @backstage/types@1.1.1 + - @backstage/plugin-tech-insights-common@0.2.12 + ## 0.3.20-next.0 ### Patch Changes diff --git a/plugins/tech-insights/package.json b/plugins/tech-insights/package.json index 8e25134cea..47b0044adb 100644 --- a/plugins/tech-insights/package.json +++ b/plugins/tech-insights/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-tech-insights", - "version": "0.3.20-next.0", + "version": "0.3.20-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/tech-radar/CHANGELOG.md b/plugins/tech-radar/CHANGELOG.md index 0157900471..b732328bb6 100644 --- a/plugins/tech-radar/CHANGELOG.md +++ b/plugins/tech-radar/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-tech-radar +## 0.6.11-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.4.0-next.1 + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/core-compat-api@0.0.1-next.1 + - @backstage/theme@0.5.0-next.0 + ## 0.6.11-next.1 ### Patch Changes diff --git a/plugins/tech-radar/alpha-api-report.md b/plugins/tech-radar/api-report-alpha.md similarity index 75% rename from plugins/tech-radar/alpha-api-report.md rename to plugins/tech-radar/api-report-alpha.md index da8f12cf0e..bcc9142223 100644 --- a/plugins/tech-radar/alpha-api-report.md +++ b/plugins/tech-radar/api-report-alpha.md @@ -4,7 +4,7 @@ ```ts import { BackstagePlugin } from '@backstage/frontend-plugin-api'; -import { Extension } from '@backstage/frontend-plugin-api'; +import { ExtensionDefinition } from '@backstage/frontend-plugin-api'; import { RouteRef } from '@backstage/frontend-plugin-api'; // @alpha (undocumented) @@ -17,7 +17,10 @@ const _default: BackstagePlugin< export default _default; // @alpha (undocumented) -export const TechRadarPage: Extension<{ +export const sampleTechRadarApi: ExtensionDefinition<{}>; + +// @alpha (undocumented) +export const TechRadarPage: ExtensionDefinition<{ height: number; width: number; title: string; diff --git a/plugins/tech-radar/package.json b/plugins/tech-radar/package.json index 258e25d753..2fba4121c7 100644 --- a/plugins/tech-radar/package.json +++ b/plugins/tech-radar/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-tech-radar", "description": "A Backstage plugin that lets you display a Tech Radar for your organization", - "version": "0.6.11-next.1", + "version": "0.6.11-next.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -68,6 +68,7 @@ "@backstage/cli": "workspace:^", "@backstage/core-app-api": "workspace:^", "@backstage/dev-utils": "workspace:^", + "@backstage/frontend-test-utils": "workspace:^", "@backstage/test-utils": "workspace:^", "@testing-library/dom": "^9.0.0", "@testing-library/jest-dom": "^6.0.0", diff --git a/plugins/tech-radar/src/alpha.test.tsx b/plugins/tech-radar/src/alpha.test.tsx new file mode 100644 index 0000000000..37d39c412b --- /dev/null +++ b/plugins/tech-radar/src/alpha.test.tsx @@ -0,0 +1,34 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { createExtensionTester } from '@backstage/frontend-test-utils'; +import { screen } from '@testing-library/react'; +import { TechRadarPage, sampleTechRadarApi } from './alpha'; + +describe('TechRadarPage', () => { + beforeAll(() => { + Object.defineProperty(window.SVGElement.prototype, 'getBBox', { + value: () => ({ width: 100, height: 100 }), + configurable: true, + }); + }); + + it('renders without exploding', async () => { + createExtensionTester(TechRadarPage).add(sampleTechRadarApi).render(); + + await expect(screen.findByText('Tech Radar')).resolves.toBeInTheDocument(); + }); +}); diff --git a/plugins/tech-radar/src/alpha.tsx b/plugins/tech-radar/src/alpha.tsx index f9a34e2158..2d15e92537 100644 --- a/plugins/tech-radar/src/alpha.tsx +++ b/plugins/tech-radar/src/alpha.tsx @@ -29,7 +29,6 @@ import { rootRouteRef } from './plugin'; /** @alpha */ export const TechRadarPage = createPageExtension({ - id: 'plugin.techradar.page', defaultPath: '/tech-radar', routeRef: convertLegacyRouteRef(rootRouteRef), configSchema: createSchemaFromZod(z => @@ -48,11 +47,9 @@ export const TechRadarPage = createPageExtension({ import('./components').then(m => ), }); -const sampleTechRadarApi = createApiExtension({ - api: techRadarApiRef, - factory() { - return createApiFactory(techRadarApiRef, new SampleTechRadarApi()); - }, +/** @alpha */ +export const sampleTechRadarApi = createApiExtension({ + factory: createApiFactory(techRadarApiRef, new SampleTechRadarApi()), }); /** @alpha */ diff --git a/plugins/techdocs-addons-test-utils/CHANGELOG.md b/plugins/techdocs-addons-test-utils/CHANGELOG.md index 2ac11de91a..0a5115644e 100644 --- a/plugins/techdocs-addons-test-utils/CHANGELOG.md +++ b/plugins/techdocs-addons-test-utils/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-techdocs-addons-test-utils +## 1.0.25-next.2 + +### Patch Changes + +- 5d796829bb: Remove unnecessary catalog dependency +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/plugin-catalog@1.16.0-next.2 + - @backstage/core-app-api@1.11.2-next.1 + - @backstage/test-utils@1.4.6-next.1 + - @backstage/plugin-search-react@1.7.4-next.1 + - @backstage/plugin-techdocs@1.9.2-next.2 + - @backstage/integration-react@1.1.22-next.1 + - @backstage/plugin-techdocs-react@1.1.14-next.1 + - @backstage/theme@0.5.0-next.0 + ## 1.0.25-next.1 ### Patch Changes diff --git a/plugins/techdocs-addons-test-utils/package.json b/plugins/techdocs-addons-test-utils/package.json index 9cb79ffb2d..6f334f9ae6 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.25-next.1", + "version": "1.0.25-next.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/techdocs-backend/CHANGELOG.md b/plugins/techdocs-backend/CHANGELOG.md index 38cafea9aa..a307b1cdbb 100644 --- a/plugins/techdocs-backend/CHANGELOG.md +++ b/plugins/techdocs-backend/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-techdocs-backend +## 1.9.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.5.0-next.0 + - @backstage/integration@1.8.0-next.1 + - @backstage/plugin-techdocs-node@1.11.0-next.1 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-catalog-common@1.0.18 + - @backstage/plugin-permission-common@0.7.10 + - @backstage/plugin-search-backend-module-techdocs@0.1.12-next.1 + - @backstage/plugin-search-common@1.2.8 + ## 1.9.1-next.0 ### Patch Changes diff --git a/plugins/techdocs-backend/alpha-api-report.md b/plugins/techdocs-backend/api-report-alpha.md similarity index 100% rename from plugins/techdocs-backend/alpha-api-report.md rename to plugins/techdocs-backend/api-report-alpha.md diff --git a/plugins/techdocs-backend/package.json b/plugins/techdocs-backend/package.json index d1885d9b94..4f288788a9 100644 --- a/plugins/techdocs-backend/package.json +++ b/plugins/techdocs-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-techdocs-backend", "description": "The Backstage backend plugin that renders technical documentation for your components", - "version": "1.9.1-next.0", + "version": "1.9.1-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/techdocs-module-addons-contrib/CHANGELOG.md b/plugins/techdocs-module-addons-contrib/CHANGELOG.md index 1b074bf428..cb30015229 100644 --- a/plugins/techdocs-module-addons-contrib/CHANGELOG.md +++ b/plugins/techdocs-module-addons-contrib/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-techdocs-module-addons-contrib +## 1.1.3-next.1 + +### Patch Changes + +- a518c5a25b: Updated dependency `@react-hookz/web` to `^23.0.0`. +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/integration@1.8.0-next.1 + - @backstage/integration-react@1.1.22-next.1 + - @backstage/plugin-techdocs-react@1.1.14-next.1 + - @backstage/theme@0.5.0-next.0 + ## 1.1.3-next.0 ### Patch Changes diff --git a/plugins/techdocs-module-addons-contrib/package.json b/plugins/techdocs-module-addons-contrib/package.json index 53ff762126..231a8c2fa7 100644 --- a/plugins/techdocs-module-addons-contrib/package.json +++ b/plugins/techdocs-module-addons-contrib/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-techdocs-module-addons-contrib", "description": "Plugin module for contributed TechDocs Addons", - "version": "1.1.3-next.0", + "version": "1.1.3-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -43,7 +43,7 @@ "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.61", - "@react-hookz/web": "^20.0.0", + "@react-hookz/web": "^23.0.0", "git-url-parse": "^13.0.0", "photoswipe": "^5.3.7", "react-use": "^17.2.4" diff --git a/plugins/techdocs-node/CHANGELOG.md b/plugins/techdocs-node/CHANGELOG.md index 84ffa704a5..b2649d492f 100644 --- a/plugins/techdocs-node/CHANGELOG.md +++ b/plugins/techdocs-node/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-techdocs-node +## 1.11.0-next.1 + +### Patch Changes + +- 99fb54183b: Updated dependency `@azure/identity` to `^4.0.0`. +- 2666675457: Updated dependency `@google-cloud/storage` to `^7.0.0`. +- 4f773c15f6: Bumped the default TechDocs docker image version to the latest which was released several month ago +- Updated dependencies + - @backstage/integration@1.8.0-next.1 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/integration-aws-node@0.1.8 + - @backstage/plugin-search-common@1.2.8 + ## 1.11.0-next.0 ### Minor Changes diff --git a/plugins/techdocs-node/api-report.md b/plugins/techdocs-node/api-report.md index de5b5b21b8..5203d30faa 100644 --- a/plugins/techdocs-node/api-report.md +++ b/plugins/techdocs-node/api-report.md @@ -264,7 +264,7 @@ export class TechdocsGenerator implements GeneratorBase { config: Config; scmIntegrations: ScmIntegrationRegistry; }); - static readonly defaultDockerImage = 'spotify/techdocs:v1.2.1'; + static readonly defaultDockerImage = 'spotify/techdocs:v1.2.3'; static fromConfig( config: Config, options: GeneratorOptions, diff --git a/plugins/techdocs-node/package.json b/plugins/techdocs-node/package.json index ed9ad108c8..51a18a28bc 100644 --- a/plugins/techdocs-node/package.json +++ b/plugins/techdocs-node/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-techdocs-node", "description": "Common node.js functionalities for TechDocs, to be shared between techdocs-backend plugin and techdocs-cli", - "version": "1.11.0-next.0", + "version": "1.11.0-next.1", "main": "src/index.ts", "types": "src/index.ts", "publishConfig": { diff --git a/plugins/techdocs-node/src/stages/generate/techdocs.ts b/plugins/techdocs-node/src/stages/generate/techdocs.ts index a4671f24a5..c5130a3f8f 100644 --- a/plugins/techdocs-node/src/stages/generate/techdocs.ts +++ b/plugins/techdocs-node/src/stages/generate/techdocs.ts @@ -53,7 +53,7 @@ export class TechdocsGenerator implements GeneratorBase { * The default docker image (and version) used to generate content. Public * and static so that techdocs-node consumers can use the same version. */ - public static readonly defaultDockerImage = 'spotify/techdocs:v1.2.1'; + public static readonly defaultDockerImage = 'spotify/techdocs:v1.2.3'; private readonly logger: Logger; private readonly containerRunner?: ContainerRunner; private readonly options: GeneratorConfig; diff --git a/plugins/techdocs-react/CHANGELOG.md b/plugins/techdocs-react/CHANGELOG.md index c22425c38d..5c7c768ee2 100644 --- a/plugins/techdocs-react/CHANGELOG.md +++ b/plugins/techdocs-react/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-techdocs-react +## 1.1.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/version-bridge@1.0.7 + ## 1.1.14-next.0 ### Patch Changes diff --git a/plugins/techdocs-react/package.json b/plugins/techdocs-react/package.json index af3f4f3898..75d5a67aa3 100644 --- a/plugins/techdocs-react/package.json +++ b/plugins/techdocs-react/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-techdocs-react", "description": "Shared frontend utilities for TechDocs and Addons", - "version": "1.1.14-next.0", + "version": "1.1.14-next.1", "publishConfig": { "access": "public", "main": "dist/index.esm.js", diff --git a/plugins/techdocs/CHANGELOG.md b/plugins/techdocs/CHANGELOG.md index 781546b460..26794126a7 100644 --- a/plugins/techdocs/CHANGELOG.md +++ b/plugins/techdocs/CHANGELOG.md @@ -1,5 +1,25 @@ # @backstage/plugin-techdocs +## 1.9.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.4.0-next.1 + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/plugin-search-react@1.7.4-next.1 + - @backstage/integration@1.8.0-next.1 + - @backstage/core-compat-api@0.0.1-next.1 + - @backstage/integration-react@1.1.22-next.1 + - @backstage/plugin-techdocs-react@1.1.14-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + - @backstage/plugin-search-common@1.2.8 + ## 1.9.2-next.1 ### Patch Changes diff --git a/plugins/techdocs/alpha-api-report.md b/plugins/techdocs/api-report-alpha.md similarity index 83% rename from plugins/techdocs/alpha-api-report.md rename to plugins/techdocs/api-report-alpha.md index 2b63d6e691..afc3202a43 100644 --- a/plugins/techdocs/alpha-api-report.md +++ b/plugins/techdocs/api-report-alpha.md @@ -4,7 +4,7 @@ ```ts import { BackstagePlugin } from '@backstage/frontend-plugin-api'; -import { Extension } from '@backstage/frontend-plugin-api'; +import { ExtensionDefinition } from '@backstage/frontend-plugin-api'; import { RouteRef } from '@backstage/frontend-plugin-api'; // @alpha (undocumented) @@ -23,7 +23,7 @@ const _default: BackstagePlugin< export default _default; // @alpha (undocumented) -export const TechDocsSearchResultListItemExtension: Extension<{ +export const TechDocsSearchResultListItemExtension: ExtensionDefinition<{ lineClamp: number; noTrack: boolean; asListItem: boolean; diff --git a/plugins/techdocs/package.json b/plugins/techdocs/package.json index 7ddd663700..2217c19956 100644 --- a/plugins/techdocs/package.json +++ b/plugins/techdocs/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-techdocs", "description": "The Backstage plugin that renders technical documentation for your components", - "version": "1.9.2-next.1", + "version": "1.9.2-next.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/techdocs/src/alpha.tsx b/plugins/techdocs/src/alpha.tsx index 533b4cb524..82bda828e2 100644 --- a/plugins/techdocs/src/alpha.tsx +++ b/plugins/techdocs/src/alpha.tsx @@ -46,53 +46,45 @@ import { createEntityContentExtension } from '@backstage/plugin-catalog-react/al /** @alpha */ const techDocsStorage = createApiExtension({ - api: techdocsStorageApiRef, - - factory() { - return createApiFactory({ - api: techdocsStorageApiRef, - deps: { - configApi: configApiRef, - discoveryApi: discoveryApiRef, - identityApi: identityApiRef, - fetchApi: fetchApiRef, - }, - factory: ({ configApi, discoveryApi, identityApi, fetchApi }) => - new TechDocsStorageClient({ - configApi, - discoveryApi, - identityApi, - fetchApi, - }), - }); - }, + factory: createApiFactory({ + api: techdocsStorageApiRef, + deps: { + configApi: configApiRef, + discoveryApi: discoveryApiRef, + identityApi: identityApiRef, + fetchApi: fetchApiRef, + }, + factory: ({ configApi, discoveryApi, identityApi, fetchApi }) => + new TechDocsStorageClient({ + configApi, + discoveryApi, + identityApi, + fetchApi, + }), + }), }); /** @alpha */ const techDocsClient = createApiExtension({ - api: techdocsApiRef, - factory() { - return createApiFactory({ - api: techdocsApiRef, - deps: { - configApi: configApiRef, - discoveryApi: discoveryApiRef, - fetchApi: fetchApiRef, - }, - factory: ({ configApi, discoveryApi, fetchApi }) => - new TechDocsClient({ - configApi, - discoveryApi, - fetchApi, - }), - }); - }, + factory: createApiFactory({ + api: techdocsApiRef, + deps: { + configApi: configApiRef, + discoveryApi: discoveryApiRef, + fetchApi: fetchApiRef, + }, + factory: ({ configApi, discoveryApi, fetchApi }) => + new TechDocsClient({ + configApi, + discoveryApi, + fetchApi, + }), + }), }); /** @alpha */ export const TechDocsSearchResultListItemExtension = createSearchResultListItemExtension({ - id: 'techdocs', configSchema: createSchemaFromZod(z => z.object({ // TODO: Define how the icon can be configurable @@ -118,7 +110,6 @@ export const TechDocsSearchResultListItemExtension = * @alpha */ const TechDocsIndexPage = createPageExtension({ - id: 'plugin.techdocs.indexPage', defaultPath: '/docs', routeRef: convertLegacyRouteRef(rootRouteRef), loader: () => @@ -133,7 +124,7 @@ const TechDocsIndexPage = createPageExtension({ * @alpha */ const TechDocsReaderPage = createPageExtension({ - id: 'plugin.techdocs.readerPage', + name: 'reader', defaultPath: '/docs/:namespace/:kind/:name', routeRef: convertLegacyRouteRef(rootDocsRouteRef), loader: () => @@ -148,7 +139,6 @@ const TechDocsReaderPage = createPageExtension({ * @alpha */ const TechDocsEntityContent = createEntityContentExtension({ - id: 'techdocs', defaultPath: 'docs', defaultTitle: 'TechDocs', loader: () => import('./Router').then(m => ), @@ -156,7 +146,6 @@ const TechDocsEntityContent = createEntityContentExtension({ /** @alpha */ const TechDocsNavItem = createNavItemExtension({ - id: 'plugin.techdocs.nav.index', icon: LibraryBooks, title: 'Docs', routeRef: convertLegacyRouteRef(rootRouteRef), diff --git a/plugins/todo-backend/CHANGELOG.md b/plugins/todo-backend/CHANGELOG.md index 21206ffb11..c2a25854d6 100644 --- a/plugins/todo-backend/CHANGELOG.md +++ b/plugins/todo-backend/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-todo-backend +## 0.3.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.5.0-next.0 + - @backstage/integration@1.8.0-next.1 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-openapi-utils@0.1.1-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-catalog-node@1.5.1-next.1 + ## 0.3.6-next.0 ### Patch Changes diff --git a/plugins/todo-backend/package.json b/plugins/todo-backend/package.json index de6cacce0e..2d33b028ca 100644 --- a/plugins/todo-backend/package.json +++ b/plugins/todo-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-todo-backend", "description": "A Backstage backend plugin that lets you browse TODO comments in your source code", - "version": "0.3.6-next.0", + "version": "0.3.6-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/todo/CHANGELOG.md b/plugins/todo/CHANGELOG.md index e11b9cf345..260e669324 100644 --- a/plugins/todo/CHANGELOG.md +++ b/plugins/todo/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-todo +## 0.2.32-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + ## 0.2.32-next.0 ### Patch Changes diff --git a/plugins/todo/package.json b/plugins/todo/package.json index a8da3e18f0..8df6781fb1 100644 --- a/plugins/todo/package.json +++ b/plugins/todo/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-todo", "description": "A Backstage plugin that lets you browse TODO comments in your source code", - "version": "0.2.32-next.0", + "version": "0.2.32-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/user-settings-backend/CHANGELOG.md b/plugins/user-settings-backend/CHANGELOG.md index e749a227fb..8158c2681d 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.2.7-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.2-next.1 + ## 0.2.7-next.0 ### Patch Changes diff --git a/plugins/user-settings-backend/alpha-api-report.md b/plugins/user-settings-backend/api-report-alpha.md similarity index 100% rename from plugins/user-settings-backend/alpha-api-report.md rename to plugins/user-settings-backend/api-report-alpha.md diff --git a/plugins/user-settings-backend/package.json b/plugins/user-settings-backend/package.json index 63957f0457..3bbbf6a2be 100644 --- a/plugins/user-settings-backend/package.json +++ b/plugins/user-settings-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-user-settings-backend", "description": "The Backstage backend plugin to manage user settings", - "version": "0.2.7-next.0", + "version": "0.2.7-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/user-settings/CHANGELOG.md b/plugins/user-settings/CHANGELOG.md index 09db865099..1cbc748245 100644 --- a/plugins/user-settings/CHANGELOG.md +++ b/plugins/user-settings/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-user-settings +## 0.7.14-next.2 + +### Patch Changes + +- fb8f3bdbc2: Updated alpha translation message keys to use nested format and camel case. +- Updated dependencies + - @backstage/frontend-plugin-api@0.4.0-next.1 + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/core-app-api@1.11.2-next.1 + - @backstage/core-compat-api@0.0.1-next.1 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + - @backstage/types@1.1.1 + ## 0.7.14-next.1 ### Patch Changes diff --git a/plugins/user-settings/alpha-api-report.md b/plugins/user-settings/api-report-alpha.md similarity index 54% rename from plugins/user-settings/alpha-api-report.md rename to plugins/user-settings/api-report-alpha.md index f375eadbee..adb609a2cb 100644 --- a/plugins/user-settings/alpha-api-report.md +++ b/plugins/user-settings/api-report-alpha.md @@ -20,16 +20,16 @@ export default _default; export const userSettingsTranslationRef: TranslationRef< 'user-settings', { - readonly language: 'Language'; - readonly change_the_language: 'Change the language'; - readonly theme: 'Theme'; - readonly theme_light: 'Light'; - readonly theme_dark: 'Dark'; - readonly theme_auto: 'Auto'; - readonly change_the_theme_mode: 'Change the theme mode'; - readonly select_theme: 'Select {{theme}}'; - readonly select_theme_auto: 'Select Auto Theme'; - readonly select_lng: 'Select language {{language}}'; + readonly 'languageToggle.select': 'Select language {{language}}'; + readonly 'languageToggle.title': 'Language'; + readonly 'languageToggle.description': 'Change the language'; + readonly 'themeToggle.select': 'Select theme {{theme}}'; + readonly 'themeToggle.title': 'Theme'; + readonly 'themeToggle.description': 'Change the theme mode'; + readonly 'themeToggle.names.auto': 'Auto'; + readonly 'themeToggle.names.dark': 'Dark'; + readonly 'themeToggle.names.light': 'Light'; + readonly 'themeToggle.selectAuto': 'Select Auto Theme'; } >; diff --git a/plugins/user-settings/package.json b/plugins/user-settings/package.json index 95cda4711e..c25cff5ede 100644 --- a/plugins/user-settings/package.json +++ b/plugins/user-settings/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-user-settings", "description": "A Backstage plugin that provides a settings page", - "version": "0.7.14-next.1", + "version": "0.7.14-next.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/user-settings/src/alpha.tsx b/plugins/user-settings/src/alpha.tsx index 419ee2808d..cda8301e82 100644 --- a/plugins/user-settings/src/alpha.tsx +++ b/plugins/user-settings/src/alpha.tsx @@ -27,7 +27,6 @@ import React from 'react'; export * from './translation'; const UserSettingsPage = createPageExtension({ - id: 'plugin.user-settings.page', defaultPath: '/settings', routeRef: convertLegacyRouteRef(settingsRouteRef), inputs: { @@ -40,7 +39,9 @@ const UserSettingsPage = createPageExtension({ }, loader: ({ inputs }) => import('./components/SettingsPage').then(m => ( - + )), }); diff --git a/plugins/user-settings/src/components/General/UserSettingsLanguageToggle.tsx b/plugins/user-settings/src/components/General/UserSettingsLanguageToggle.tsx index 56756b2248..67519d24ae 100644 --- a/plugins/user-settings/src/components/General/UserSettingsLanguageToggle.tsx +++ b/plugins/user-settings/src/components/General/UserSettingsLanguageToggle.tsx @@ -118,8 +118,8 @@ export const UserSettingsLanguageToggle = () => { > { return ( <>{language} diff --git a/plugins/user-settings/src/components/General/UserSettingsThemeToggle.tsx b/plugins/user-settings/src/components/General/UserSettingsThemeToggle.tsx index ca57de3a78..37d91ecae0 100644 --- a/plugins/user-settings/src/components/General/UserSettingsThemeToggle.tsx +++ b/plugins/user-settings/src/components/General/UserSettingsThemeToggle.tsx @@ -130,8 +130,8 @@ export const UserSettingsThemeToggle = () => { > { const themeTitle = theme.title || (themeId === 'light' || themeId === 'dark' - ? t(`theme_${themeId}`) + ? t(`themeToggle.names.${themeId}`) : themeId); return ( <> @@ -165,9 +165,9 @@ export const UserSettingsThemeToggle = () => { ); })} - + - {t('theme_auto')}  + {t('themeToggle.names.auto')}  diff --git a/plugins/user-settings/src/translation.ts b/plugins/user-settings/src/translation.ts index 42fd8311e2..411ca1af51 100644 --- a/plugins/user-settings/src/translation.ts +++ b/plugins/user-settings/src/translation.ts @@ -20,15 +20,21 @@ import { createTranslationRef } from '@backstage/core-plugin-api/alpha'; export const userSettingsTranslationRef = createTranslationRef({ id: 'user-settings', messages: { - language: 'Language', - change_the_language: 'Change the language', - theme: 'Theme', - theme_light: 'Light', - theme_dark: 'Dark', - theme_auto: 'Auto', - change_the_theme_mode: 'Change the theme mode', - select_theme: 'Select {{theme}}', - select_theme_auto: 'Select Auto Theme', - select_lng: 'Select language {{language}}', + languageToggle: { + title: 'Language', + description: 'Change the language', + select: 'Select language {{language}}', + }, + themeToggle: { + title: 'Theme', + description: 'Change the theme mode', + select: 'Select theme {{theme}}', + selectAuto: 'Select Auto Theme', + names: { + light: 'Light', + dark: 'Dark', + auto: 'Auto', + }, + }, }, }); diff --git a/plugins/vault-backend/CHANGELOG.md b/plugins/vault-backend/CHANGELOG.md index 6437abf187..ccbf0d4030 100644 --- a/plugins/vault-backend/CHANGELOG.md +++ b/plugins/vault-backend/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-vault-backend +## 0.4.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.1 + - @backstage/backend-plugin-api@0.6.8-next.1 + - @backstage/backend-tasks@0.5.13-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-vault-node@0.1.1-next.1 + ## 0.4.1-next.0 ### Patch Changes diff --git a/plugins/vault-backend/package.json b/plugins/vault-backend/package.json index f3e1b35e34..82420b5493 100644 --- a/plugins/vault-backend/package.json +++ b/plugins/vault-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-vault-backend", "description": "A Backstage backend plugin that integrates towards Vault", - "version": "0.4.1-next.0", + "version": "0.4.1-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/vault-node/CHANGELOG.md b/plugins/vault-node/CHANGELOG.md index cad01e299c..794c570555 100644 --- a/plugins/vault-node/CHANGELOG.md +++ b/plugins/vault-node/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-vault-node +## 0.1.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.8-next.1 + ## 0.1.1-next.0 ### Patch Changes diff --git a/plugins/vault-node/package.json b/plugins/vault-node/package.json index 5895ebe1c6..b8ccb98ce3 100644 --- a/plugins/vault-node/package.json +++ b/plugins/vault-node/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-vault-node", "description": "Node.js library for the vault plugin", - "version": "0.1.1-next.0", + "version": "0.1.1-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/vault/CHANGELOG.md b/plugins/vault/CHANGELOG.md index 8af5828881..b1a30d0111 100644 --- a/plugins/vault/CHANGELOG.md +++ b/plugins/vault/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-vault +## 0.1.23-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + ## 0.1.23-next.0 ### Patch Changes diff --git a/plugins/vault/package.json b/plugins/vault/package.json index f6d6523564..9374be54ec 100644 --- a/plugins/vault/package.json +++ b/plugins/vault/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-vault", "description": "A Backstage plugin that integrates towards Vault", - "version": "0.1.23-next.0", + "version": "0.1.23-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/xcmetrics/CHANGELOG.md b/plugins/xcmetrics/CHANGELOG.md index 7db4abda2d..a1aacf43a0 100644 --- a/plugins/xcmetrics/CHANGELOG.md +++ b/plugins/xcmetrics/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-xcmetrics +## 0.2.46-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + ## 0.2.46-next.0 ### Patch Changes diff --git a/plugins/xcmetrics/package.json b/plugins/xcmetrics/package.json index c4e3b72cdb..516c609b7a 100644 --- a/plugins/xcmetrics/package.json +++ b/plugins/xcmetrics/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-xcmetrics", "description": "A Backstage plugin that shows XCode build metrics for your components", - "version": "0.2.46-next.0", + "version": "0.2.46-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/yarn.lock b/yarn.lock index ac8c50cc20..9759cf8787 100644 --- a/yarn.lock +++ b/yarn.lock @@ -397,25 +397,25 @@ __metadata: languageName: node linkType: hard -"@aws-sdk/client-cognito-identity@npm:3.454.0": - version: 3.454.0 - resolution: "@aws-sdk/client-cognito-identity@npm:3.454.0" +"@aws-sdk/client-cognito-identity@npm:3.462.0": + version: 3.462.0 + resolution: "@aws-sdk/client-cognito-identity@npm:3.462.0" dependencies: "@aws-crypto/sha256-browser": 3.0.0 "@aws-crypto/sha256-js": 3.0.0 - "@aws-sdk/client-sts": 3.454.0 + "@aws-sdk/client-sts": 3.462.0 "@aws-sdk/core": 3.451.0 - "@aws-sdk/credential-provider-node": 3.451.0 - "@aws-sdk/middleware-host-header": 3.451.0 - "@aws-sdk/middleware-logger": 3.451.0 - "@aws-sdk/middleware-recursion-detection": 3.451.0 - "@aws-sdk/middleware-signing": 3.451.0 - "@aws-sdk/middleware-user-agent": 3.451.0 + "@aws-sdk/credential-provider-node": 3.460.0 + "@aws-sdk/middleware-host-header": 3.460.0 + "@aws-sdk/middleware-logger": 3.460.0 + "@aws-sdk/middleware-recursion-detection": 3.460.0 + "@aws-sdk/middleware-signing": 3.461.0 + "@aws-sdk/middleware-user-agent": 3.460.0 "@aws-sdk/region-config-resolver": 3.451.0 - "@aws-sdk/types": 3.451.0 - "@aws-sdk/util-endpoints": 3.451.0 - "@aws-sdk/util-user-agent-browser": 3.451.0 - "@aws-sdk/util-user-agent-node": 3.451.0 + "@aws-sdk/types": 3.460.0 + "@aws-sdk/util-endpoints": 3.460.0 + "@aws-sdk/util-user-agent-browser": 3.460.0 + "@aws-sdk/util-user-agent-node": 3.460.0 "@smithy/config-resolver": ^2.0.18 "@smithy/fetch-http-handler": ^2.2.6 "@smithy/hash-node": ^2.0.15 @@ -440,29 +440,29 @@ __metadata: "@smithy/util-retry": ^2.0.6 "@smithy/util-utf8": ^2.0.2 tslib: ^2.5.0 - checksum: 277d664aeaaf3b053949ca96d26c09d4bafc3afbd93a35bbd3a9b9b05c742e82fcc2398b1c63422de049ddbf45b80272427f444721b3e72435ffa40cf7519942 + checksum: e1f7839e977dfafb1d99df9fd781c6fe0830f3e8383ca975dc06cda5a31b25e5dcd95da37f524ed3665b2d46616f7660085bec31dfb79f73471170fa960b0aa2 languageName: node linkType: hard "@aws-sdk/client-eks@npm:^3.350.0": - version: 3.454.0 - resolution: "@aws-sdk/client-eks@npm:3.454.0" + version: 3.462.0 + resolution: "@aws-sdk/client-eks@npm:3.462.0" dependencies: "@aws-crypto/sha256-browser": 3.0.0 "@aws-crypto/sha256-js": 3.0.0 - "@aws-sdk/client-sts": 3.454.0 + "@aws-sdk/client-sts": 3.462.0 "@aws-sdk/core": 3.451.0 - "@aws-sdk/credential-provider-node": 3.451.0 - "@aws-sdk/middleware-host-header": 3.451.0 - "@aws-sdk/middleware-logger": 3.451.0 - "@aws-sdk/middleware-recursion-detection": 3.451.0 - "@aws-sdk/middleware-signing": 3.451.0 - "@aws-sdk/middleware-user-agent": 3.451.0 + "@aws-sdk/credential-provider-node": 3.460.0 + "@aws-sdk/middleware-host-header": 3.460.0 + "@aws-sdk/middleware-logger": 3.460.0 + "@aws-sdk/middleware-recursion-detection": 3.460.0 + "@aws-sdk/middleware-signing": 3.461.0 + "@aws-sdk/middleware-user-agent": 3.460.0 "@aws-sdk/region-config-resolver": 3.451.0 - "@aws-sdk/types": 3.451.0 - "@aws-sdk/util-endpoints": 3.451.0 - "@aws-sdk/util-user-agent-browser": 3.451.0 - "@aws-sdk/util-user-agent-node": 3.451.0 + "@aws-sdk/types": 3.460.0 + "@aws-sdk/util-endpoints": 3.460.0 + "@aws-sdk/util-user-agent-browser": 3.460.0 + "@aws-sdk/util-user-agent-node": 3.460.0 "@smithy/config-resolver": ^2.0.18 "@smithy/fetch-http-handler": ^2.2.6 "@smithy/hash-node": ^2.0.15 @@ -489,29 +489,29 @@ __metadata: "@smithy/util-waiter": ^2.0.13 tslib: ^2.5.0 uuid: ^8.3.2 - checksum: 5a7cf06e2f516ef9dfff82d4097144fcf1b5477dd1277b9ebba22f38fa8108c029fc7c89e62f420daf51c575e52ed6f97c38b64a81770d9f26b367a5f212a446 + checksum: 92b89f2850784250c529dd041f3d7e59293130dc8f6a941851513fbbf8c44097cab1e6b6caedbd659668b8f9232f9c8818c3e991bc5f05e8dae10694c46de3ae languageName: node linkType: hard "@aws-sdk/client-organizations@npm:^3.350.0": - version: 3.454.0 - resolution: "@aws-sdk/client-organizations@npm:3.454.0" + version: 3.462.0 + resolution: "@aws-sdk/client-organizations@npm:3.462.0" dependencies: "@aws-crypto/sha256-browser": 3.0.0 "@aws-crypto/sha256-js": 3.0.0 - "@aws-sdk/client-sts": 3.454.0 + "@aws-sdk/client-sts": 3.462.0 "@aws-sdk/core": 3.451.0 - "@aws-sdk/credential-provider-node": 3.451.0 - "@aws-sdk/middleware-host-header": 3.451.0 - "@aws-sdk/middleware-logger": 3.451.0 - "@aws-sdk/middleware-recursion-detection": 3.451.0 - "@aws-sdk/middleware-signing": 3.451.0 - "@aws-sdk/middleware-user-agent": 3.451.0 + "@aws-sdk/credential-provider-node": 3.460.0 + "@aws-sdk/middleware-host-header": 3.460.0 + "@aws-sdk/middleware-logger": 3.460.0 + "@aws-sdk/middleware-recursion-detection": 3.460.0 + "@aws-sdk/middleware-signing": 3.461.0 + "@aws-sdk/middleware-user-agent": 3.460.0 "@aws-sdk/region-config-resolver": 3.451.0 - "@aws-sdk/types": 3.451.0 - "@aws-sdk/util-endpoints": 3.451.0 - "@aws-sdk/util-user-agent-browser": 3.451.0 - "@aws-sdk/util-user-agent-node": 3.451.0 + "@aws-sdk/types": 3.460.0 + "@aws-sdk/util-endpoints": 3.460.0 + "@aws-sdk/util-user-agent-browser": 3.460.0 + "@aws-sdk/util-user-agent-node": 3.460.0 "@smithy/config-resolver": ^2.0.18 "@smithy/fetch-http-handler": ^2.2.6 "@smithy/hash-node": ^2.0.15 @@ -536,37 +536,37 @@ __metadata: "@smithy/util-retry": ^2.0.6 "@smithy/util-utf8": ^2.0.2 tslib: ^2.5.0 - checksum: 66c4cf0f6eb8eccd61852b6231704aac6561f2a20a6232dc50c5988c7699f3a6bc878e3fb30daeb63e53201c807efe37316746e4a0e40e7df5b633f66a7612e1 + checksum: cf3db6fb3b4f06a4fdec3d6d2bbd5e23f8e66f476cda2fa9821b484de03572016685864ac2229ac14eb348105cdcd86c18030afa2973691ab79f586bdd4a84f9 languageName: node linkType: hard "@aws-sdk/client-s3@npm:^3.350.0": - version: 3.454.0 - resolution: "@aws-sdk/client-s3@npm:3.454.0" + version: 3.462.0 + resolution: "@aws-sdk/client-s3@npm:3.462.0" dependencies: "@aws-crypto/sha1-browser": 3.0.0 "@aws-crypto/sha256-browser": 3.0.0 "@aws-crypto/sha256-js": 3.0.0 - "@aws-sdk/client-sts": 3.454.0 + "@aws-sdk/client-sts": 3.462.0 "@aws-sdk/core": 3.451.0 - "@aws-sdk/credential-provider-node": 3.451.0 - "@aws-sdk/middleware-bucket-endpoint": 3.451.0 - "@aws-sdk/middleware-expect-continue": 3.451.0 - "@aws-sdk/middleware-flexible-checksums": 3.451.0 - "@aws-sdk/middleware-host-header": 3.451.0 - "@aws-sdk/middleware-location-constraint": 3.451.0 - "@aws-sdk/middleware-logger": 3.451.0 - "@aws-sdk/middleware-recursion-detection": 3.451.0 - "@aws-sdk/middleware-sdk-s3": 3.451.0 - "@aws-sdk/middleware-signing": 3.451.0 - "@aws-sdk/middleware-ssec": 3.451.0 - "@aws-sdk/middleware-user-agent": 3.451.0 + "@aws-sdk/credential-provider-node": 3.460.0 + "@aws-sdk/middleware-bucket-endpoint": 3.460.0 + "@aws-sdk/middleware-expect-continue": 3.460.0 + "@aws-sdk/middleware-flexible-checksums": 3.461.0 + "@aws-sdk/middleware-host-header": 3.460.0 + "@aws-sdk/middleware-location-constraint": 3.461.0 + "@aws-sdk/middleware-logger": 3.460.0 + "@aws-sdk/middleware-recursion-detection": 3.460.0 + "@aws-sdk/middleware-sdk-s3": 3.461.0 + "@aws-sdk/middleware-signing": 3.461.0 + "@aws-sdk/middleware-ssec": 3.460.0 + "@aws-sdk/middleware-user-agent": 3.460.0 "@aws-sdk/region-config-resolver": 3.451.0 - "@aws-sdk/signature-v4-multi-region": 3.451.0 - "@aws-sdk/types": 3.451.0 - "@aws-sdk/util-endpoints": 3.451.0 - "@aws-sdk/util-user-agent-browser": 3.451.0 - "@aws-sdk/util-user-agent-node": 3.451.0 + "@aws-sdk/signature-v4-multi-region": 3.461.0 + "@aws-sdk/types": 3.460.0 + "@aws-sdk/util-endpoints": 3.460.0 + "@aws-sdk/util-user-agent-browser": 3.460.0 + "@aws-sdk/util-user-agent-node": 3.460.0 "@aws-sdk/xml-builder": 3.310.0 "@smithy/config-resolver": ^2.0.18 "@smithy/eventstream-serde-browser": ^2.0.13 @@ -601,30 +601,30 @@ __metadata: "@smithy/util-waiter": ^2.0.13 fast-xml-parser: 4.2.5 tslib: ^2.5.0 - checksum: 1317ab80b8457e6ac83f335390c98489be35c6edebaed9132121fcd3e905d7ee98b6a5fd7d03ba0b8aee1a9a93f8ce2abfa97d6ced9d1c0fde210c0385a7efa8 + checksum: dc48dd3b956f1ba2d3942e4806eb7d35d8c3c54b056cd3c704d83227642fe5f4d6d4545f14e6dad8b0d0a1b347884e28cabf8abe7d8385bbdbf15a5b860a7a0e languageName: node linkType: hard "@aws-sdk/client-sqs@npm:^3.350.0": - version: 3.454.0 - resolution: "@aws-sdk/client-sqs@npm:3.454.0" + version: 3.462.0 + resolution: "@aws-sdk/client-sqs@npm:3.462.0" dependencies: "@aws-crypto/sha256-browser": 3.0.0 "@aws-crypto/sha256-js": 3.0.0 - "@aws-sdk/client-sts": 3.454.0 + "@aws-sdk/client-sts": 3.462.0 "@aws-sdk/core": 3.451.0 - "@aws-sdk/credential-provider-node": 3.451.0 - "@aws-sdk/middleware-host-header": 3.451.0 - "@aws-sdk/middleware-logger": 3.451.0 - "@aws-sdk/middleware-recursion-detection": 3.451.0 - "@aws-sdk/middleware-sdk-sqs": 3.451.0 - "@aws-sdk/middleware-signing": 3.451.0 - "@aws-sdk/middleware-user-agent": 3.451.0 + "@aws-sdk/credential-provider-node": 3.460.0 + "@aws-sdk/middleware-host-header": 3.460.0 + "@aws-sdk/middleware-logger": 3.460.0 + "@aws-sdk/middleware-recursion-detection": 3.460.0 + "@aws-sdk/middleware-sdk-sqs": 3.460.0 + "@aws-sdk/middleware-signing": 3.461.0 + "@aws-sdk/middleware-user-agent": 3.460.0 "@aws-sdk/region-config-resolver": 3.451.0 - "@aws-sdk/types": 3.451.0 - "@aws-sdk/util-endpoints": 3.451.0 - "@aws-sdk/util-user-agent-browser": 3.451.0 - "@aws-sdk/util-user-agent-node": 3.451.0 + "@aws-sdk/types": 3.460.0 + "@aws-sdk/util-endpoints": 3.460.0 + "@aws-sdk/util-user-agent-browser": 3.460.0 + "@aws-sdk/util-user-agent-node": 3.460.0 "@smithy/config-resolver": ^2.0.18 "@smithy/fetch-http-handler": ^2.2.6 "@smithy/hash-node": ^2.0.15 @@ -650,26 +650,26 @@ __metadata: "@smithy/util-retry": ^2.0.6 "@smithy/util-utf8": ^2.0.2 tslib: ^2.5.0 - checksum: 4851ddbd509982a9185a2e7e20f52e326ce84c94a42f26368c2e18bd77d29a3e16c832b6308ab126adc5fd5f6b53c9d556cdf5e1aa2bb81bed5c8651d70e6377 + checksum: 4112bd08a25daee6250d6d60f8787cc70f0a02938e7e0eae9bd87dc24c5f7872ae34f2e53791bb097d34590da457a2303bb8a3d1f35277f0e51427e166a77f0f languageName: node linkType: hard -"@aws-sdk/client-sso@npm:3.451.0": - version: 3.451.0 - resolution: "@aws-sdk/client-sso@npm:3.451.0" +"@aws-sdk/client-sso@npm:3.460.0": + version: 3.460.0 + resolution: "@aws-sdk/client-sso@npm:3.460.0" dependencies: "@aws-crypto/sha256-browser": 3.0.0 "@aws-crypto/sha256-js": 3.0.0 "@aws-sdk/core": 3.451.0 - "@aws-sdk/middleware-host-header": 3.451.0 - "@aws-sdk/middleware-logger": 3.451.0 - "@aws-sdk/middleware-recursion-detection": 3.451.0 - "@aws-sdk/middleware-user-agent": 3.451.0 + "@aws-sdk/middleware-host-header": 3.460.0 + "@aws-sdk/middleware-logger": 3.460.0 + "@aws-sdk/middleware-recursion-detection": 3.460.0 + "@aws-sdk/middleware-user-agent": 3.460.0 "@aws-sdk/region-config-resolver": 3.451.0 - "@aws-sdk/types": 3.451.0 - "@aws-sdk/util-endpoints": 3.451.0 - "@aws-sdk/util-user-agent-browser": 3.451.0 - "@aws-sdk/util-user-agent-node": 3.451.0 + "@aws-sdk/types": 3.460.0 + "@aws-sdk/util-endpoints": 3.460.0 + "@aws-sdk/util-user-agent-browser": 3.460.0 + "@aws-sdk/util-user-agent-node": 3.460.0 "@smithy/config-resolver": ^2.0.18 "@smithy/fetch-http-handler": ^2.2.6 "@smithy/hash-node": ^2.0.15 @@ -694,29 +694,29 @@ __metadata: "@smithy/util-retry": ^2.0.6 "@smithy/util-utf8": ^2.0.2 tslib: ^2.5.0 - checksum: 5ab78bf7704acad673eb2f97b7c9873e4e045e8476f827fc5f6e6f46ca37674f04efc1e168cdc4b3b0c5be1e6ada8e1a76fb4963a7af48560423a60b855f6005 + checksum: 902edb7c8ff29c68a6e179473b3a5431b73182d2492d2b9d69617717d9ef365aad9286ae33a57fc8c42206bea29da29e63d7a36c5969f5b6039e49e535f2d2f3 languageName: node linkType: hard -"@aws-sdk/client-sts@npm:3.454.0, @aws-sdk/client-sts@npm:^3.350.0": - version: 3.454.0 - resolution: "@aws-sdk/client-sts@npm:3.454.0" +"@aws-sdk/client-sts@npm:3.462.0, @aws-sdk/client-sts@npm:^3.350.0": + version: 3.462.0 + resolution: "@aws-sdk/client-sts@npm:3.462.0" dependencies: "@aws-crypto/sha256-browser": 3.0.0 "@aws-crypto/sha256-js": 3.0.0 "@aws-sdk/core": 3.451.0 - "@aws-sdk/credential-provider-node": 3.451.0 - "@aws-sdk/middleware-host-header": 3.451.0 - "@aws-sdk/middleware-logger": 3.451.0 - "@aws-sdk/middleware-recursion-detection": 3.451.0 - "@aws-sdk/middleware-sdk-sts": 3.451.0 - "@aws-sdk/middleware-signing": 3.451.0 - "@aws-sdk/middleware-user-agent": 3.451.0 + "@aws-sdk/credential-provider-node": 3.460.0 + "@aws-sdk/middleware-host-header": 3.460.0 + "@aws-sdk/middleware-logger": 3.460.0 + "@aws-sdk/middleware-recursion-detection": 3.460.0 + "@aws-sdk/middleware-sdk-sts": 3.461.0 + "@aws-sdk/middleware-signing": 3.461.0 + "@aws-sdk/middleware-user-agent": 3.460.0 "@aws-sdk/region-config-resolver": 3.451.0 - "@aws-sdk/types": 3.451.0 - "@aws-sdk/util-endpoints": 3.451.0 - "@aws-sdk/util-user-agent-browser": 3.451.0 - "@aws-sdk/util-user-agent-node": 3.451.0 + "@aws-sdk/types": 3.460.0 + "@aws-sdk/util-endpoints": 3.460.0 + "@aws-sdk/util-user-agent-browser": 3.460.0 + "@aws-sdk/util-user-agent-node": 3.460.0 "@smithy/config-resolver": ^2.0.18 "@smithy/fetch-http-handler": ^2.2.6 "@smithy/hash-node": ^2.0.15 @@ -742,7 +742,7 @@ __metadata: "@smithy/util-utf8": ^2.0.2 fast-xml-parser: 4.2.5 tslib: ^2.5.0 - checksum: 92e687abc8dcd5f6ddf911fe5800f04eeb11164214327072334275b04725ac77ee2170247b69df645ff756ed9f2b27916921d442fb393dc46084c114b5f76f54 + checksum: 7e1882a815b90c406b5104cb9f38edffd215af1bf092848531a9bf0b623461e53da36736b04de35e8582a68cb3641ec8f7dbecd364a699426bf32f235a08b5b0 languageName: node linkType: hard @@ -756,36 +756,36 @@ __metadata: languageName: node linkType: hard -"@aws-sdk/credential-provider-cognito-identity@npm:3.454.0": - version: 3.454.0 - resolution: "@aws-sdk/credential-provider-cognito-identity@npm:3.454.0" +"@aws-sdk/credential-provider-cognito-identity@npm:3.462.0": + version: 3.462.0 + resolution: "@aws-sdk/credential-provider-cognito-identity@npm:3.462.0" dependencies: - "@aws-sdk/client-cognito-identity": 3.454.0 - "@aws-sdk/types": 3.451.0 + "@aws-sdk/client-cognito-identity": 3.462.0 + "@aws-sdk/types": 3.460.0 "@smithy/property-provider": ^2.0.0 "@smithy/types": ^2.5.0 tslib: ^2.5.0 - checksum: 4291033f9554ecd83f6a6dffc3309fa0da212dc9c3d197438abc77ed006a3622e9b81fe070305e5038f3ed15eb2a276306d8a040bea7a02d5591d66452e1c460 + checksum: 02afd72dc4addc86c33120f574a2e1b97d91f387535406af26ee5bea10aa74da005dae4eac0b6632d9d21d368d42587ff117f20da269669e9b7bb2c56c5fbc05 languageName: node linkType: hard -"@aws-sdk/credential-provider-env@npm:3.451.0": - version: 3.451.0 - resolution: "@aws-sdk/credential-provider-env@npm:3.451.0" +"@aws-sdk/credential-provider-env@npm:3.460.0": + version: 3.460.0 + resolution: "@aws-sdk/credential-provider-env@npm:3.460.0" dependencies: - "@aws-sdk/types": 3.451.0 + "@aws-sdk/types": 3.460.0 "@smithy/property-provider": ^2.0.0 "@smithy/types": ^2.5.0 tslib: ^2.5.0 - checksum: 20268e3d30317ab92cfce042fbaf751959d8da8e5669b822f8089df26bf5ea4972bab04eb0653fd903a4ab6c41118fae57f589a7e05fd3022e0154cb0cd71ed7 + checksum: a5b3bb0c9d9b05f9b55738b99db33bc894b6b1e30c1ec62b9124f5a3878b24f23b1f26d38fb0ade06bf12c0ea1c4a0d67d197e43f45bb4fd88bdb19249078e72 languageName: node linkType: hard -"@aws-sdk/credential-provider-http@npm:3.451.0": - version: 3.451.0 - resolution: "@aws-sdk/credential-provider-http@npm:3.451.0" +"@aws-sdk/credential-provider-http@npm:3.460.0": + version: 3.460.0 + resolution: "@aws-sdk/credential-provider-http@npm:3.460.0" dependencies: - "@aws-sdk/types": 3.451.0 + "@aws-sdk/types": 3.460.0 "@smithy/fetch-http-handler": ^2.2.6 "@smithy/node-http-handler": ^2.1.9 "@smithy/property-provider": ^2.0.0 @@ -794,108 +794,108 @@ __metadata: "@smithy/types": ^2.5.0 "@smithy/util-stream": ^2.0.20 tslib: ^2.5.0 - checksum: 72d6c1487a24a95e765b665a0d2fb337fc86bb426f1fc999f7a2789a826302a649850791ec428eff13e250b4bf0e33f9d333dc3c34cfb15c83c863c254f601df + checksum: 3e43978179dc869920ce2cc7b4e075bde4e5e948b6c0533ac58393790bed49be38c336233cadc4b52c13aa2df442693a29f0067026f178f2d0ba8034c4de14d9 languageName: node linkType: hard -"@aws-sdk/credential-provider-ini@npm:3.451.0": - version: 3.451.0 - resolution: "@aws-sdk/credential-provider-ini@npm:3.451.0" +"@aws-sdk/credential-provider-ini@npm:3.460.0": + version: 3.460.0 + resolution: "@aws-sdk/credential-provider-ini@npm:3.460.0" dependencies: - "@aws-sdk/credential-provider-env": 3.451.0 - "@aws-sdk/credential-provider-process": 3.451.0 - "@aws-sdk/credential-provider-sso": 3.451.0 - "@aws-sdk/credential-provider-web-identity": 3.451.0 - "@aws-sdk/types": 3.451.0 + "@aws-sdk/credential-provider-env": 3.460.0 + "@aws-sdk/credential-provider-process": 3.460.0 + "@aws-sdk/credential-provider-sso": 3.460.0 + "@aws-sdk/credential-provider-web-identity": 3.460.0 + "@aws-sdk/types": 3.460.0 "@smithy/credential-provider-imds": ^2.0.0 "@smithy/property-provider": ^2.0.0 "@smithy/shared-ini-file-loader": ^2.0.6 "@smithy/types": ^2.5.0 tslib: ^2.5.0 - checksum: 6d549724209f24c2e21fa88f318c7faaa9e2e58c854fcabc257b71cd2e6f849d38eaa932e16b44bf56c420183fd4ae804ac44c7584808af868ae7316fde2c3b1 + checksum: dcb6d94dfaa98971188102ac277746b8226cba4d76d589b47dbd17af271cba52d97606863912699c123d9671ae2b32a92d05a2306b749c909ebeaf1f95a566fb languageName: node linkType: hard -"@aws-sdk/credential-provider-node@npm:3.451.0, @aws-sdk/credential-provider-node@npm:^3.350.0": - version: 3.451.0 - resolution: "@aws-sdk/credential-provider-node@npm:3.451.0" +"@aws-sdk/credential-provider-node@npm:3.460.0, @aws-sdk/credential-provider-node@npm:^3.350.0": + version: 3.460.0 + resolution: "@aws-sdk/credential-provider-node@npm:3.460.0" dependencies: - "@aws-sdk/credential-provider-env": 3.451.0 - "@aws-sdk/credential-provider-ini": 3.451.0 - "@aws-sdk/credential-provider-process": 3.451.0 - "@aws-sdk/credential-provider-sso": 3.451.0 - "@aws-sdk/credential-provider-web-identity": 3.451.0 - "@aws-sdk/types": 3.451.0 + "@aws-sdk/credential-provider-env": 3.460.0 + "@aws-sdk/credential-provider-ini": 3.460.0 + "@aws-sdk/credential-provider-process": 3.460.0 + "@aws-sdk/credential-provider-sso": 3.460.0 + "@aws-sdk/credential-provider-web-identity": 3.460.0 + "@aws-sdk/types": 3.460.0 "@smithy/credential-provider-imds": ^2.0.0 "@smithy/property-provider": ^2.0.0 "@smithy/shared-ini-file-loader": ^2.0.6 "@smithy/types": ^2.5.0 tslib: ^2.5.0 - checksum: 19dd93776250fd4e55a70daaca311f1f0bb27e5b195713e3f9d8e8431411e637c917a46bbd41dcf2044a93e0fdea02ebb238863f8c49684bf3e63aff928b5a6b + checksum: e8ba989a219c75abfcdb9447622a933fd7c601bcbc41e535627d0bee514773c970dd86342baf23e0d241695c96d3e90d19bc028c598b87fdad6a025c16690bf5 languageName: node linkType: hard -"@aws-sdk/credential-provider-process@npm:3.451.0": - version: 3.451.0 - resolution: "@aws-sdk/credential-provider-process@npm:3.451.0" +"@aws-sdk/credential-provider-process@npm:3.460.0": + version: 3.460.0 + resolution: "@aws-sdk/credential-provider-process@npm:3.460.0" dependencies: - "@aws-sdk/types": 3.451.0 + "@aws-sdk/types": 3.460.0 "@smithy/property-provider": ^2.0.0 "@smithy/shared-ini-file-loader": ^2.0.6 "@smithy/types": ^2.5.0 tslib: ^2.5.0 - checksum: 2c01b893d03f9a001bb970553bad14061e807445ec96002bb85d37445fa28403a5adbfc68036f01fc20d7c5310e88ccbcd96cd54d5ba9326640a18d6c96b93a2 + checksum: 67115399ef754dd46138ce086936f0bf680cc701ed12939a059c9fae7dda870829247e6f73c0f5e0d07353d151b77fe070ff50d5946cd21583b78dcbf25b2aab languageName: node linkType: hard -"@aws-sdk/credential-provider-sso@npm:3.451.0": - version: 3.451.0 - resolution: "@aws-sdk/credential-provider-sso@npm:3.451.0" +"@aws-sdk/credential-provider-sso@npm:3.460.0": + version: 3.460.0 + resolution: "@aws-sdk/credential-provider-sso@npm:3.460.0" dependencies: - "@aws-sdk/client-sso": 3.451.0 - "@aws-sdk/token-providers": 3.451.0 - "@aws-sdk/types": 3.451.0 + "@aws-sdk/client-sso": 3.460.0 + "@aws-sdk/token-providers": 3.460.0 + "@aws-sdk/types": 3.460.0 "@smithy/property-provider": ^2.0.0 "@smithy/shared-ini-file-loader": ^2.0.6 "@smithy/types": ^2.5.0 tslib: ^2.5.0 - checksum: 34483b4a213cac1f4859978319b38ef7f69a0d090de49a98d987486da2691a4cf5f474601ce338497d030156b1616a455a8d0bb79a4abbdbe8873a91e451ead2 + checksum: 5536ef6c8c5f2792e26f19e1d9a3a3503b5c38bc02da65372be805eb1ea69498a81759b3701fd9e0714c628a054d6a52de3d9b98430b23d8b1402f25760b3c54 languageName: node linkType: hard -"@aws-sdk/credential-provider-web-identity@npm:3.451.0": - version: 3.451.0 - resolution: "@aws-sdk/credential-provider-web-identity@npm:3.451.0" +"@aws-sdk/credential-provider-web-identity@npm:3.460.0": + version: 3.460.0 + resolution: "@aws-sdk/credential-provider-web-identity@npm:3.460.0" dependencies: - "@aws-sdk/types": 3.451.0 + "@aws-sdk/types": 3.460.0 "@smithy/property-provider": ^2.0.0 "@smithy/types": ^2.5.0 tslib: ^2.5.0 - checksum: 2f677b9e4f727b6c24ed64d1ba23f524d8d136394dc62376db7b60acf57938a911d178bcdc33688a0cb17b5f55e2cc4aa455473a4393864568cacd202a50ce44 + checksum: 76a069b1f0dce14234afed3bab3ed6c3d63f0fcfb7a2d63343d2b2c63526fb7c14de5e8943eafec451b89ff9899f5b0d327b36409bc4661165f5e429b495fc85 languageName: node linkType: hard "@aws-sdk/credential-providers@npm:^3.350.0": - version: 3.454.0 - resolution: "@aws-sdk/credential-providers@npm:3.454.0" + version: 3.462.0 + resolution: "@aws-sdk/credential-providers@npm:3.462.0" dependencies: - "@aws-sdk/client-cognito-identity": 3.454.0 - "@aws-sdk/client-sso": 3.451.0 - "@aws-sdk/client-sts": 3.454.0 - "@aws-sdk/credential-provider-cognito-identity": 3.454.0 - "@aws-sdk/credential-provider-env": 3.451.0 - "@aws-sdk/credential-provider-http": 3.451.0 - "@aws-sdk/credential-provider-ini": 3.451.0 - "@aws-sdk/credential-provider-node": 3.451.0 - "@aws-sdk/credential-provider-process": 3.451.0 - "@aws-sdk/credential-provider-sso": 3.451.0 - "@aws-sdk/credential-provider-web-identity": 3.451.0 - "@aws-sdk/types": 3.451.0 + "@aws-sdk/client-cognito-identity": 3.462.0 + "@aws-sdk/client-sso": 3.460.0 + "@aws-sdk/client-sts": 3.462.0 + "@aws-sdk/credential-provider-cognito-identity": 3.462.0 + "@aws-sdk/credential-provider-env": 3.460.0 + "@aws-sdk/credential-provider-http": 3.460.0 + "@aws-sdk/credential-provider-ini": 3.460.0 + "@aws-sdk/credential-provider-node": 3.460.0 + "@aws-sdk/credential-provider-process": 3.460.0 + "@aws-sdk/credential-provider-sso": 3.460.0 + "@aws-sdk/credential-provider-web-identity": 3.460.0 + "@aws-sdk/types": 3.460.0 "@smithy/credential-provider-imds": ^2.0.0 "@smithy/property-provider": ^2.0.0 "@smithy/types": ^2.5.0 tslib: ^2.5.0 - checksum: fd6a59aded8a16a800c4354ca5445faac0b5507ca65ca7d1bdc2d5b12efb2c161eb87f399ac2c3e1579b5913f26858bb90d74e1a5026647b375efa7ae9233db9 + checksum: 5b552c761baf459c502afba5b7e600d9f70a155d8a3892c17559db06b6cb653363c88f4ad581d896b9bfb33d3e233d08e0ede69883cf3bc910157059c8ac5ce0 languageName: node linkType: hard @@ -921,8 +921,8 @@ __metadata: linkType: hard "@aws-sdk/lib-storage@npm:^3.350.0": - version: 3.454.0 - resolution: "@aws-sdk/lib-storage@npm:3.454.0" + version: 3.462.0 + resolution: "@aws-sdk/lib-storage@npm:3.462.0" dependencies: "@smithy/abort-controller": ^2.0.1 "@smithy/middleware-endpoint": ^2.2.0 @@ -933,22 +933,22 @@ __metadata: tslib: ^2.5.0 peerDependencies: "@aws-sdk/client-s3": ^3.0.0 - checksum: b8d7dd7a333e5d1d13de449269f1295e2d4835aa3e6fcef01e262a551ec0968591ce669f46fd988b678323cfae86dd5d92528282aa4bf772c980feefe3420e8e + checksum: 1d294ebc76f822397f8594c6c7e998532d71f0f5dd32b36641b55185f7298855377952e179e65d3c725a4286be2d09bd83a39da4f12a18457080a07abf92fb9f languageName: node linkType: hard -"@aws-sdk/middleware-bucket-endpoint@npm:3.451.0": - version: 3.451.0 - resolution: "@aws-sdk/middleware-bucket-endpoint@npm:3.451.0" +"@aws-sdk/middleware-bucket-endpoint@npm:3.460.0": + version: 3.460.0 + resolution: "@aws-sdk/middleware-bucket-endpoint@npm:3.460.0" dependencies: - "@aws-sdk/types": 3.451.0 + "@aws-sdk/types": 3.460.0 "@aws-sdk/util-arn-parser": 3.310.0 "@smithy/node-config-provider": ^2.1.5 "@smithy/protocol-http": ^3.0.9 "@smithy/types": ^2.5.0 "@smithy/util-config-provider": ^2.0.0 tslib: ^2.5.0 - checksum: 4fba4c2ca560fe9a482d59a374239b5c6bf63a9f49373b91d9b1ed3aced648f0198b7d3b82c14fa896ec5f465562871bf9c59c29ff2e3f94df88f4641ff2bf78 + checksum: c0c6bb73a6fe64718d438225176b07b6a84e41ddd83ca306fb1aaa92f21b17915497861853b79f4af52e0915bf8859cf3a425c9a2ad270e42bbf409031f70b95 languageName: node linkType: hard @@ -965,116 +965,119 @@ __metadata: languageName: node linkType: hard -"@aws-sdk/middleware-expect-continue@npm:3.451.0": - version: 3.451.0 - resolution: "@aws-sdk/middleware-expect-continue@npm:3.451.0" +"@aws-sdk/middleware-expect-continue@npm:3.460.0": + version: 3.460.0 + resolution: "@aws-sdk/middleware-expect-continue@npm:3.460.0" dependencies: - "@aws-sdk/types": 3.451.0 + "@aws-sdk/types": 3.460.0 "@smithy/protocol-http": ^3.0.9 "@smithy/types": ^2.5.0 tslib: ^2.5.0 - checksum: 3bfe5a1fc8f3e8baaec650aa085cb7a1c79d8d5af41bd940c005ba9c3c6d4feca2b65513d3494fe986276f43154e829f95ce1de4d852a741d7900cd95b864c1a + checksum: b18d117ae409a4d219b796bed5cb8e0ae7ac6e5e5085efdf66f1a4790d312a9d5988b0f74f843337b3e0deff867712482c7a0d813effe70ec9c41f40007e7d28 languageName: node linkType: hard -"@aws-sdk/middleware-flexible-checksums@npm:3.451.0": - version: 3.451.0 - resolution: "@aws-sdk/middleware-flexible-checksums@npm:3.451.0" +"@aws-sdk/middleware-flexible-checksums@npm:3.461.0": + version: 3.461.0 + resolution: "@aws-sdk/middleware-flexible-checksums@npm:3.461.0" dependencies: "@aws-crypto/crc32": 3.0.0 "@aws-crypto/crc32c": 3.0.0 - "@aws-sdk/types": 3.451.0 + "@aws-sdk/types": 3.460.0 "@smithy/is-array-buffer": ^2.0.0 "@smithy/protocol-http": ^3.0.9 "@smithy/types": ^2.5.0 "@smithy/util-utf8": ^2.0.2 tslib: ^2.5.0 - checksum: 24f69c68437899286c19e68caf417e263529190f4e663c8e886a032090452c9cbaa0d6d25887a65a7234edb9d2657f847b2ff589aec2341852d727a174c94cf2 + checksum: 1e0af49e58b809400df1ffc7b72a5d694d6a35aab4f819d4fec3570a867d93fca101bd1a64e78d1b3838cfb6f753178ab98e7f6ed526d75184bf5760a9cb7948 languageName: node linkType: hard -"@aws-sdk/middleware-host-header@npm:3.451.0": - version: 3.451.0 - resolution: "@aws-sdk/middleware-host-header@npm:3.451.0" +"@aws-sdk/middleware-host-header@npm:3.460.0": + version: 3.460.0 + resolution: "@aws-sdk/middleware-host-header@npm:3.460.0" dependencies: - "@aws-sdk/types": 3.451.0 + "@aws-sdk/types": 3.460.0 "@smithy/protocol-http": ^3.0.9 "@smithy/types": ^2.5.0 tslib: ^2.5.0 - checksum: ba06894d88f5fdd06762de839e7542b1605c73474d7bf93e6b0b74a62e77c5f3f4dd612daaf76e9eff59447c95a73cfa69b29f9856f7282227eea31173db99ca + checksum: ff3f3bf367a6db742e4f3dc05a100e51f49eadbc19602b3f44ab97dad6002baa2e905d7575fe87a86dc32da4063395098e282336f7c56aad61cc04592d4fb61e languageName: node linkType: hard -"@aws-sdk/middleware-location-constraint@npm:3.451.0": - version: 3.451.0 - resolution: "@aws-sdk/middleware-location-constraint@npm:3.451.0" +"@aws-sdk/middleware-location-constraint@npm:3.461.0": + version: 3.461.0 + resolution: "@aws-sdk/middleware-location-constraint@npm:3.461.0" dependencies: - "@aws-sdk/types": 3.451.0 + "@aws-sdk/types": 3.460.0 "@smithy/types": ^2.5.0 tslib: ^2.5.0 - checksum: d47ea96b28c429196352f2e99cbd5a3ab57726e0be0bfdb2d9ef7acb1e492a4a318f7c40e54bd14ce3b8d3f6f340e1fa89fcd5e3c62cf4e1f8edf08edd78b98b + checksum: 5d9a206dde792df6d703fa5edc0902fed658a1be0c75adb1ebe10018edff5eb3be7ab58a78d71d0112fd96648c1ef2879a5b2f4a1f449133b2ebb0281aeafe4f languageName: node linkType: hard -"@aws-sdk/middleware-logger@npm:3.451.0": - version: 3.451.0 - resolution: "@aws-sdk/middleware-logger@npm:3.451.0" +"@aws-sdk/middleware-logger@npm:3.460.0": + version: 3.460.0 + resolution: "@aws-sdk/middleware-logger@npm:3.460.0" dependencies: - "@aws-sdk/types": 3.451.0 + "@aws-sdk/types": 3.460.0 "@smithy/types": ^2.5.0 tslib: ^2.5.0 - checksum: 34342389013ab83407c34eb58a5e162fb7b294050030f494a2b538e85492828a2719642365af1f7e5b89f847888fd936469c4bdb6843b81887edb89896b0a926 + checksum: 21b5835cb19d9dade6a1a07ac9566b8c0519cd5b1c632f36a707345292fbbfe6f5914bf0a37c7384ecdde469b01996cb42efe77dfa6f7b63ac7c131383001660 languageName: node linkType: hard -"@aws-sdk/middleware-recursion-detection@npm:3.451.0": - version: 3.451.0 - resolution: "@aws-sdk/middleware-recursion-detection@npm:3.451.0" +"@aws-sdk/middleware-recursion-detection@npm:3.460.0": + version: 3.460.0 + resolution: "@aws-sdk/middleware-recursion-detection@npm:3.460.0" dependencies: - "@aws-sdk/types": 3.451.0 + "@aws-sdk/types": 3.460.0 "@smithy/protocol-http": ^3.0.9 "@smithy/types": ^2.5.0 tslib: ^2.5.0 - checksum: 70625b5c9e9f61d4c58a05a8aa616ec31c7bb23f45e8187fffe30003861fd3fe0bce96e6b120b4bb3937c0c66e04c96c8e024a4c42dd0524f0a0b6dc73b7e1d5 + checksum: 45786cb548d8e5f823a911bc443f596ddf16db1bfd2a5e017fd4c6c098021ed2ba21e4b505df3a1839f5ffd9976f3a01b2743d02fbdaacfbc7fd94dd65fdc6da languageName: node linkType: hard -"@aws-sdk/middleware-sdk-s3@npm:3.451.0": - version: 3.451.0 - resolution: "@aws-sdk/middleware-sdk-s3@npm:3.451.0" +"@aws-sdk/middleware-sdk-s3@npm:3.461.0": + version: 3.461.0 + resolution: "@aws-sdk/middleware-sdk-s3@npm:3.461.0" dependencies: - "@aws-sdk/types": 3.451.0 + "@aws-sdk/types": 3.460.0 "@aws-sdk/util-arn-parser": 3.310.0 + "@smithy/node-config-provider": ^2.1.5 "@smithy/protocol-http": ^3.0.9 + "@smithy/signature-v4": ^2.0.0 "@smithy/smithy-client": ^2.1.15 "@smithy/types": ^2.5.0 + "@smithy/util-config-provider": ^2.0.0 tslib: ^2.5.0 - checksum: e8ba5b5d5918223b4f76113dcfabf7f342880f0ef5a0ebae861f142f2d74748f1aadd47270d61c591e31ba88c52cfca0cf7ba992384b34271e13ca144edf48a6 + checksum: bab702b64227960239bde83021c964f34ee93d398b6a9db4c3550af81d19450ada72d0bab5f27a710b5eceea2b19874f62543f52279ee734407239c3552a99da languageName: node linkType: hard -"@aws-sdk/middleware-sdk-sqs@npm:3.451.0": - version: 3.451.0 - resolution: "@aws-sdk/middleware-sdk-sqs@npm:3.451.0" +"@aws-sdk/middleware-sdk-sqs@npm:3.460.0": + version: 3.460.0 + resolution: "@aws-sdk/middleware-sdk-sqs@npm:3.460.0" dependencies: - "@aws-sdk/types": 3.451.0 + "@aws-sdk/types": 3.460.0 "@smithy/types": ^2.5.0 "@smithy/util-hex-encoding": ^2.0.0 "@smithy/util-utf8": ^2.0.2 tslib: ^2.5.0 - checksum: 4ef20e1ebffb3a2fe6ddc77a5ca18203ef405e71f77b3167cbee8241e5ff77be08d3ca43fb25c3582454ddc53d42b44f38c6942c95d706ac4c4f6111d062910a + checksum: 6c60fa8d80b12ab658b20f28d9991434853fcf64dd683c6ac29c0a405113d9aea96c072576bf5ccf016391af24f32309e5c15d1083aba8518b06d9983e8f6731 languageName: node linkType: hard -"@aws-sdk/middleware-sdk-sts@npm:3.451.0": - version: 3.451.0 - resolution: "@aws-sdk/middleware-sdk-sts@npm:3.451.0" +"@aws-sdk/middleware-sdk-sts@npm:3.461.0": + version: 3.461.0 + resolution: "@aws-sdk/middleware-sdk-sts@npm:3.461.0" dependencies: - "@aws-sdk/middleware-signing": 3.451.0 - "@aws-sdk/types": 3.451.0 + "@aws-sdk/middleware-signing": 3.461.0 + "@aws-sdk/types": 3.460.0 "@smithy/types": ^2.5.0 tslib: ^2.5.0 - checksum: 8aef507f62ffc342bbf70f3ecb24e96d286526aa23040a13a1f4761059b1a57f3d2bed9b14caef0bde53dd440584ef9fea6947ff18cfdae7498b028beefa9ec5 + checksum: c9b04d9be26357d493475564134a9d3e03bb9291ccc5ff3209b143c6e5495123fcaa4af92ec030bd8959ede9c45182f8bf9aa1d6502a1603f9d4a08d5e6e27b5 languageName: node linkType: hard @@ -1088,42 +1091,42 @@ __metadata: languageName: node linkType: hard -"@aws-sdk/middleware-signing@npm:3.451.0": - version: 3.451.0 - resolution: "@aws-sdk/middleware-signing@npm:3.451.0" +"@aws-sdk/middleware-signing@npm:3.461.0": + version: 3.461.0 + resolution: "@aws-sdk/middleware-signing@npm:3.461.0" dependencies: - "@aws-sdk/types": 3.451.0 + "@aws-sdk/types": 3.460.0 "@smithy/property-provider": ^2.0.0 "@smithy/protocol-http": ^3.0.9 "@smithy/signature-v4": ^2.0.0 "@smithy/types": ^2.5.0 "@smithy/util-middleware": ^2.0.6 tslib: ^2.5.0 - checksum: 6a2900bdad76733fec5155af97f9f2b3448451bf15357d0dfbc35f646a321120dc6b5d5e3c5690b0bfc5294044c139ce28b3a07c149f33dd9572399f5ae0999d + checksum: 5a202e6fe8f9482aecc455843d5b97d42198cb6e98d6b739eff6a769742220f925876981b67c0d15963fd59b9a4a79d374505d28ac4f33be8ac9599908dbe528 languageName: node linkType: hard -"@aws-sdk/middleware-ssec@npm:3.451.0": - version: 3.451.0 - resolution: "@aws-sdk/middleware-ssec@npm:3.451.0" +"@aws-sdk/middleware-ssec@npm:3.460.0": + version: 3.460.0 + resolution: "@aws-sdk/middleware-ssec@npm:3.460.0" dependencies: - "@aws-sdk/types": 3.451.0 + "@aws-sdk/types": 3.460.0 "@smithy/types": ^2.5.0 tslib: ^2.5.0 - checksum: f9666c4aba9085eb6257e49636e0d4eae61ce9be1411965cb2212a0ce1bf63f89365db5b4710a9154e1001dba8ac0be09a46d6e38c13f6b9dc2b73b8a4efdce4 + checksum: 48cf9b1c5a1f17dca62c448335a6191b592435522754b8edfc52ee281a47c951703af5cd8bb235a8ca8d51e0571e74c9770d5b418f2273fa3638611196b6166d languageName: node linkType: hard -"@aws-sdk/middleware-user-agent@npm:3.451.0": - version: 3.451.0 - resolution: "@aws-sdk/middleware-user-agent@npm:3.451.0" +"@aws-sdk/middleware-user-agent@npm:3.460.0": + version: 3.460.0 + resolution: "@aws-sdk/middleware-user-agent@npm:3.460.0" dependencies: - "@aws-sdk/types": 3.451.0 - "@aws-sdk/util-endpoints": 3.451.0 + "@aws-sdk/types": 3.460.0 + "@aws-sdk/util-endpoints": 3.460.0 "@smithy/protocol-http": ^3.0.9 "@smithy/types": ^2.5.0 tslib: ^2.5.0 - checksum: d547fb999a6f657d8806165102b57a082a4e5464666c928148292e233be38a53209c1b9a39eac7e0c403a77e0336159b9a72e89daad86365306421058b0c4f92 + checksum: a65daeece1071de6fe6e3ce3b9426905febda16906236f982bfc09b57d0e1837e1b47a7467b9b9f69fcf900938d29b030380a38fc76d4dde4147dca4b571f54c languageName: node linkType: hard @@ -1184,16 +1187,17 @@ __metadata: languageName: node linkType: hard -"@aws-sdk/signature-v4-multi-region@npm:3.451.0": - version: 3.451.0 - resolution: "@aws-sdk/signature-v4-multi-region@npm:3.451.0" +"@aws-sdk/signature-v4-multi-region@npm:3.461.0": + version: 3.461.0 + resolution: "@aws-sdk/signature-v4-multi-region@npm:3.461.0" dependencies: - "@aws-sdk/types": 3.451.0 + "@aws-sdk/middleware-sdk-s3": 3.461.0 + "@aws-sdk/types": 3.460.0 "@smithy/protocol-http": ^3.0.9 "@smithy/signature-v4": ^2.0.0 "@smithy/types": ^2.5.0 tslib: ^2.5.0 - checksum: d0c9e434e28c4c06a78d6d12ebe0067bf4d299b73e106ef336dd730a63941dcdb443147a998603c0f64ec8a1a9f53c0b8ad7f0e67fe36959c154c1dcf664a485 + checksum: f0fe24b52e57950c5930fb5aec446d6f82f1ef42b9d48c50a92d47bd70569cac99d0927069be6a23e28d760028a9a00ea5eaf542452a1899ecb50150afbad98d languageName: node linkType: hard @@ -1213,21 +1217,21 @@ __metadata: languageName: node linkType: hard -"@aws-sdk/token-providers@npm:3.451.0": - version: 3.451.0 - resolution: "@aws-sdk/token-providers@npm:3.451.0" +"@aws-sdk/token-providers@npm:3.460.0": + version: 3.460.0 + resolution: "@aws-sdk/token-providers@npm:3.460.0" dependencies: "@aws-crypto/sha256-browser": 3.0.0 "@aws-crypto/sha256-js": 3.0.0 - "@aws-sdk/middleware-host-header": 3.451.0 - "@aws-sdk/middleware-logger": 3.451.0 - "@aws-sdk/middleware-recursion-detection": 3.451.0 - "@aws-sdk/middleware-user-agent": 3.451.0 + "@aws-sdk/middleware-host-header": 3.460.0 + "@aws-sdk/middleware-logger": 3.460.0 + "@aws-sdk/middleware-recursion-detection": 3.460.0 + "@aws-sdk/middleware-user-agent": 3.460.0 "@aws-sdk/region-config-resolver": 3.451.0 - "@aws-sdk/types": 3.451.0 - "@aws-sdk/util-endpoints": 3.451.0 - "@aws-sdk/util-user-agent-browser": 3.451.0 - "@aws-sdk/util-user-agent-node": 3.451.0 + "@aws-sdk/types": 3.460.0 + "@aws-sdk/util-endpoints": 3.460.0 + "@aws-sdk/util-user-agent-browser": 3.460.0 + "@aws-sdk/util-user-agent-node": 3.460.0 "@smithy/config-resolver": ^2.0.18 "@smithy/fetch-http-handler": ^2.2.6 "@smithy/hash-node": ^2.0.15 @@ -1254,7 +1258,7 @@ __metadata: "@smithy/util-retry": ^2.0.6 "@smithy/util-utf8": ^2.0.2 tslib: ^2.5.0 - checksum: 1c2d3927e33eedcfc0482744f065c44f632d13fb7a03152dd4d5b04546ab06fed6373f2e8432caf332c0ab93ac9a64bf88cc5071702f6f1adacc8aed53ed3e3b + checksum: f459533489ef191afae6a7bb5cf6676ec4685b382880e6058c8861a7e8f45931aa4c323dfac5f17c45f390fc6997250e3b7063eb58054eae1c7919e3b940cdb2 languageName: node linkType: hard @@ -1268,13 +1272,13 @@ __metadata: languageName: node linkType: hard -"@aws-sdk/types@npm:3.451.0, @aws-sdk/types@npm:^3.222.0, @aws-sdk/types@npm:^3.347.0": - version: 3.451.0 - resolution: "@aws-sdk/types@npm:3.451.0" +"@aws-sdk/types@npm:3.460.0, @aws-sdk/types@npm:^3.222.0, @aws-sdk/types@npm:^3.347.0": + version: 3.460.0 + resolution: "@aws-sdk/types@npm:3.460.0" dependencies: "@smithy/types": ^2.5.0 tslib: ^2.5.0 - checksum: 0f66eccf707ece1f21af6c8099a6b13191a119f48dacebd8794d74263628b95dcf0bfa479493ec1774c902fe7bb8867cfcbd1cf7d908653fe0e0759168970d19 + checksum: 6e3b367bebb2840a7e6efca2bbf74afeea4ce6df2a96095f1641bf0424838ae9fd12da6cbe669f4f58244963e61f38216f7f80709feb71a521d68fbb17b18f4c languageName: node linkType: hard @@ -1308,14 +1312,14 @@ __metadata: languageName: node linkType: hard -"@aws-sdk/util-endpoints@npm:3.451.0": - version: 3.451.0 - resolution: "@aws-sdk/util-endpoints@npm:3.451.0" +"@aws-sdk/util-endpoints@npm:3.460.0": + version: 3.460.0 + resolution: "@aws-sdk/util-endpoints@npm:3.460.0" dependencies: - "@aws-sdk/types": 3.451.0 + "@aws-sdk/types": 3.460.0 "@smithy/util-endpoints": ^1.0.4 tslib: ^2.5.0 - checksum: ebb558cadb896754f2f0078b31720441bf8d8f4735372950a00c677b2db1a9076a3aefa3c14148e39708b593f148a12930e1b29ecce6e4ed653ea10ab26f3c80 + checksum: 4b0d3c4fe38a9c28b38ad4d575d600d1a29de7b829ac579a674f79b058473467dd0f31afbae03dd13c453fee48f3decb026259714051faf54161429a3b713124 languageName: node linkType: hard @@ -1367,23 +1371,23 @@ __metadata: languageName: node linkType: hard -"@aws-sdk/util-user-agent-browser@npm:3.451.0": - version: 3.451.0 - resolution: "@aws-sdk/util-user-agent-browser@npm:3.451.0" +"@aws-sdk/util-user-agent-browser@npm:3.460.0": + version: 3.460.0 + resolution: "@aws-sdk/util-user-agent-browser@npm:3.460.0" dependencies: - "@aws-sdk/types": 3.451.0 + "@aws-sdk/types": 3.460.0 "@smithy/types": ^2.5.0 bowser: ^2.11.0 tslib: ^2.5.0 - checksum: 83e6a74aca3575b385db09787021bbd40589afdff0fcad1d4ea33c7a4711f3147d61fa6465a629838d8d093609d43097bedb4bfc6e12e3695179f4c244b691e3 + checksum: 6cdbf7c0159ce0d74964ac6c751fc256084c1388720cb4f70c1a2602762270fb2f04e8ff7dc187b2975248a965f1834adfd1bc74a2769bbde7722bbf04773764 languageName: node linkType: hard -"@aws-sdk/util-user-agent-node@npm:3.451.0": - version: 3.451.0 - resolution: "@aws-sdk/util-user-agent-node@npm:3.451.0" +"@aws-sdk/util-user-agent-node@npm:3.460.0": + version: 3.460.0 + resolution: "@aws-sdk/util-user-agent-node@npm:3.460.0" dependencies: - "@aws-sdk/types": 3.451.0 + "@aws-sdk/types": 3.460.0 "@smithy/node-config-provider": ^2.1.5 "@smithy/types": ^2.5.0 tslib: ^2.5.0 @@ -1392,7 +1396,7 @@ __metadata: peerDependenciesMeta: aws-crt: optional: true - checksum: 94ccacf05776b558f965d8081602435565be59a62e0ec61946a49c2519f742ae2552df407b9ba9b37d8cbac1ef8ea5538bb72c96bc1e1c55ab9ecb6be187fe67 + checksum: 12eb5ae7e6fd0d60d188ae24f2eba746fdf9a612cc28d317c4a2762b0dc8ef4427ff82def11c964600f329edd6b14076bc6d4d8a65877ca0c42ebacfcb34c5f7 languageName: node linkType: hard @@ -1434,17 +1438,17 @@ __metadata: linkType: hard "@azure/arm-appservice@npm:^14.0.0": - version: 14.0.0 - resolution: "@azure/arm-appservice@npm:14.0.0" + version: 14.1.0 + resolution: "@azure/arm-appservice@npm:14.1.0" dependencies: "@azure/abort-controller": ^1.0.0 "@azure/core-auth": ^1.3.0 "@azure/core-client": ^1.7.0 - "@azure/core-lro": ^2.5.0 + "@azure/core-lro": ^2.5.4 "@azure/core-paging": ^1.2.0 - "@azure/core-rest-pipeline": ^1.8.0 + "@azure/core-rest-pipeline": ^1.12.0 tslib: ^2.2.0 - checksum: a34776202f28808237a00958e6498f74547ed61cb284b681ac69fbd62fc9c174b12570ac8a02224162d4c9a2f066736ca40c4e31d12098fa630f9a9eef87d8ab + checksum: b989bc26d94056e0f02d2ede9981ebbf90ebefdc8b35c8f66531d3220c4ce52484d49c0a15b7056b79898a76533b0de0edf141b7736aadfabf2c33305804b884 languageName: node linkType: hard @@ -1508,7 +1512,7 @@ __metadata: languageName: node linkType: hard -"@azure/core-lro@npm:^2.2.0, @azure/core-lro@npm:^2.5.0": +"@azure/core-lro@npm:^2.2.0, @azure/core-lro@npm:^2.5.4": version: 2.5.4 resolution: "@azure/core-lro@npm:2.5.4" dependencies: @@ -1529,21 +1533,20 @@ __metadata: languageName: node linkType: hard -"@azure/core-rest-pipeline@npm:^1.1.0, @azure/core-rest-pipeline@npm:^1.8.0, @azure/core-rest-pipeline@npm:^1.9.1": - version: 1.9.2 - resolution: "@azure/core-rest-pipeline@npm:1.9.2" +"@azure/core-rest-pipeline@npm:^1.1.0, @azure/core-rest-pipeline@npm:^1.12.0, @azure/core-rest-pipeline@npm:^1.9.1": + version: 1.12.2 + resolution: "@azure/core-rest-pipeline@npm:1.12.2" dependencies: "@azure/abort-controller": ^1.0.0 "@azure/core-auth": ^1.4.0 "@azure/core-tracing": ^1.0.1 - "@azure/core-util": ^1.0.0 + "@azure/core-util": ^1.3.0 "@azure/logger": ^1.0.0 form-data: ^4.0.0 http-proxy-agent: ^5.0.0 https-proxy-agent: ^5.0.0 tslib: ^2.2.0 - uuid: ^8.3.0 - checksum: cdfdaf8bdb0778f40ecfeb8b5da0b4f8643d641b75bb5869485afd38f7c3127ab088ac2fe2980df03d28fb6715928e04560343ee6416246aebe75443c4dd2906 + checksum: a774ab2e87ff56d7f3ef1844a814bbd76264ea1c33534081d38ce6df1098d803d0b13b15912f9053d0695ca2669b0dd104c8feec8dbf24df80a3608ea784fc9e languageName: node linkType: hard @@ -1566,7 +1569,7 @@ __metadata: languageName: node linkType: hard -"@azure/core-util@npm:^1.0.0, @azure/core-util@npm:^1.1.0, @azure/core-util@npm:^1.1.1, @azure/core-util@npm:^1.2.0, @azure/core-util@npm:^1.6.1": +"@azure/core-util@npm:^1.0.0, @azure/core-util@npm:^1.1.0, @azure/core-util@npm:^1.1.1, @azure/core-util@npm:^1.2.0, @azure/core-util@npm:^1.3.0, @azure/core-util@npm:^1.6.1": version: 1.6.1 resolution: "@azure/core-util@npm:1.6.1" dependencies: @@ -3172,12 +3175,12 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.10.1, @babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.13.10, @babel/runtime@npm:^7.15.4, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.18.6, @babel/runtime@npm:^7.2.0, @babel/runtime@npm:^7.20.1, @babel/runtime@npm:^7.20.13, @babel/runtime@npm:^7.20.6, @babel/runtime@npm:^7.21.0, @babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.4.4, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.6.0, @babel/runtime@npm:^7.7.6, @babel/runtime@npm:^7.8.3, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7, @babel/runtime@npm:^7.9.2": - version: 7.23.2 - resolution: "@babel/runtime@npm:7.23.2" +"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.10.1, @babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.13.10, @babel/runtime@npm:^7.15.4, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.18.6, @babel/runtime@npm:^7.2.0, @babel/runtime@npm:^7.20.1, @babel/runtime@npm:^7.20.13, @babel/runtime@npm:^7.20.6, @babel/runtime@npm:^7.21.0, @babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.23.4, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.4.4, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.6.0, @babel/runtime@npm:^7.7.6, @babel/runtime@npm:^7.8.3, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7, @babel/runtime@npm:^7.9.2": + version: 7.23.5 + resolution: "@babel/runtime@npm:7.23.5" dependencies: regenerator-runtime: ^0.14.0 - checksum: 6c4df4839ec75ca10175f636d6362f91df8a3137f86b38f6cd3a4c90668a0fe8e9281d320958f4fbd43b394988958585a17c3aab2a4ea6bf7316b22916a371fb + checksum: 164d9802424f06908e62d29b8fd3a87db55accf82f46f964ac481dcead11ff7df8391e3696e5fa91a8ca10ea8845bf650acd730fa88cf13f8026cd8d5eec6936 languageName: node linkType: hard @@ -3319,7 +3322,7 @@ __metadata: "@kubernetes/client-node": 0.20.0 "@manypkg/get-packages": ^1.1.3 "@octokit/rest": ^19.0.3 - "@types/archiver": ^5.1.0 + "@types/archiver": ^6.0.0 "@types/base64-stream": ^1.0.2 "@types/compression": ^1.7.0 "@types/concat-stream": ^2.0.0 @@ -3334,7 +3337,7 @@ __metadata: "@types/tar": ^6.1.1 "@types/webpack-env": ^1.15.2 "@types/yauzl": ^2.10.0 - archiver: ^5.0.2 + archiver: ^6.0.0 aws-sdk-client-mock: ^3.0.0 base64-stream: ^1.0.0 better-sqlite3: ^9.0.0 @@ -3521,7 +3524,18 @@ __metadata: languageName: unknown linkType: soft -"@backstage/catalog-client@^1.4.6, @backstage/catalog-client@workspace:^, @backstage/catalog-client@workspace:packages/catalog-client": +"@backstage/catalog-client@npm:^1.4.6": + version: 1.4.6 + resolution: "@backstage/catalog-client@npm:1.4.6" + dependencies: + "@backstage/catalog-model": ^1.4.3 + "@backstage/errors": ^1.2.3 + cross-fetch: ^4.0.0 + checksum: c62b479ea8865c23046f742d75db5fb36827627e78cfdff64e713eb72abd11fef071b8150c07e695e5e6d879d257a8a9dc85ec132b7c64229c6a0bd2ef821200 + languageName: node + linkType: hard + +"@backstage/catalog-client@workspace:^, @backstage/catalog-client@workspace:packages/catalog-client": version: 0.0.0-use.local resolution: "@backstage/catalog-client@workspace:packages/catalog-client" dependencies: @@ -3530,6 +3544,7 @@ __metadata: "@backstage/errors": "workspace:^" cross-fetch: ^4.0.0 msw: ^1.0.0 + uri-template: ^2.0.0 languageName: unknown linkType: soft @@ -3603,9 +3618,9 @@ __metadata: "@octokit/oauth-app": ^4.2.0 "@octokit/request": ^6.0.0 "@pmmmwh/react-refresh-webpack-plugin": ^0.5.7 - "@rollup/plugin-commonjs": ^23.0.0 - "@rollup/plugin-json": ^5.0.0 - "@rollup/plugin-node-resolve": ^13.0.6 + "@rollup/plugin-commonjs": ^25.0.0 + "@rollup/plugin-json": ^6.0.0 + "@rollup/plugin-node-resolve": ^15.0.0 "@rollup/plugin-yaml": ^4.0.0 "@spotify/eslint-config-base": ^14.0.0 "@spotify/eslint-config-react": ^14.0.0 @@ -3638,7 +3653,7 @@ __metadata: "@types/terser-webpack-plugin": ^5.0.4 "@types/webpack-env": ^1.15.2 "@types/yarnpkg__lockfile": ^1.1.4 - "@typescript-eslint/eslint-plugin": 6.11.0 + "@typescript-eslint/eslint-plugin": ^6.12.0 "@typescript-eslint/parser": ^6.7.2 "@yarnpkg/lockfile": ^1.1.0 "@yarnpkg/parsers": ^3.0.0-rc.4 @@ -3664,6 +3679,7 @@ __metadata: eslint-plugin-jsx-a11y: ^6.5.1 eslint-plugin-react: ^7.28.0 eslint-plugin-react-hooks: ^4.3.0 + eslint-plugin-unused-imports: ^3.0.0 eslint-webpack-plugin: ^3.1.1 express: ^4.17.1 fork-ts-checker-webpack-plugin: ^7.0.0-alpha.8 @@ -3720,7 +3736,7 @@ __metadata: "@vitejs/plugin-react": ^4.0.4 vite: ^4.4.9 vite-plugin-html: ^3.2.0 - vite-plugin-node-polyfills: ^0.16.0 + vite-plugin-node-polyfills: ^0.17.0 peerDependenciesMeta: "@vitejs/plugin-react": optional: true @@ -3900,7 +3916,7 @@ __metadata: languageName: node linkType: hard -"@backstage/core-components@npm:^0.13.7, @backstage/core-components@npm:^0.13.8": +"@backstage/core-components@npm:^0.13.8": version: 0.13.8 resolution: "@backstage/core-components@npm:0.13.8" dependencies: @@ -3970,7 +3986,7 @@ __metadata: "@material-ui/core": ^4.12.2 "@material-ui/icons": ^4.9.1 "@material-ui/lab": 4.0.0-alpha.61 - "@react-hookz/web": ^20.0.0 + "@react-hookz/web": ^23.0.0 "@testing-library/dom": ^9.0.0 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 @@ -4000,7 +4016,7 @@ __metadata: history: ^5.0.0 immer: ^9.0.1 linkify-react: 4.1.3 - linkifyjs: 4.1.2 + linkifyjs: 4.1.3 lodash: ^4.17.21 msw: ^1.0.0 pluralize: ^8.0.0 @@ -4026,7 +4042,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/core-plugin-api@npm:^1.3.0, @backstage/core-plugin-api@npm:^1.5.0, @backstage/core-plugin-api@npm:^1.7.0, @backstage/core-plugin-api@npm:^1.8.0": +"@backstage/core-plugin-api@npm:^1.3.0, @backstage/core-plugin-api@npm:^1.5.0, @backstage/core-plugin-api@npm:^1.8.0": version: 1.8.0 resolution: "@backstage/core-plugin-api@npm:1.8.0" dependencies: @@ -4271,7 +4287,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/integration-react@npm:^1.1.20, @backstage/integration-react@npm:^1.1.21": +"@backstage/integration-react@npm:^1.1.21": version: 1.1.21 resolution: "@backstage/integration-react@npm:1.1.21" dependencies: @@ -4759,7 +4775,7 @@ __metadata: "@backstage/plugin-auth-backend": "workspace:^" "@backstage/plugin-auth-node": "workspace:^" express: ^4.18.2 - passport: ^0.6.0 + passport: ^0.7.0 passport-atlassian-oauth2: ^2.1.0 supertest: ^6.3.3 languageName: unknown @@ -4810,7 +4826,7 @@ __metadata: "@backstage/plugin-auth-backend": "workspace:^" "@backstage/plugin-auth-node": "workspace:^" express: ^4.18.2 - passport: ^0.6.0 + passport: ^0.7.0 passport-gitlab2: ^5.0.0 supertest: ^6.3.3 languageName: unknown @@ -4850,7 +4866,7 @@ __metadata: jose: ^4.6.0 msw: ^1.0.0 node-fetch: ^2.6.7 - passport: ^0.6.0 + passport: ^0.7.0 passport-microsoft: ^1.0.0 supertest: ^6.3.3 languageName: unknown @@ -4868,12 +4884,26 @@ __metadata: "@backstage/plugin-auth-backend": "workspace:^" "@backstage/plugin-auth-node": "workspace:^" express: ^4.18.2 - passport: ^0.6.0 + passport: ^0.7.0 passport-oauth2: ^1.6.1 supertest: ^6.3.3 languageName: unknown linkType: soft +"@backstage/plugin-auth-backend-module-oauth2-proxy-provider@workspace:^, @backstage/plugin-auth-backend-module-oauth2-proxy-provider@workspace:plugins/auth-backend-module-oauth2-proxy-provider": + version: 0.0.0-use.local + resolution: "@backstage/plugin-auth-backend-module-oauth2-proxy-provider@workspace:plugins/auth-backend-module-oauth2-proxy-provider" + dependencies: + "@backstage/backend-common": "workspace:^" + "@backstage/backend-plugin-api": "workspace:^" + "@backstage/backend-test-utils": "workspace:^" + "@backstage/cli": "workspace:^" + "@backstage/errors": "workspace:^" + "@backstage/plugin-auth-node": "workspace:^" + jose: ^4.6.0 + languageName: unknown + linkType: soft + "@backstage/plugin-auth-backend-module-okta-provider@workspace:^, @backstage/plugin-auth-backend-module-okta-provider@workspace:plugins/auth-backend-module-okta-provider": version: 0.0.0-use.local resolution: "@backstage/plugin-auth-backend-module-okta-provider@workspace:plugins/auth-backend-module-okta-provider" @@ -4887,7 +4917,7 @@ __metadata: "@backstage/plugin-auth-node": "workspace:^" "@davidzemon/passport-okta-oauth": ^0.0.5 express: ^4.18.2 - passport: ^0.6.0 + passport: ^0.7.0 supertest: ^6.3.3 languageName: unknown linkType: soft @@ -4912,7 +4942,7 @@ __metadata: luxon: ^3.4.3 msw: ^1.3.0 openid-client: ^5.4.3 - passport: ^0.6.0 + passport: ^0.7.0 supertest: ^6.3.3 languageName: unknown linkType: soft @@ -4958,6 +4988,7 @@ __metadata: "@backstage/plugin-auth-backend-module-gitlab-provider": "workspace:^" "@backstage/plugin-auth-backend-module-google-provider": "workspace:^" "@backstage/plugin-auth-backend-module-oauth2-provider": "workspace:^" + "@backstage/plugin-auth-backend-module-oauth2-proxy-provider": "workspace:^" "@backstage/plugin-auth-backend-module-okta-provider": "workspace:^" "@backstage/plugin-auth-node": "workspace:^" "@backstage/plugin-catalog-node": "workspace:^" @@ -4996,7 +5027,7 @@ __metadata: node-cache: ^5.1.2 node-fetch: ^2.6.7 openid-client: ^5.2.1 - passport: ^0.6.0 + passport: ^0.7.0 passport-auth0: ^1.4.3 passport-bitbucket-oauth2: ^0.1.2 passport-github2: ^0.1.12 @@ -5035,7 +5066,7 @@ __metadata: lodash: ^4.17.21 msw: ^1.0.0 node-fetch: ^2.6.7 - passport: ^0.6.0 + passport: ^0.7.0 supertest: ^6.1.3 uuid: ^8.0.0 winston: ^3.2.1 @@ -5050,14 +5081,19 @@ __metadata: dependencies: "@backstage/backend-common": "workspace:^" "@backstage/backend-plugin-api": "workspace:^" + "@backstage/catalog-model": "workspace:^" "@backstage/cli": "workspace:^" "@backstage/config": "workspace:^" + "@backstage/integration": "workspace:^" "@backstage/plugin-azure-devops-common": "workspace:^" + "@backstage/plugin-catalog-common": "workspace:^" + "@backstage/plugin-catalog-node": "workspace:^" "@types/express": ^4.17.6 "@types/supertest": ^2.0.8 azure-devops-node-api: ^11.0.1 express: ^4.17.1 express-promise-router: ^4.1.0 + lodash: ^4.17.21 mime-types: ^2.1.27 msw: ^1.0.0 p-limit: ^3.1.0 @@ -5776,7 +5812,7 @@ __metadata: "@types/core-js": ^2.5.4 "@types/express": ^4.17.6 "@types/git-url-parse": ^9.0.0 - "@types/glob": ^7.1.1 + "@types/glob": ^8.0.0 "@types/lodash": ^4.14.151 "@types/supertest": ^2.0.8 "@types/uuid": ^8.0.0 @@ -5908,11 +5944,13 @@ __metadata: "@backstage/cli": "workspace:^" "@backstage/errors": "workspace:^" "@backstage/plugin-catalog-common": "workspace:^" + "@backstage/plugin-permission-common": "workspace:^" + "@backstage/plugin-permission-node": "workspace:^" "@backstage/types": "workspace:^" languageName: unknown linkType: soft -"@backstage/plugin-catalog-react@npm:^1.2.4, @backstage/plugin-catalog-react@npm:^1.8.5": +"@backstage/plugin-catalog-react@npm:^1.2.4, @backstage/plugin-catalog-react@npm:^1.9.1": version: 1.9.1 resolution: "@backstage/plugin-catalog-react@npm:1.9.1" dependencies: @@ -6119,7 +6157,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/plugin-circleci@workspace:^, @backstage/plugin-circleci@workspace:plugins/circleci": +"@backstage/plugin-circleci@workspace:plugins/circleci": version: 0.0.0-use.local resolution: "@backstage/plugin-circleci@workspace:plugins/circleci" dependencies: @@ -6608,7 +6646,7 @@ __metadata: "@material-ui/core": ^4.12.2 "@material-ui/icons": ^4.9.1 "@material-ui/lab": 4.0.0-alpha.61 - "@react-hookz/web": ^20.0.0 + "@react-hookz/web": ^23.0.0 "@testing-library/dom": ^9.0.0 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 @@ -6970,7 +7008,7 @@ __metadata: "@material-ui/core": ^4.12.2 "@material-ui/icons": ^4.9.1 "@material-ui/lab": 4.0.0-alpha.61 - "@react-hookz/web": ^20.0.0 + "@react-hookz/web": ^23.0.0 "@testing-library/dom": ^9.0.0 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 @@ -7287,7 +7325,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/plugin-home-react@npm:^0.1.4": +"@backstage/plugin-home-react@npm:^0.1.5": version: 0.1.5 resolution: "@backstage/plugin-home-react@npm:0.1.5" dependencies: @@ -7317,7 +7355,7 @@ __metadata: "@backstage/test-utils": "workspace:^" "@material-ui/core": ^4.12.2 "@material-ui/icons": ^4.9.1 - "@rjsf/utils": 5.14.2 + "@rjsf/utils": 5.14.3 "@testing-library/dom": ^9.0.0 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 @@ -7354,10 +7392,10 @@ __metadata: "@material-ui/core": ^4.12.2 "@material-ui/icons": ^4.9.1 "@material-ui/lab": 4.0.0-alpha.61 - "@rjsf/core": 5.14.2 - "@rjsf/material-ui": 5.14.2 - "@rjsf/utils": 5.14.2 - "@rjsf/validator-ajv8": 5.14.2 + "@rjsf/core": 5.14.3 + "@rjsf/material-ui": 5.14.3 + "@rjsf/utils": 5.14.3 + "@rjsf/validator-ajv8": 5.14.3 "@testing-library/dom": ^9.0.0 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 @@ -8659,10 +8697,14 @@ __metadata: version: 0.0.0-use.local resolution: "@backstage/plugin-scaffolder-backend-module-sentry@workspace:plugins/scaffolder-backend-module-sentry" dependencies: + "@backstage/backend-test-utils": "workspace:^" "@backstage/cli": "workspace:^" "@backstage/config": "workspace:^" "@backstage/errors": "workspace:^" "@backstage/plugin-scaffolder-node": "workspace:^" + "@backstage/types": "workspace:^" + msw: ^2.0.0 + yaml: ^2.3.3 languageName: unknown linkType: soft @@ -8809,11 +8851,11 @@ __metadata: "@material-ui/core": ^4.12.2 "@material-ui/icons": ^4.9.1 "@material-ui/lab": 4.0.0-alpha.61 - "@react-hookz/web": ^20.0.0 - "@rjsf/core": 5.14.2 - "@rjsf/material-ui": 5.14.2 - "@rjsf/utils": 5.14.2 - "@rjsf/validator-ajv8": 5.14.2 + "@react-hookz/web": ^23.0.0 + "@rjsf/core": 5.14.3 + "@rjsf/material-ui": 5.14.3 + "@rjsf/utils": 5.14.3 + "@rjsf/validator-ajv8": 5.14.3 "@testing-library/dom": ^9.0.0 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 @@ -8872,11 +8914,11 @@ __metadata: "@material-ui/core": ^4.12.2 "@material-ui/icons": ^4.9.1 "@material-ui/lab": 4.0.0-alpha.61 - "@react-hookz/web": ^20.0.0 - "@rjsf/core": 5.14.2 - "@rjsf/material-ui": 5.14.2 - "@rjsf/utils": 5.14.2 - "@rjsf/validator-ajv8": 5.14.2 + "@react-hookz/web": ^23.0.0 + "@rjsf/core": 5.14.3 + "@rjsf/material-ui": 5.14.3 + "@rjsf/utils": 5.14.3 + "@rjsf/validator-ajv8": 5.14.3 "@testing-library/dom": ^9.0.0 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 @@ -9551,6 +9593,7 @@ __metadata: "@backstage/core-plugin-api": "workspace:^" "@backstage/dev-utils": "workspace:^" "@backstage/frontend-plugin-api": "workspace:^" + "@backstage/frontend-test-utils": "workspace:^" "@backstage/test-utils": "workspace:^" "@backstage/theme": "workspace:^" "@material-ui/core": ^4.12.2 @@ -9663,7 +9706,7 @@ __metadata: "@material-ui/core": ^4.12.2 "@material-ui/icons": ^4.9.1 "@material-ui/lab": 4.0.0-alpha.61 - "@react-hookz/web": ^20.0.0 + "@react-hookz/web": ^23.0.0 "@testing-library/dom": ^9.0.0 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 @@ -10049,13 +10092,14 @@ __metadata: "@manypkg/get-packages": ^1.1.3 "@microsoft/api-documenter": ^7.22.33 "@microsoft/api-extractor": ^7.36.4 + "@openapitools/openapi-generator-cli": ^2.7.0 "@stoplight/spectral-core": ^1.18.0 "@stoplight/spectral-formatters": ^1.1.0 "@stoplight/spectral-functions": ^1.7.2 "@stoplight/spectral-parsers": ^1.0.2 "@stoplight/spectral-rulesets": ^1.18.0 "@stoplight/spectral-runtime": ^1.1.2 - "@stoplight/types": ^13.14.0 + "@stoplight/types": ^14.0.0 "@types/is-glob": ^4.0.2 "@types/mock-fs": ^4.13.0 "@types/node": ^18.17.8 @@ -10129,7 +10173,7 @@ __metadata: languageName: node linkType: hard -"@backstage/theme@npm:^0.4.3, @backstage/theme@npm:^0.4.4": +"@backstage/theme@npm:^0.4.4": version: 0.4.4 resolution: "@backstage/theme@npm:0.4.4" dependencies: @@ -10239,15 +10283,15 @@ __metadata: languageName: node linkType: hard -"@changesets/apply-release-plan@npm:^6.1.4": - version: 6.1.4 - resolution: "@changesets/apply-release-plan@npm:6.1.4" +"@changesets/apply-release-plan@npm:^7.0.0": + version: 7.0.0 + resolution: "@changesets/apply-release-plan@npm:7.0.0" dependencies: "@babel/runtime": ^7.20.1 - "@changesets/config": ^2.3.1 - "@changesets/get-version-range-type": ^0.3.2 - "@changesets/git": ^2.0.0 - "@changesets/types": ^5.2.1 + "@changesets/config": ^3.0.0 + "@changesets/get-version-range-type": ^0.4.0 + "@changesets/git": ^3.0.0 + "@changesets/types": ^6.0.0 "@manypkg/get-packages": ^1.1.3 detect-indent: ^6.0.0 fs-extra: ^7.0.1 @@ -10256,61 +10300,60 @@ __metadata: prettier: ^2.7.1 resolve-from: ^5.0.0 semver: ^7.5.3 - checksum: d386aee70c5483c97d964c6fa1191878005b7050d34b2e1e4a1ad66d9ad44f8f20d1c884e01e770b954bd2d4364f935510e53ae896212669f67e5c37b2a610c7 + checksum: ad83f89a3d46cd5249fa960cb0324114532bd5f25e74466d181afd6661273824859d038a12ba587a5e044f9169810e4a6febbb61e23c3819b3b28c00176a8bdf languageName: node linkType: hard -"@changesets/assemble-release-plan@npm:^5.2.4": - version: 5.2.4 - resolution: "@changesets/assemble-release-plan@npm:5.2.4" +"@changesets/assemble-release-plan@npm:^6.0.0": + version: 6.0.0 + resolution: "@changesets/assemble-release-plan@npm:6.0.0" dependencies: "@babel/runtime": ^7.20.1 - "@changesets/errors": ^0.1.4 - "@changesets/get-dependents-graph": ^1.3.6 - "@changesets/types": ^5.2.1 + "@changesets/errors": ^0.2.0 + "@changesets/get-dependents-graph": ^2.0.0 + "@changesets/types": ^6.0.0 "@manypkg/get-packages": ^1.1.3 semver: ^7.5.3 - checksum: 32f443a0afec3d5a4afc68c8de32e8ff88531ea24976b50583b1d6870d71cec2729f27952af82854eb54e2ad0a619872d211d654c596ee0eb42c83ab54ad15ae + checksum: 0e6d25f25e0e3cc0e92aa8c43f5f496bae9464e2523be4ff81e31b6c9971b63bb1264821a2483c48d451d89d60af1acebe727e7f8c392ed48188a3ff26d0950e languageName: node linkType: hard -"@changesets/changelog-git@npm:^0.1.14": - version: 0.1.14 - resolution: "@changesets/changelog-git@npm:0.1.14" +"@changesets/changelog-git@npm:^0.2.0": + version: 0.2.0 + resolution: "@changesets/changelog-git@npm:0.2.0" dependencies: - "@changesets/types": ^5.2.1 - checksum: 60b45bb899e66cec669ab3884d5d18550cd30bf5a8b06f335eb72aa6c9e018dd3e0187e4df61c91a22076153e346b735b792f0e9c6186e6245b1b7aec2fc42d4 + "@changesets/types": ^6.0.0 + checksum: 132660f7fdabbdda00ac803cc822d6427a1a38a17a5f414e87ad32f6dc4cbef5280a147ecdc087a28dc06c8bd0762f8d6e7132d01b8a4142b59fbe1bc2177034 languageName: node linkType: hard "@changesets/cli@npm:^2.14.0": - version: 2.26.2 - resolution: "@changesets/cli@npm:2.26.2" + version: 2.27.1 + resolution: "@changesets/cli@npm:2.27.1" dependencies: "@babel/runtime": ^7.20.1 - "@changesets/apply-release-plan": ^6.1.4 - "@changesets/assemble-release-plan": ^5.2.4 - "@changesets/changelog-git": ^0.1.14 - "@changesets/config": ^2.3.1 - "@changesets/errors": ^0.1.4 - "@changesets/get-dependents-graph": ^1.3.6 - "@changesets/get-release-plan": ^3.0.17 - "@changesets/git": ^2.0.0 - "@changesets/logger": ^0.0.5 - "@changesets/pre": ^1.0.14 - "@changesets/read": ^0.5.9 - "@changesets/types": ^5.2.1 - "@changesets/write": ^0.2.3 + "@changesets/apply-release-plan": ^7.0.0 + "@changesets/assemble-release-plan": ^6.0.0 + "@changesets/changelog-git": ^0.2.0 + "@changesets/config": ^3.0.0 + "@changesets/errors": ^0.2.0 + "@changesets/get-dependents-graph": ^2.0.0 + "@changesets/get-release-plan": ^4.0.0 + "@changesets/git": ^3.0.0 + "@changesets/logger": ^0.1.0 + "@changesets/pre": ^2.0.0 + "@changesets/read": ^0.6.0 + "@changesets/types": ^6.0.0 + "@changesets/write": ^0.3.0 "@manypkg/get-packages": ^1.1.3 - "@types/is-ci": ^3.0.0 "@types/semver": ^7.5.0 ansi-colors: ^4.1.3 chalk: ^2.1.0 + ci-info: ^3.7.0 enquirer: ^2.3.0 external-editor: ^3.1.0 fs-extra: ^7.0.1 human-id: ^1.0.2 - is-ci: ^3.0.1 meow: ^6.0.0 outdent: ^0.5.0 p-limit: ^2.2.0 @@ -10322,129 +10365,129 @@ __metadata: tty-table: ^4.1.5 bin: changeset: bin.js - checksum: fc7b5bf319b19abed7a8d33a9fbd9ce49108af61c9c51920f609a49cb0c557f0b998711250d0cac149d0bed8a522f3109c4d8b0dda65b96ff2f823d16ca2f972 + checksum: 0d030dec7e0ef28626082a257d57f46cdf65edb65a95f5a3511a9d298ca052388d8ab7f9a714943864eddc59148c4afb0b802a9c75b5bea45aade4c0dc7a5fa6 languageName: node linkType: hard -"@changesets/config@npm:^2.3.1": - version: 2.3.1 - resolution: "@changesets/config@npm:2.3.1" +"@changesets/config@npm:^3.0.0": + version: 3.0.0 + resolution: "@changesets/config@npm:3.0.0" dependencies: - "@changesets/errors": ^0.1.4 - "@changesets/get-dependents-graph": ^1.3.6 - "@changesets/logger": ^0.0.5 - "@changesets/types": ^5.2.1 + "@changesets/errors": ^0.2.0 + "@changesets/get-dependents-graph": ^2.0.0 + "@changesets/logger": ^0.1.0 + "@changesets/types": ^6.0.0 "@manypkg/get-packages": ^1.1.3 fs-extra: ^7.0.1 micromatch: ^4.0.2 - checksum: 8af58e3add4751ac8ce2c01f026ac8843b8d1c07c9a3df6518496eaef67f56458a84cad310763c588f7eccbf6831afbf280df7e05e78b294027b6b847be3d0cc + checksum: 31a8c37e38768cf3676d24b7d371009dd1d691f221ecf086b79f0d96dc8e95aa408cda3659eb867a14615ea38a1c2be448bf0655c7570539af57c930ca784051 languageName: node linkType: hard -"@changesets/errors@npm:^0.1.4": - version: 0.1.4 - resolution: "@changesets/errors@npm:0.1.4" +"@changesets/errors@npm:^0.2.0": + version: 0.2.0 + resolution: "@changesets/errors@npm:0.2.0" dependencies: extendable-error: ^0.1.5 - checksum: 10734f1379715bf5a70b566dd42b50a75964d76f382bb67332776614454deda6d04a43dd7e727cd7cba56d7f2f7c95a07c7c0a19dd5d64fb1980b28322840733 + checksum: 4b79373f92287af4f723e8dbbccaf0299aa8735fc043243d0ad587f04a7614615ea50180be575d4438b9f00aa82d1cf85e902b77a55bdd3e0a8dd97e77b18c60 languageName: node linkType: hard -"@changesets/get-dependents-graph@npm:^1.3.6": - version: 1.3.6 - resolution: "@changesets/get-dependents-graph@npm:1.3.6" +"@changesets/get-dependents-graph@npm:^2.0.0": + version: 2.0.0 + resolution: "@changesets/get-dependents-graph@npm:2.0.0" dependencies: - "@changesets/types": ^5.2.1 + "@changesets/types": ^6.0.0 "@manypkg/get-packages": ^1.1.3 chalk: ^2.1.0 fs-extra: ^7.0.1 semver: ^7.5.3 - checksum: d2cbbc5041063b939899502d1b264a0d9edb655acefd7f6197883229156bb7cfd1ace642ae4a1f7f7b432f2c51429f5dc9851ff5a9ed47f1c0159916e66627a9 + checksum: 6690d3ed36e8a636bc2a985d209bd72ee1100601ccf00850ca1fbe8500af839a3f4e5bd2167858cf11383aa76360f853e481533157060ad882fb56319db3090a languageName: node linkType: hard -"@changesets/get-release-plan@npm:^3.0.17": - version: 3.0.17 - resolution: "@changesets/get-release-plan@npm:3.0.17" +"@changesets/get-release-plan@npm:^4.0.0": + version: 4.0.0 + resolution: "@changesets/get-release-plan@npm:4.0.0" dependencies: "@babel/runtime": ^7.20.1 - "@changesets/assemble-release-plan": ^5.2.4 - "@changesets/config": ^2.3.1 - "@changesets/pre": ^1.0.14 - "@changesets/read": ^0.5.9 - "@changesets/types": ^5.2.1 + "@changesets/assemble-release-plan": ^6.0.0 + "@changesets/config": ^3.0.0 + "@changesets/pre": ^2.0.0 + "@changesets/read": ^0.6.0 + "@changesets/types": ^6.0.0 "@manypkg/get-packages": ^1.1.3 - checksum: 8a0e3794d0f1e6220d173dbec96352ad69b585d013c3183888ca598dfdfcaa8a5ac3f7f36d5c511575cdc3559c2ad6f8cecfaa16ba9c24380899a81daa7af924 + checksum: 57672c1e94f95de8ac65aac969275e0cb225f02aa86b2cef69329fff6e36ba5fde04eadeb6af36f4d8ac41a8fd329028b4df4c23c15c10fd13e026c77463d576 languageName: node linkType: hard -"@changesets/get-version-range-type@npm:^0.3.2": - version: 0.3.2 - resolution: "@changesets/get-version-range-type@npm:0.3.2" - checksum: b7ee7127c472a3886906ca6db336ac11233a5e75abc882084bfb4794e79a8936e3faceec3c04bf61c26453cd7f74278d9bf22aea4cdca8c1cd992591925b3c9b +"@changesets/get-version-range-type@npm:^0.4.0": + version: 0.4.0 + resolution: "@changesets/get-version-range-type@npm:0.4.0" + checksum: 2e8c511e658e193f48de7f09522649c4cf072932f0cbe0f252a7f2703d7775b0b90b632254526338795d0658e340be9dff3879cfc8eba4534b8cd6071efff8c9 languageName: node linkType: hard -"@changesets/git@npm:^2.0.0": - version: 2.0.0 - resolution: "@changesets/git@npm:2.0.0" +"@changesets/git@npm:^3.0.0": + version: 3.0.0 + resolution: "@changesets/git@npm:3.0.0" dependencies: "@babel/runtime": ^7.20.1 - "@changesets/errors": ^0.1.4 - "@changesets/types": ^5.2.1 + "@changesets/errors": ^0.2.0 + "@changesets/types": ^6.0.0 "@manypkg/get-packages": ^1.1.3 is-subdir: ^1.1.1 micromatch: ^4.0.2 spawndamnit: ^2.0.0 - checksum: 3820b7b689bbe8dfb93222c766bee214e68a45f07b2b5c8056891f9ffe6f1e369c0f84388246a9eea5317b496ae80ffd1508319190f79c359f060ebf8ccb7b13 + checksum: a8fa66d77302b50d5e604aca898ee813247537d23a05004637ecee4aa1579d6a2859283c099bdcf3e2b232258c93ff81dd57aa867858788e457df40118c64c2b languageName: node linkType: hard -"@changesets/logger@npm:^0.0.5": - version: 0.0.5 - resolution: "@changesets/logger@npm:0.0.5" +"@changesets/logger@npm:^0.1.0": + version: 0.1.0 + resolution: "@changesets/logger@npm:0.1.0" dependencies: chalk: ^2.1.0 - checksum: bfec3cd9122b00c0ec25e96730f771ffd662ef3906d571bad1e4e9993f9d54d357d3eaf074b3dfaa4e23af759ce68efa2a97d8b845b0d8c951df5d21c6dfdff5 + checksum: d8ef1b7caf3d2c15a9e7743b7a9462e0c2e61c76d9a5bbed5eff805afa8226117505309c6e9095001136b4f6d9ae0aba61377e53af8aa0809f1febd1b5f787f1 languageName: node linkType: hard -"@changesets/parse@npm:^0.3.16": - version: 0.3.16 - resolution: "@changesets/parse@npm:0.3.16" +"@changesets/parse@npm:^0.4.0": + version: 0.4.0 + resolution: "@changesets/parse@npm:0.4.0" dependencies: - "@changesets/types": ^5.2.1 + "@changesets/types": ^6.0.0 js-yaml: ^3.13.1 - checksum: 475f808ac8d33ec90af3914d55af1da8eeb9336d6cab7dd9e5be74af844f0ec04f4a67d5237a1d3284a468e0c9198e2be01d0e5870a1b28e63bc240f5f1ffea9 + checksum: 3dd970b244479746233ebd357cfff3816cf9f344ebf2cf0c7c55ce8579adfd3f506978e86ad61222dc3acf1548a2105ffdd8b3e940b3f82b225741315cee2bf0 languageName: node linkType: hard -"@changesets/pre@npm:^1.0.14": - version: 1.0.14 - resolution: "@changesets/pre@npm:1.0.14" +"@changesets/pre@npm:^2.0.0": + version: 2.0.0 + resolution: "@changesets/pre@npm:2.0.0" dependencies: "@babel/runtime": ^7.20.1 - "@changesets/errors": ^0.1.4 - "@changesets/types": ^5.2.1 + "@changesets/errors": ^0.2.0 + "@changesets/types": ^6.0.0 "@manypkg/get-packages": ^1.1.3 fs-extra: ^7.0.1 - checksum: 6b849bd6f916476a5b5664bc4286020bee506985c82f723a757fa4e681b0b7129db81751f16072ac55a980ffd83a4b234d6b8d0f8b6bc889aa0c0fd5377431e8 + checksum: 6a01086405f4e4ce63abb8f222de39b69a5762c9c8c8f19c0d3c72f7798248d7a152937028f1be24be1f8a4a5e47e4cb23c54bc36f979539b24a728c893caf4e languageName: node linkType: hard -"@changesets/read@npm:^0.5.9": - version: 0.5.9 - resolution: "@changesets/read@npm:0.5.9" +"@changesets/read@npm:^0.6.0": + version: 0.6.0 + resolution: "@changesets/read@npm:0.6.0" dependencies: "@babel/runtime": ^7.20.1 - "@changesets/git": ^2.0.0 - "@changesets/logger": ^0.0.5 - "@changesets/parse": ^0.3.16 - "@changesets/types": ^5.2.1 + "@changesets/git": ^3.0.0 + "@changesets/logger": ^0.1.0 + "@changesets/parse": ^0.4.0 + "@changesets/types": ^6.0.0 chalk: ^2.1.0 fs-extra: ^7.0.1 p-filter: ^2.1.0 - checksum: 0875a80829186de2da55bc0347601cc31b269d54fb6967a5093abacbbd9f949e352907b8340b61348a304228fdade670ded151327f16eea3424b5b4b2bb9888c + checksum: 3da6428124b4983f6ccbdae324c73044cd6a84269bfdbaff545331042e3d6845c647613b5d8f4ffdd48bad5b791623eca2be1b507652ea47b77e136cd2e26c70 languageName: node linkType: hard @@ -10455,23 +10498,49 @@ __metadata: languageName: node linkType: hard -"@changesets/types@npm:^5.2.1": - version: 5.2.1 - resolution: "@changesets/types@npm:5.2.1" - checksum: 527dc1aa41b040fe35bcd55f7d07bec710320b179b000c429723e25b87aac18be487daf5047d4fecf2781aad78f73abff111e76e411b652f7a2e812a464c69f2 +"@changesets/types@npm:^6.0.0": + version: 6.0.0 + resolution: "@changesets/types@npm:6.0.0" + checksum: d528b5d712f62c26ea422c7d34ccf6eac57a353c0733d96716db3c796ecd9bba5d496d48b37d5d46b784dc45b69c06ce3345fa3515df981bb68456cad68e6465 languageName: node linkType: hard -"@changesets/write@npm:^0.2.3": - version: 0.2.3 - resolution: "@changesets/write@npm:0.2.3" +"@changesets/write@npm:^0.3.0": + version: 0.3.0 + resolution: "@changesets/write@npm:0.3.0" dependencies: "@babel/runtime": ^7.20.1 - "@changesets/types": ^5.2.1 + "@changesets/types": ^6.0.0 fs-extra: ^7.0.1 human-id: ^1.0.2 prettier: ^2.7.1 - checksum: 40ad8069f9adc565b78a5f25992e31b41a12e551d94c29e1b4def49ce98871a1e358feda6536be8b363a6dba18b1226a22ecfc60fdd7bc1e74bfcf46b07f91be + checksum: 37588eb3ef2af15b3ea09d46864c994780619d20b791ea5b654801a035a3a12540c7f953e6e4f36731678615edc6d1c32f8fe174d599d3e6ce2d68263865788b + languageName: node + linkType: hard + +"@circleci/backstage-plugin@npm:^0.1.1": + version: 0.1.1 + resolution: "@circleci/backstage-plugin@npm:0.1.1" + dependencies: + "@backstage/catalog-model": "workspace:^" + "@backstage/core-components": "workspace:^" + "@backstage/core-plugin-api": "workspace:^" + "@backstage/plugin-catalog-react": "workspace:^" + "@backstage/theme": "workspace:^" + "@material-ui/core": ^4.12.2 + "@material-ui/icons": ^4.9.1 + "@material-ui/lab": 4.0.0-alpha.61 + "@types/react": ^16.13.1 || ^17.0.0 + circleci-api: ^4.0.0 + humanize-duration: ^3.27.0 + lodash: ^4.17.21 + luxon: ^3.0.0 + react-use: ^17.2.4 + peerDependencies: + react: ^16.13.1 || ^17.0.0 + react-dom: ^16.13.1 || ^17.0.0 + react-router-dom: 6.0.0-beta.0 || ^6.3.0 + checksum: ded80e2eef1cec3f83f76e62b1362499c1662ae6307bdc829b78aed02101af6fd85134d94716fbbb60e8a0d5b3d2a4653df81fbdfcab576a47f7b59a332e580f languageName: node linkType: hard @@ -10505,8 +10574,8 @@ __metadata: linkType: hard "@codemirror/language@npm:^6.0.0": - version: 6.9.2 - resolution: "@codemirror/language@npm:6.9.2" + version: 6.9.3 + resolution: "@codemirror/language@npm:6.9.3" dependencies: "@codemirror/state": ^6.0.0 "@codemirror/view": ^6.0.0 @@ -10514,7 +10583,7 @@ __metadata: "@lezer/highlight": ^1.0.0 "@lezer/lr": ^1.0.0 style-mod: ^4.0.0 - checksum: eee7b861b5591114cac7502cd532d5b923639740081a4cd7e28696c252af8d759b14686aaf6d5eee7e0969ff647b7aaf03a5eea7235fb6d9858ee19433f1c74d + checksum: 774a40bc91c748d418a9a774161a5b083061124e4439bb753072bc657ec4c4784f595161c10c7c3935154b22291bf6dc74c9abe827033db32e217ac3963478f3 languageName: node linkType: hard @@ -10569,13 +10638,13 @@ __metadata: linkType: hard "@codemirror/view@npm:^6.0.0": - version: 6.22.0 - resolution: "@codemirror/view@npm:6.22.0" + version: 6.22.1 + resolution: "@codemirror/view@npm:6.22.1" dependencies: "@codemirror/state": ^6.1.4 style-mod: ^4.1.0 w3c-keyname: ^2.2.4 - checksum: 2a24674687fbde06898d0a131abe5f86a812d79e111cf8dc94110dac86eed8c20a2094b547c1b3c379fe8edf0c66318d03a7594158e4f6628ee060a03a5d1bab + checksum: 8643d86cf8c34433a410b28b1339c9a333c519e815890bf6494ca35a47d6ed38b51a13480cf4f6bea319ab2a17991e1ba47cd1b4a2c5a99fb76bad014682925a languageName: node linkType: hard @@ -10874,9 +10943,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/android-arm64@npm:0.19.7": - version: 0.19.7 - resolution: "@esbuild/android-arm64@npm:0.19.7" +"@esbuild/android-arm64@npm:0.19.8": + version: 0.19.8 + resolution: "@esbuild/android-arm64@npm:0.19.8" conditions: os=android & cpu=arm64 languageName: node linkType: hard @@ -10895,9 +10964,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/android-arm@npm:0.19.7": - version: 0.19.7 - resolution: "@esbuild/android-arm@npm:0.19.7" +"@esbuild/android-arm@npm:0.19.8": + version: 0.19.8 + resolution: "@esbuild/android-arm@npm:0.19.8" conditions: os=android & cpu=arm languageName: node linkType: hard @@ -10916,9 +10985,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/android-x64@npm:0.19.7": - version: 0.19.7 - resolution: "@esbuild/android-x64@npm:0.19.7" +"@esbuild/android-x64@npm:0.19.8": + version: 0.19.8 + resolution: "@esbuild/android-x64@npm:0.19.8" conditions: os=android & cpu=x64 languageName: node linkType: hard @@ -10937,9 +11006,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/darwin-arm64@npm:0.19.7": - version: 0.19.7 - resolution: "@esbuild/darwin-arm64@npm:0.19.7" +"@esbuild/darwin-arm64@npm:0.19.8": + version: 0.19.8 + resolution: "@esbuild/darwin-arm64@npm:0.19.8" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard @@ -10958,9 +11027,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/darwin-x64@npm:0.19.7": - version: 0.19.7 - resolution: "@esbuild/darwin-x64@npm:0.19.7" +"@esbuild/darwin-x64@npm:0.19.8": + version: 0.19.8 + resolution: "@esbuild/darwin-x64@npm:0.19.8" conditions: os=darwin & cpu=x64 languageName: node linkType: hard @@ -10979,9 +11048,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/freebsd-arm64@npm:0.19.7": - version: 0.19.7 - resolution: "@esbuild/freebsd-arm64@npm:0.19.7" +"@esbuild/freebsd-arm64@npm:0.19.8": + version: 0.19.8 + resolution: "@esbuild/freebsd-arm64@npm:0.19.8" conditions: os=freebsd & cpu=arm64 languageName: node linkType: hard @@ -11000,9 +11069,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/freebsd-x64@npm:0.19.7": - version: 0.19.7 - resolution: "@esbuild/freebsd-x64@npm:0.19.7" +"@esbuild/freebsd-x64@npm:0.19.8": + version: 0.19.8 + resolution: "@esbuild/freebsd-x64@npm:0.19.8" conditions: os=freebsd & cpu=x64 languageName: node linkType: hard @@ -11021,9 +11090,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-arm64@npm:0.19.7": - version: 0.19.7 - resolution: "@esbuild/linux-arm64@npm:0.19.7" +"@esbuild/linux-arm64@npm:0.19.8": + version: 0.19.8 + resolution: "@esbuild/linux-arm64@npm:0.19.8" conditions: os=linux & cpu=arm64 languageName: node linkType: hard @@ -11042,9 +11111,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-arm@npm:0.19.7": - version: 0.19.7 - resolution: "@esbuild/linux-arm@npm:0.19.7" +"@esbuild/linux-arm@npm:0.19.8": + version: 0.19.8 + resolution: "@esbuild/linux-arm@npm:0.19.8" conditions: os=linux & cpu=arm languageName: node linkType: hard @@ -11063,9 +11132,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-ia32@npm:0.19.7": - version: 0.19.7 - resolution: "@esbuild/linux-ia32@npm:0.19.7" +"@esbuild/linux-ia32@npm:0.19.8": + version: 0.19.8 + resolution: "@esbuild/linux-ia32@npm:0.19.8" conditions: os=linux & cpu=ia32 languageName: node linkType: hard @@ -11084,9 +11153,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-loong64@npm:0.19.7": - version: 0.19.7 - resolution: "@esbuild/linux-loong64@npm:0.19.7" +"@esbuild/linux-loong64@npm:0.19.8": + version: 0.19.8 + resolution: "@esbuild/linux-loong64@npm:0.19.8" conditions: os=linux & cpu=loong64 languageName: node linkType: hard @@ -11105,9 +11174,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-mips64el@npm:0.19.7": - version: 0.19.7 - resolution: "@esbuild/linux-mips64el@npm:0.19.7" +"@esbuild/linux-mips64el@npm:0.19.8": + version: 0.19.8 + resolution: "@esbuild/linux-mips64el@npm:0.19.8" conditions: os=linux & cpu=mips64el languageName: node linkType: hard @@ -11126,9 +11195,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-ppc64@npm:0.19.7": - version: 0.19.7 - resolution: "@esbuild/linux-ppc64@npm:0.19.7" +"@esbuild/linux-ppc64@npm:0.19.8": + version: 0.19.8 + resolution: "@esbuild/linux-ppc64@npm:0.19.8" conditions: os=linux & cpu=ppc64 languageName: node linkType: hard @@ -11147,9 +11216,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-riscv64@npm:0.19.7": - version: 0.19.7 - resolution: "@esbuild/linux-riscv64@npm:0.19.7" +"@esbuild/linux-riscv64@npm:0.19.8": + version: 0.19.8 + resolution: "@esbuild/linux-riscv64@npm:0.19.8" conditions: os=linux & cpu=riscv64 languageName: node linkType: hard @@ -11168,9 +11237,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-s390x@npm:0.19.7": - version: 0.19.7 - resolution: "@esbuild/linux-s390x@npm:0.19.7" +"@esbuild/linux-s390x@npm:0.19.8": + version: 0.19.8 + resolution: "@esbuild/linux-s390x@npm:0.19.8" conditions: os=linux & cpu=s390x languageName: node linkType: hard @@ -11189,9 +11258,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-x64@npm:0.19.7": - version: 0.19.7 - resolution: "@esbuild/linux-x64@npm:0.19.7" +"@esbuild/linux-x64@npm:0.19.8": + version: 0.19.8 + resolution: "@esbuild/linux-x64@npm:0.19.8" conditions: os=linux & cpu=x64 languageName: node linkType: hard @@ -11210,9 +11279,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/netbsd-x64@npm:0.19.7": - version: 0.19.7 - resolution: "@esbuild/netbsd-x64@npm:0.19.7" +"@esbuild/netbsd-x64@npm:0.19.8": + version: 0.19.8 + resolution: "@esbuild/netbsd-x64@npm:0.19.8" conditions: os=netbsd & cpu=x64 languageName: node linkType: hard @@ -11231,9 +11300,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/openbsd-x64@npm:0.19.7": - version: 0.19.7 - resolution: "@esbuild/openbsd-x64@npm:0.19.7" +"@esbuild/openbsd-x64@npm:0.19.8": + version: 0.19.8 + resolution: "@esbuild/openbsd-x64@npm:0.19.8" conditions: os=openbsd & cpu=x64 languageName: node linkType: hard @@ -11252,9 +11321,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/sunos-x64@npm:0.19.7": - version: 0.19.7 - resolution: "@esbuild/sunos-x64@npm:0.19.7" +"@esbuild/sunos-x64@npm:0.19.8": + version: 0.19.8 + resolution: "@esbuild/sunos-x64@npm:0.19.8" conditions: os=sunos & cpu=x64 languageName: node linkType: hard @@ -11273,9 +11342,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/win32-arm64@npm:0.19.7": - version: 0.19.7 - resolution: "@esbuild/win32-arm64@npm:0.19.7" +"@esbuild/win32-arm64@npm:0.19.8": + version: 0.19.8 + resolution: "@esbuild/win32-arm64@npm:0.19.8" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard @@ -11294,9 +11363,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/win32-ia32@npm:0.19.7": - version: 0.19.7 - resolution: "@esbuild/win32-ia32@npm:0.19.7" +"@esbuild/win32-ia32@npm:0.19.8": + version: 0.19.8 + resolution: "@esbuild/win32-ia32@npm:0.19.8" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard @@ -11315,9 +11384,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/win32-x64@npm:0.19.7": - version: 0.19.7 - resolution: "@esbuild/win32-x64@npm:0.19.7" +"@esbuild/win32-x64@npm:0.19.8": + version: 0.19.8 + resolution: "@esbuild/win32-x64@npm:0.19.8" conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -11569,8 +11638,8 @@ __metadata: linkType: hard "@google-cloud/storage@npm:^7.0.0": - version: 7.6.0 - resolution: "@google-cloud/storage@npm:7.6.0" + version: 7.7.0 + resolution: "@google-cloud/storage@npm:7.7.0" dependencies: "@google-cloud/paginator": ^5.0.0 "@google-cloud/projectify": ^4.0.0 @@ -11589,7 +11658,7 @@ __metadata: retry-request: ^7.0.0 teeny-request: ^9.0.0 uuid: ^8.0.0 - checksum: 0ab71792ddafef3ce3634e2428fe3508572611caa436c4e6315f5f8657f48fa5714f0410383f8b006b469d7e71b7ef2dd2c0d68323753bcd0b381b06bdd3e1f4 + checksum: b63069b7e591e55f9132aab9e8f9cd03b72a5b6e531c1f37fc44c4cd34eb02dd50e8007739dad6f0ac2ddb216eb5a80bc3b062d1d8c42aad7351e6cc6008d27f languageName: node linkType: hard @@ -12483,7 +12552,7 @@ __metadata: languageName: node linkType: hard -"@jridgewell/sourcemap-codec@npm:^1.4.10, @jridgewell/sourcemap-codec@npm:^1.4.13, @jridgewell/sourcemap-codec@npm:^1.4.14, @jridgewell/sourcemap-codec@npm:^1.4.15": +"@jridgewell/sourcemap-codec@npm:^1.4.10, @jridgewell/sourcemap-codec@npm:^1.4.14, @jridgewell/sourcemap-codec@npm:^1.4.15": version: 1.4.15 resolution: "@jridgewell/sourcemap-codec@npm:1.4.15" checksum: b881c7e503db3fc7f3c1f35a1dd2655a188cc51a3612d76efc8a6eb74728bef5606e6758ee77423e564092b4a518aba569bbb21c9bac5ab7a35b0c6ae7e344c8 @@ -13091,9 +13160,9 @@ __metadata: linkType: hard "@microsoft/microsoft-graph-types@npm:^2.25.0, @microsoft/microsoft-graph-types@npm:^2.6.0": - version: 2.38.0 - resolution: "@microsoft/microsoft-graph-types@npm:2.38.0" - checksum: f5778650655034a6d92fbb932376002aefcf0cd358de323267b65fbec59284e8c0208e895c5523dcbbe79c4b583f3822c6beea01b70c687da26929c92de93c92 + version: 2.40.0 + resolution: "@microsoft/microsoft-graph-types@npm:2.40.0" + checksum: 3892e910baf4fcf2d1bcee4c71683e3707818be6ff1b4b9dbc29b9e2eab942ea0b171b3023d4620213f10d93d03611e6615706a3d1af4ca44028491a0544ccaf languageName: node linkType: hard @@ -13228,14 +13297,14 @@ __metadata: languageName: node linkType: hard -"@mui/base@npm:5.0.0-beta.24": - version: 5.0.0-beta.24 - resolution: "@mui/base@npm:5.0.0-beta.24" +"@mui/base@npm:5.0.0-beta.25": + version: 5.0.0-beta.25 + resolution: "@mui/base@npm:5.0.0-beta.25" dependencies: - "@babel/runtime": ^7.23.2 + "@babel/runtime": ^7.23.4 "@floating-ui/react-dom": ^2.0.4 - "@mui/types": ^7.2.9 - "@mui/utils": ^5.14.18 + "@mui/types": ^7.2.10 + "@mui/utils": ^5.14.19 "@popperjs/core": ^2.11.8 clsx: ^2.0.0 prop-types: ^15.8.1 @@ -13246,28 +13315,28 @@ __metadata: peerDependenciesMeta: "@types/react": optional: true - checksum: dbd6eb0af7cb89389b1dccd6a84d07128c41f60d297d8f1d484919950c9b45a5d8767042b622c74e311bce0368ee3f760afb34d80ad97af61f5b5650ae990c60 + checksum: ce22c38593db1bbbb162574bd297bc3f2f5040ed6e304965e17aff55e4d15782e813196bd67c9b82757ee67675ad6d557e22541f84e4d7a4b6f3349dc83741f3 languageName: node linkType: hard -"@mui/core-downloads-tracker@npm:^5.14.18": - version: 5.14.18 - resolution: "@mui/core-downloads-tracker@npm:5.14.18" - checksum: 3d367797282e4b93eacee997667d62bc4eeac979e9664724e9db8d632278d8629f2a141894cf53d706f1d2a9f19965bffe2858aa2bc9833722e674cd560b896e +"@mui/core-downloads-tracker@npm:^5.14.19": + version: 5.14.19 + resolution: "@mui/core-downloads-tracker@npm:5.14.19" + checksum: e71c886f12bbd83791638545017c0b8439c3c6b51125979fea105f938f2f5b109629d4deddd38448c05b8be10b3249334324f1505c1306c52a2b8d315a1005c3 languageName: node linkType: hard "@mui/material@npm:^5.12.2": - version: 5.14.18 - resolution: "@mui/material@npm:5.14.18" + version: 5.14.19 + resolution: "@mui/material@npm:5.14.19" dependencies: - "@babel/runtime": ^7.23.2 - "@mui/base": 5.0.0-beta.24 - "@mui/core-downloads-tracker": ^5.14.18 - "@mui/system": ^5.14.18 - "@mui/types": ^7.2.9 - "@mui/utils": ^5.14.18 - "@types/react-transition-group": ^4.4.8 + "@babel/runtime": ^7.23.4 + "@mui/base": 5.0.0-beta.25 + "@mui/core-downloads-tracker": ^5.14.19 + "@mui/system": ^5.14.19 + "@mui/types": ^7.2.10 + "@mui/utils": ^5.14.19 + "@types/react-transition-group": ^4.4.9 clsx: ^2.0.0 csstype: ^3.1.2 prop-types: ^15.8.1 @@ -13286,16 +13355,16 @@ __metadata: optional: true "@types/react": optional: true - checksum: 488322f09638a71bb5d4bb5c027dde69d5cf89bcfd433594e3b90fea9fefa302bc85b9d2bf386493bf68d360107b59c6d148f46a68477e7f472f4f2d14d698a3 + checksum: 8fc63b7ecb98c5eb8f67190cde096f83100af9271fe2113e1d3edb13a08650f611e7ff186b04c66695958cc88480011ab31dc950cf0fd6e0fbb6c727ec834cbd languageName: node linkType: hard -"@mui/private-theming@npm:^5.14.18": - version: 5.14.18 - resolution: "@mui/private-theming@npm:5.14.18" +"@mui/private-theming@npm:^5.14.19": + version: 5.14.19 + resolution: "@mui/private-theming@npm:5.14.19" dependencies: - "@babel/runtime": ^7.23.2 - "@mui/utils": ^5.14.18 + "@babel/runtime": ^7.23.4 + "@mui/utils": ^5.14.19 prop-types: ^15.8.1 peerDependencies: "@types/react": ^17.0.0 || ^18.0.0 @@ -13303,15 +13372,15 @@ __metadata: peerDependenciesMeta: "@types/react": optional: true - checksum: e7ba8e945f589db16ed05d507b0821d2e07b3d760bf78c9f745a75d75b5a94d1e546278629de15b090de289a9244591bfa2c09bb9f9f8679fb4e1d8cec360833 + checksum: ee17fa123ae671fcfb6e59787e9a5b6d650d4a53ea575f5d5519dd187c9b04fbb50f12f2da5a663d3ba3a82d503086faa18bf3b68923237de78f44e401e04935 languageName: node linkType: hard -"@mui/styled-engine@npm:^5.14.18": - version: 5.14.18 - resolution: "@mui/styled-engine@npm:5.14.18" +"@mui/styled-engine@npm:^5.14.19": + version: 5.14.19 + resolution: "@mui/styled-engine@npm:5.14.19" dependencies: - "@babel/runtime": ^7.23.2 + "@babel/runtime": ^7.23.4 "@emotion/cache": ^11.11.0 csstype: ^3.1.2 prop-types: ^15.8.1 @@ -13324,19 +13393,19 @@ __metadata: optional: true "@emotion/styled": optional: true - checksum: 6eba307d95ef462f8b80aaa50ec1db7409d19cead985eba28e86aec066ad4bc70b0daa6d89158a7aae88ea43d614cace5f890cbebe0d8636573e894b7da9cae1 + checksum: 2b8dc8e08e47e18ad6345d25539d5978ef153cb23abff3d98c3cf2795f836e446279021392c2071142ffbae17e19906235acfb3e73dc0f14440b138f919a26a4 languageName: node linkType: hard "@mui/styles@npm:^5.14.18": - version: 5.14.18 - resolution: "@mui/styles@npm:5.14.18" + version: 5.14.19 + resolution: "@mui/styles@npm:5.14.19" dependencies: - "@babel/runtime": ^7.23.2 + "@babel/runtime": ^7.23.4 "@emotion/hash": ^0.9.1 - "@mui/private-theming": ^5.14.18 - "@mui/types": ^7.2.9 - "@mui/utils": ^5.14.18 + "@mui/private-theming": ^5.14.19 + "@mui/types": ^7.2.10 + "@mui/utils": ^5.14.19 clsx: ^2.0.0 csstype: ^3.1.2 hoist-non-react-statics: ^3.3.2 @@ -13355,19 +13424,19 @@ __metadata: peerDependenciesMeta: "@types/react": optional: true - checksum: c3a50ff3ab96e931f1506431de70c173af49c7a7d9410a2d132f65ef789f38f4cc742f456cfc16b24ecb7cb492b30dc1afb653cf31318b7059fd52fa790983b5 + checksum: 03780cba63ee5dca4bd3f81aa8634039e6a18291eb2f89cdcee236669e4b05a2593a3e5e74c21378950cc34a790163d2d3b80d2b649fd368a5b09439f198489e languageName: node linkType: hard -"@mui/system@npm:^5.14.18": - version: 5.14.18 - resolution: "@mui/system@npm:5.14.18" +"@mui/system@npm:^5.14.19": + version: 5.14.19 + resolution: "@mui/system@npm:5.14.19" dependencies: - "@babel/runtime": ^7.23.2 - "@mui/private-theming": ^5.14.18 - "@mui/styled-engine": ^5.14.18 - "@mui/types": ^7.2.9 - "@mui/utils": ^5.14.18 + "@babel/runtime": ^7.23.4 + "@mui/private-theming": ^5.14.19 + "@mui/styled-engine": ^5.14.19 + "@mui/types": ^7.2.10 + "@mui/utils": ^5.14.19 clsx: ^2.0.0 csstype: ^3.1.2 prop-types: ^15.8.1 @@ -13383,28 +13452,28 @@ __metadata: optional: true "@types/react": optional: true - checksum: 8f7c4f3555ee64467826e1a40cfdb34b5c02520fd9f27779b8e6aa97cb9bbd27d98386a194d2e4c44b148babe5f67e3a715f8a09c03a1d71f2f14ddf60045174 + checksum: aea4935cf72a7c4fe3d03eb040491204b0c20fbae19fc73173ec925c9025375601f6b7007e53b5ab6ea44a4dd74b94e9acc9ada85461db1deae9062f8506a99a languageName: node linkType: hard -"@mui/types@npm:^7.2.9": - version: 7.2.9 - resolution: "@mui/types@npm:7.2.9" +"@mui/types@npm:^7.2.10": + version: 7.2.10 + resolution: "@mui/types@npm:7.2.10" peerDependencies: "@types/react": ^17.0.0 || ^18.0.0 peerDependenciesMeta: "@types/react": optional: true - checksum: 4888a1cf9a1adbae1c2f53ba9f8a0cf23eb5e0954bca00b958c240c87b5287771cb5e99bedb61a2c04f5dcbdef13a5b1f238153e98d8959ba51c5470db2c4b32 + checksum: b9c4629929450e243015d79cf6b102824336db07b852e55f114aca85dbad0b39f831e9c65ad94b028a65c08d33be9f7de1afb530a4e6a80be5702b396ccb90b1 languageName: node linkType: hard -"@mui/utils@npm:^5.14.15, @mui/utils@npm:^5.14.18": - version: 5.14.18 - resolution: "@mui/utils@npm:5.14.18" +"@mui/utils@npm:^5.14.15, @mui/utils@npm:^5.14.19": + version: 5.14.19 + resolution: "@mui/utils@npm:5.14.19" dependencies: - "@babel/runtime": ^7.23.2 - "@types/prop-types": ^15.7.10 + "@babel/runtime": ^7.23.4 + "@types/prop-types": ^15.7.11 prop-types: ^15.8.1 react-is: ^18.2.0 peerDependencies: @@ -13413,7 +13482,7 @@ __metadata: peerDependenciesMeta: "@types/react": optional: true - checksum: f24badcb6b026b392b94919f65c7f3bea6cfb0f741c1b8ebd74b02bba9ffc61f72e35bdea887e47dfd97d37ff465863aa29c7c5cfd33fb83224f4ff2300432b3 + checksum: f53f746eb33bc5d700b9f2b454e211ca17f5aa320cd30f54a82a347e4da0c61b872f323f01ff4b5247d3f992ac4f72a69ac79974300a6dedf01ff6b8f6d75b49 languageName: node linkType: hard @@ -14200,7 +14269,7 @@ __metadata: languageName: node linkType: hard -"@openapitools/openapi-generator-cli@npm:^2.4.26": +"@openapitools/openapi-generator-cli@npm:^2.4.26, @openapitools/openapi-generator-cli@npm:^2.7.0": version: 2.7.0 resolution: "@openapitools/openapi-generator-cli@npm:2.7.0" dependencies: @@ -14334,13 +14403,13 @@ __metadata: linkType: hard "@playwright/test@npm:^1.32.3": - version: 1.40.0 - resolution: "@playwright/test@npm:1.40.0" + version: 1.40.1 + resolution: "@playwright/test@npm:1.40.1" dependencies: - playwright: 1.40.0 + playwright: 1.40.1 bin: playwright: cli.js - checksum: 128f05978f9f5a557f0b7924ec134d43cb70c78d74bc3bf7b18576f00e72399100ddf1f4a139e05ea8275407d8e27be0203ac34f514319a2cbeb01eaf0be5be4 + checksum: ae094e6cb809365c0707ee2b184e42d2a2542569ada020d2d44ca5866066941262bd9a67af185f86c2fb0133c9b712ea8cb73e2959a289e4261c5fd17077283c languageName: node linkType: hard @@ -15081,9 +15150,9 @@ __metadata: languageName: node linkType: hard -"@rjsf/core@npm:5.14.2": - version: 5.14.2 - resolution: "@rjsf/core@npm:5.14.2" +"@rjsf/core@npm:5.14.3": + version: 5.14.3 + resolution: "@rjsf/core@npm:5.14.3" dependencies: lodash: ^4.17.21 lodash-es: ^4.17.21 @@ -15093,20 +15162,20 @@ __metadata: peerDependencies: "@rjsf/utils": ^5.12.x react: ^16.14.0 || >=17 - checksum: 8e3ce39e6c31ae4a72e7d4483f091b77327578ab74a65ebc39c348286d737e0fe829902e0d1218e354bf8a8e8a5055c90aac6c996f386ef7a48546f7d3ea6500 + checksum: 9929ae7b99f1c79dc108095c3338a30fe373cd0fc6c613f65d163f7729cda2509070bb7da581ce3ad068a41ea5050f4e8417508c12e53feb41c56119082d3aac languageName: node linkType: hard -"@rjsf/material-ui@npm:5.14.2": - version: 5.14.2 - resolution: "@rjsf/material-ui@npm:5.14.2" +"@rjsf/material-ui@npm:5.14.3": + version: 5.14.3 + resolution: "@rjsf/material-ui@npm:5.14.3" peerDependencies: "@material-ui/core": ^4.12.3 "@material-ui/icons": ^4.11.2 "@rjsf/core": ^5.12.x "@rjsf/utils": ^5.12.x react: ^16.14.0 || >=17 - checksum: 502cdea7ae9d7d24f9aaee86edc4cc1019e5f956c2446c4f2b5ec377aff31d2d6a6df5c75db54f6fa0d3eb22d855b67e1e8778dfa2b37d88f917098344bbdf08 + checksum: fa9b9a831fa333bb794ccde85f415c8daf18334d975a0f2aa1cd497017e7a03c398d5b4f4209eafd4aed4aa476578ef82e95cf544c5542ee030b080226f09388 languageName: node linkType: hard @@ -15125,9 +15194,9 @@ __metadata: languageName: node linkType: hard -"@rjsf/utils@npm:5.14.2": - version: 5.14.2 - resolution: "@rjsf/utils@npm:5.14.2" +"@rjsf/utils@npm:5.14.3": + version: 5.14.3 + resolution: "@rjsf/utils@npm:5.14.3" dependencies: json-schema-merge-allof: ^0.8.1 jsonpointer: ^5.0.1 @@ -15136,13 +15205,13 @@ __metadata: react-is: ^18.2.0 peerDependencies: react: ^16.14.0 || >=17 - checksum: e1caf316a3ab96b7b184988fd8e4db4904bdf0ab01146826f4dbd7ab5765c6f28f2e8c328366ace586f2bf8f903f482c32b4aefaf76ed72a16f31ca9814308ba + checksum: 759f85e1d205d360024e569ab04c57543cbec4af8293636ba0af0ec7797bdcb361cd0b33c176ede464f101b33eb4240b60e241537ba19edefc04b91b80e6051e languageName: node linkType: hard -"@rjsf/validator-ajv8@npm:5.14.2": - version: 5.14.2 - resolution: "@rjsf/validator-ajv8@npm:5.14.2" +"@rjsf/validator-ajv8@npm:5.14.3": + version: 5.14.3 + resolution: "@rjsf/validator-ajv8@npm:5.14.3" dependencies: ajv: ^8.12.0 ajv-formats: ^2.1.1 @@ -15150,19 +15219,19 @@ __metadata: lodash-es: ^4.17.21 peerDependencies: "@rjsf/utils": ^5.12.x - checksum: 5f8b7961e8ae15ab596e9904f38fc543858ad72566b4fbf15e57cea7dd1f9b9017cb8ba780bf0312635696ef681ef11765cb523c361a68d6e998ae486df23d6e + checksum: 8b7caa0d5a81ecffaee1a538625c93ff8fdf860d67b80e75901df62112d00479bed6987c25427b84957b4e483c80c80d91713922bdb21ae8dd440242f0e8f5e3 languageName: node linkType: hard "@roadiehq/backstage-plugin-buildkite@npm:^2.0.8": - version: 2.1.16 - resolution: "@roadiehq/backstage-plugin-buildkite@npm:2.1.16" + version: 2.1.17 + resolution: "@roadiehq/backstage-plugin-buildkite@npm:2.1.17" dependencies: "@backstage/catalog-model": ^1.4.3 - "@backstage/core-components": ^0.13.7 - "@backstage/core-plugin-api": ^1.7.0 - "@backstage/plugin-catalog-react": ^1.8.5 - "@backstage/theme": ^0.4.3 + "@backstage/core-components": ^0.13.8 + "@backstage/core-plugin-api": ^1.8.0 + "@backstage/plugin-catalog-react": ^1.9.1 + "@backstage/theme": ^0.4.4 "@material-ui/core": ^4.12.1 "@material-ui/icons": ^4.11.2 "@material-ui/lab": 4.0.0-alpha.57 @@ -15174,20 +15243,20 @@ __metadata: react-dom: ^16.13.1 || ^17.0.0 react-router: 6.0.0-beta.0 || ^6.3.0 react-router-dom: 6.0.0-beta.0 || ^6.3.0 - checksum: 716755abfc85c452382ca1535d819a7ebb9a52d024557c51d240df7193a15b98567dad5fe0dcbbf72df0b27f773f1be0f25a5e876024f3a1f03c0eff99fc7a2c + checksum: 218a614d81ee89b2a1613979258788d612c675da2af7ffdcecaa6e47206d6aa4f463defc376f5c4a53ba2136038d0e0659ba80182bd8001c144323e40f3ff5ab languageName: node linkType: hard "@roadiehq/backstage-plugin-github-insights@npm:^2.0.5": - version: 2.3.22 - resolution: "@roadiehq/backstage-plugin-github-insights@npm:2.3.22" + version: 2.3.23 + resolution: "@roadiehq/backstage-plugin-github-insights@npm:2.3.23" dependencies: "@backstage/catalog-model": ^1.4.3 - "@backstage/core-components": ^0.13.7 - "@backstage/core-plugin-api": ^1.7.0 - "@backstage/integration-react": ^1.1.20 - "@backstage/plugin-catalog-react": ^1.8.5 - "@backstage/theme": ^0.4.3 + "@backstage/core-components": ^0.13.8 + "@backstage/core-plugin-api": ^1.8.0 + "@backstage/integration-react": ^1.1.21 + "@backstage/plugin-catalog-react": ^1.9.1 + "@backstage/theme": ^0.4.4 "@date-io/core": 2.10.7 "@material-ui/core": ^4.11.0 "@material-ui/icons": ^4.9.1 @@ -15202,19 +15271,19 @@ __metadata: react: ^16.13.1 || ^17.0.0 react-dom: ^16.13.1 || ^17.0.0 react-router: 6.0.0-beta.0 || ^6.3.0 - checksum: 1bdfa2dc1fca12f5a47a11a2df54a774992c1a91d294e646b70f1a197575b96c34da78356d230d0a65695348a1caef6ab62f2553e7e75a01504e58407108135a + checksum: 07dd3df1d8f99ea6f2910d343c361bd27b249fbd1e982fc42630ddc8e530901ef42e9a3cf5a944c80abd0c5be25cca716c12a838057ec1e83ad538a9462033e4 languageName: node linkType: hard "@roadiehq/backstage-plugin-github-pull-requests@npm:^2.2.7": - version: 2.5.19 - resolution: "@roadiehq/backstage-plugin-github-pull-requests@npm:2.5.19" + version: 2.5.20 + resolution: "@roadiehq/backstage-plugin-github-pull-requests@npm:2.5.20" dependencies: "@backstage/catalog-model": ^1.4.3 - "@backstage/core-components": ^0.13.7 - "@backstage/core-plugin-api": ^1.7.0 - "@backstage/plugin-catalog-react": ^1.8.5 - "@backstage/plugin-home-react": ^0.1.4 + "@backstage/core-components": ^0.13.8 + "@backstage/core-plugin-api": ^1.8.0 + "@backstage/plugin-catalog-react": ^1.9.1 + "@backstage/plugin-home-react": ^0.1.5 "@material-ui/core": ^4.11.0 "@material-ui/icons": ^4.9.1 "@material-ui/lab": ^4.0.0-alpha.60 @@ -15231,19 +15300,19 @@ __metadata: react: ^16.13.1 || ^17.0.0 react-dom: ^16.13.1 || ^17.0.0 react-router: 6.0.0-beta.0 || ^6.3.0 - checksum: 35d760d22626d6d76438db66e36610aff8f1b171ae4d60944edcd631427b55f2a0489339db761764d2d406c5d3480ec8786db16ffb803d28eb96544b8c8e75a7 + checksum: f9ac5bbff5173eff98002f3f8bd7b7fe349723039481651b1e81b7aa5bbaaa799f4cd51d5425a5d74cebf3d0f5bb7340a0c4091b858addc45791881c3802f7a7 languageName: node linkType: hard "@roadiehq/backstage-plugin-travis-ci@npm:^2.0.5": - version: 2.1.16 - resolution: "@roadiehq/backstage-plugin-travis-ci@npm:2.1.16" + version: 2.1.17 + resolution: "@roadiehq/backstage-plugin-travis-ci@npm:2.1.17" dependencies: "@backstage/catalog-model": ^1.4.3 - "@backstage/core-components": ^0.13.7 - "@backstage/core-plugin-api": ^1.7.0 - "@backstage/plugin-catalog-react": ^1.8.5 - "@backstage/theme": ^0.4.3 + "@backstage/core-components": ^0.13.8 + "@backstage/core-plugin-api": ^1.8.0 + "@backstage/plugin-catalog-react": ^1.9.1 + "@backstage/theme": ^0.4.4 "@material-ui/core": ^4.11.3 "@material-ui/icons": ^4.11.2 "@material-ui/lab": 4.0.0-alpha.57 @@ -15257,26 +15326,26 @@ __metadata: react-dom: ^16.13.1 || ^17.0.0 react-router: 6.0.0-beta.0 || ^6.3.0 react-router-dom: 6.0.0-beta.0 || ^6.3.0 - checksum: 175cbae49d6f990cdc0c0d75d27dfe36717f615fea1d4faf350d618bfa4420995c378e3735546ec403ad3c726d18c6ab2bdfc0f72edea26001eb4798e4093305 + checksum: fcf0b788a2f004a6eff181ff9f9babd07aceb630333b1a3e8ce7fb37b6e9a303444ecc9521a920f6aced2c9a081fa3024ee9079d1bb9f453e2d322af04f57113 languageName: node linkType: hard -"@rollup/plugin-commonjs@npm:^23.0.0": - version: 23.0.7 - resolution: "@rollup/plugin-commonjs@npm:23.0.7" +"@rollup/plugin-commonjs@npm:^25.0.0": + version: 25.0.7 + resolution: "@rollup/plugin-commonjs@npm:25.0.7" dependencies: "@rollup/pluginutils": ^5.0.1 commondir: ^1.0.1 estree-walker: ^2.0.2 glob: ^8.0.3 is-reference: 1.2.1 - magic-string: ^0.27.0 + magic-string: ^0.30.3 peerDependencies: - rollup: ^2.68.0||^3.0.0 + rollup: ^2.68.0||^3.0.0||^4.0.0 peerDependenciesMeta: rollup: optional: true - checksum: 01d90947bd4aa664c568cec172399825921f29afc035a6d8aec153868ab151ce7901ad56a101c76655e31b21567ddc70313c4bca476685b872218f041757a8c9 + checksum: 052e11839a9edc556eda5dcc759ab816dcc57e9f0f905a1e6e14fff954eaa6b1e2d0d544f5bd18d863993c5eba43d8ac9c19d9bb53b1c3b1213f32cfc9d50b2e languageName: node linkType: hard @@ -15296,33 +15365,36 @@ __metadata: languageName: node linkType: hard -"@rollup/plugin-json@npm:^5.0.0": - version: 5.0.2 - resolution: "@rollup/plugin-json@npm:5.0.2" +"@rollup/plugin-json@npm:^6.0.0": + version: 6.0.1 + resolution: "@rollup/plugin-json@npm:6.0.1" dependencies: "@rollup/pluginutils": ^5.0.1 peerDependencies: - rollup: ^1.20.0||^2.0.0||^3.0.0 + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 peerDependenciesMeta: rollup: optional: true - checksum: 9b5f90ea311dfcfacf0f38af39bbb1954ea56d6faecdee3d528f73d0e02da96a0706ab21fae0c8eef9bb5d756f6f50b40b5a252ffd9800397012b5bac6764b6f + checksum: 86995e3ceec0205bd0b5ae3075f4592e0ab3e6e6327a5dcfc825b44113eaae5819d26d5403d29b177ac59299e0b08c641c8d030e0c72805b92805ededc9cac44 languageName: node linkType: hard -"@rollup/plugin-node-resolve@npm:^13.0.6": - version: 13.3.0 - resolution: "@rollup/plugin-node-resolve@npm:13.3.0" +"@rollup/plugin-node-resolve@npm:^15.0.0": + version: 15.2.3 + resolution: "@rollup/plugin-node-resolve@npm:15.2.3" dependencies: - "@rollup/pluginutils": ^3.1.0 - "@types/resolve": 1.17.1 + "@rollup/pluginutils": ^5.0.1 + "@types/resolve": 1.20.2 deepmerge: ^4.2.2 - is-builtin-module: ^3.1.0 + is-builtin-module: ^3.2.1 is-module: ^1.0.0 - resolve: ^1.19.0 + resolve: ^1.22.1 peerDependencies: - rollup: ^2.42.0 - checksum: ec5418e6b3c23a9e30683056b3010e9d325316dcfae93fbc673ae64dad8e56a2ce761c15c48f5e2dcfe0c822fdc4a4905ee6346e3dcf90603ba2260afef5a5e6 + rollup: ^2.78.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + checksum: 730f32c2f8fdddff07cf0fca86a5dac7c475605fb96930197a868c066e62eb6388c557545e4f7d99b7a283411754c9fbf98944ab086b6074e04fc1292e234aa8 languageName: node linkType: hard @@ -15342,19 +15414,6 @@ __metadata: languageName: node linkType: hard -"@rollup/pluginutils@npm:^3.1.0": - version: 3.1.0 - resolution: "@rollup/pluginutils@npm:3.1.0" - dependencies: - "@types/estree": 0.0.39 - estree-walker: ^1.0.1 - picomatch: ^2.2.2 - peerDependencies: - rollup: ^1.20.0||^2.0.0 - checksum: 8be16e27863c219edbb25a4e6ec2fe0e1e451d9e917b6a43cf2ae5bc025a6b8faaa40f82a6e53b66d0de37b58ff472c6c3d57a83037ae635041f8df959d6d9aa - languageName: node - linkType: hard - "@rollup/pluginutils@npm:^4.1.1, @rollup/pluginutils@npm:^4.2.0, @rollup/pluginutils@npm:^4.2.1": version: 4.2.1 resolution: "@rollup/pluginutils@npm:4.2.1" @@ -16448,7 +16507,7 @@ __metadata: languageName: node linkType: hard -"@stoplight/types@npm:^12.3.0 || ^13.0.0, @stoplight/types@npm:^13.0.0, @stoplight/types@npm:^13.12.0, @stoplight/types@npm:^13.14.0, @stoplight/types@npm:^13.15.0, @stoplight/types@npm:^13.6.0": +"@stoplight/types@npm:^12.3.0 || ^13.0.0, @stoplight/types@npm:^13.0.0, @stoplight/types@npm:^13.12.0, @stoplight/types@npm:^13.15.0, @stoplight/types@npm:^13.6.0": version: 13.20.0 resolution: "@stoplight/types@npm:13.20.0" dependencies: @@ -16458,6 +16517,16 @@ __metadata: languageName: node linkType: hard +"@stoplight/types@npm:^14.0.0": + version: 14.0.0 + resolution: "@stoplight/types@npm:14.0.0" + dependencies: + "@types/json-schema": ^7.0.4 + utility-types: ^3.10.0 + checksum: 462fb6c718a2679927421ca1fffb5e31be2d150130e95a2771ffba04f68fc0e0f8ef3d4ae050e63fd4044bc4103e2ea82c98e1f5c7ff192a78914838eb250e5e + languageName: node + linkType: hard + "@stoplight/types@npm:~13.6.0": version: 13.6.0 resolution: "@stoplight/types@npm:13.6.0" @@ -17350,8 +17419,8 @@ __metadata: linkType: hard "@testing-library/jest-dom@npm:^6.0.0": - version: 6.1.4 - resolution: "@testing-library/jest-dom@npm:6.1.4" + version: 6.1.5 + resolution: "@testing-library/jest-dom@npm:6.1.5" dependencies: "@adobe/css-tools": ^4.3.1 "@babel/runtime": ^7.9.2 @@ -17375,7 +17444,7 @@ __metadata: optional: true vitest: optional: true - checksum: c6bd9469554136a25d94b55ea16736d56b8c5d200526023774dbf35ca35551a721257e6734f1b404bbd07ae0a1950f1912b5be60e113db2ff2ff50af14f7085c + checksum: 67f1433c7eb8649db6676df97d1144cf288e2d94c61e89531c05c587b56f2277454c558c97bcca567d5060ebd39caba5ba01d49dc57b3f005837477a401ad113 languageName: node linkType: hard @@ -17520,7 +17589,7 @@ __metadata: languageName: node linkType: hard -"@types/archiver@npm:^5.1.0, @types/archiver@npm:^5.3.1": +"@types/archiver@npm:^5.3.1": version: 5.3.4 resolution: "@types/archiver@npm:5.3.4" dependencies: @@ -17529,6 +17598,15 @@ __metadata: languageName: node linkType: hard +"@types/archiver@npm:^6.0.0": + version: 6.0.2 + resolution: "@types/archiver@npm:6.0.2" + dependencies: + "@types/readdir-glob": "*" + checksum: 4bef71e5b95863be9880339275ad92c2513dd78d56587653614dc5a74af1d078f5f5926ec996de06e7071a1003e0c2764d0e9d2bb0893a2ffb14ab1718eaf5ca + languageName: node + linkType: hard + "@types/argparse@npm:1.0.38": version: 1.0.38 resolution: "@types/argparse@npm:1.0.38" @@ -18118,13 +18196,13 @@ __metadata: languageName: node linkType: hard -"@types/glob@npm:^7.1.1": - version: 7.2.0 - resolution: "@types/glob@npm:7.2.0" +"@types/glob@npm:^8.0.0": + version: 8.1.0 + resolution: "@types/glob@npm:8.1.0" dependencies: - "@types/minimatch": "*" + "@types/minimatch": ^5.1.2 "@types/node": "*" - checksum: 6ae717fedfdfdad25f3d5a568323926c64f52ef35897bcac8aca8e19bc50c0bd84630bbd063e5d52078b2137d8e7d3c26eabebd1a2f03ff350fff8a91e79fc19 + checksum: 9101f3a9061e40137190f70626aa0e202369b5ec4012c3fabe6f5d229cce04772db9a94fa5a0eb39655e2e4ad105c38afbb4af56a56c0996a8c7d4fc72350e3d languageName: node linkType: hard @@ -18230,15 +18308,6 @@ __metadata: languageName: node linkType: hard -"@types/is-ci@npm:^3.0.0": - version: 3.0.0 - resolution: "@types/is-ci@npm:3.0.0" - dependencies: - ci-info: ^3.1.0 - checksum: 7c1f1f16c1fa2134de7400d82766c83fa76057261ba890628af77a09382ebb92d945bb077b98cfcf3d40ab1469c9ffbd2278112867edbe57aa655f53547eb139 - languageName: node - linkType: hard - "@types/is-glob@npm:^4.0.2": version: 4.0.4 resolution: "@types/is-glob@npm:4.0.4" @@ -18459,9 +18528,9 @@ __metadata: linkType: hard "@types/luxon@npm:*, @types/luxon@npm:^3.0.0, @types/luxon@npm:~3.3.0": - version: 3.3.5 - resolution: "@types/luxon@npm:3.3.5" - checksum: 7fd31c673600dd494dedefe8c405d2e6957c0ba101f8445f7eaf9afcf4a1fa1ca649cab01c9777523ff9de58c3c35e2589a1a004a0c629cd7953033115ac74f5 + version: 3.3.6 + resolution: "@types/luxon@npm:3.3.6" + checksum: 44fd3e617fb5d9e370a8d5529de3e703b295bfe225936e786e0b5e7fce662ef82012a6ec29cd2763afbd59bf9a9ab43186bad5c51aa552d963cd5e8c8f4e1d4f languageName: node linkType: hard @@ -18502,13 +18571,6 @@ __metadata: languageName: node linkType: hard -"@types/minimatch@npm:*, @types/minimatch@npm:^5.0.0": - version: 5.1.2 - resolution: "@types/minimatch@npm:5.1.2" - checksum: 0391a282860c7cb6fe262c12b99564732401bdaa5e395bee9ca323c312c1a0f45efbf34dce974682036e857db59a5c9b1da522f3d6055aeead7097264c8705a8 - languageName: node - linkType: hard - "@types/minimatch@npm:^3.0.3, @types/minimatch@npm:^3.0.5": version: 3.0.5 resolution: "@types/minimatch@npm:3.0.5" @@ -18516,6 +18578,13 @@ __metadata: languageName: node linkType: hard +"@types/minimatch@npm:^5.0.0, @types/minimatch@npm:^5.1.2": + version: 5.1.2 + resolution: "@types/minimatch@npm:5.1.2" + checksum: 0391a282860c7cb6fe262c12b99564732401bdaa5e395bee9ca323c312c1a0f45efbf34dce974682036e857db59a5c9b1da522f3d6055aeead7097264c8705a8 + languageName: node + linkType: hard + "@types/minimist@npm:^1.2.0": version: 1.2.5 resolution: "@types/minimist@npm:1.2.5" @@ -18587,11 +18656,11 @@ __metadata: linkType: hard "@types/node@npm:*, @types/node@npm:>=12.12.47, @types/node@npm:>=13.7.0, @types/node@npm:^20.1.1": - version: 20.9.5 - resolution: "@types/node@npm:20.9.5" + version: 20.10.1 + resolution: "@types/node@npm:20.10.1" dependencies: undici-types: ~5.26.4 - checksum: 050ebd958e31ad21143f93614a25b15a8de5d0004338359409f76a661f734c4549698d852f21176c62f5661dacb572a090669cffef93cf6361c959e4d27d8d95 + checksum: 9dfdcd2496ce535dba0ae496985d6e991a8a5d70a180db3a94c947a2123d99318a95dce4aa2a192f7e57c3afa3fdb44d6fd63e18efd49568950d6c239dadcc39 languageName: node linkType: hard @@ -18610,9 +18679,9 @@ __metadata: linkType: hard "@types/node@npm:^16.11.26, @types/node@npm:^16.7.10, @types/node@npm:^16.9.2": - version: 16.18.65 - resolution: "@types/node@npm:16.18.65" - checksum: c53cd5bfff0b6868c2735c7afb24008b23ce204ce090cc4fd1bbe0515de32f1088d401183ecb1a329ea69ab81591ca4935875d0744abb4bd60f4f85810d8446e + version: 16.18.66 + resolution: "@types/node@npm:16.18.66" + checksum: 84408652068f8c7e3aa1fa6f333be8044ae802a700eb3b4101fc27d047eefcc742e636809a6318fb0100b1355658649f4a0fc2398fb04f27b1a357f6f3579cd0 languageName: node linkType: hard @@ -18624,11 +18693,11 @@ __metadata: linkType: hard "@types/node@npm:^18.17.8": - version: 18.18.13 - resolution: "@types/node@npm:18.18.13" + version: 18.19.0 + resolution: "@types/node@npm:18.19.0" dependencies: undici-types: ~5.26.4 - checksum: e36d7a0ea6ce8fb771fd84dc2412935408448c1fed098205d3103be661852fc40d22a987e6c6c926a20719a15266e3071c8d4fd2548a96ad6c3c74eb474b5e63 + checksum: cf91c4dd7c219e101dee820c69dc5ad497acbcc0a44a7d5efb00fdea4bede247446ce5f4bbd3d115ca539dd21321d840ad42eaacce3321cde1f732b29a621fb8 languageName: node linkType: hard @@ -18789,7 +18858,7 @@ __metadata: languageName: node linkType: hard -"@types/prop-types@npm:*, @types/prop-types@npm:^15.0.0, @types/prop-types@npm:^15.7.10, @types/prop-types@npm:^15.7.3": +"@types/prop-types@npm:*, @types/prop-types@npm:^15.0.0, @types/prop-types@npm:^15.7.11, @types/prop-types@npm:^15.7.3": version: 15.7.11 resolution: "@types/prop-types@npm:15.7.11" checksum: 7519ff11d06fbf6b275029fe03fff9ec377b4cb6e864cac34d87d7146c7f5a7560fd164bdc1d2dbe00b60c43713631251af1fd3d34d46c69cd354602bc0c7c54 @@ -18912,12 +18981,12 @@ __metadata: languageName: node linkType: hard -"@types/react-transition-group@npm:^4.2.0, @types/react-transition-group@npm:^4.4.8": - version: 4.4.8 - resolution: "@types/react-transition-group@npm:4.4.8" +"@types/react-transition-group@npm:^4.2.0, @types/react-transition-group@npm:^4.4.9": + version: 4.4.9 + resolution: "@types/react-transition-group@npm:4.4.9" dependencies: "@types/react": "*" - checksum: ad7ba2bce97631fda9d89b4ed9772489bd050fec3ccd7563041b206dbe219d37d22e0d7731b1f90f56e89daf40e69ba16beba8066c42165bf8a584533feb6a2c + checksum: be9e256e53919a7cf3b4a075f6d01c0a2dd3a67911dd28276aa6158be4beade4ca5327cbf1f096c28b413e04989f069122319b02e5a09c280d903a0accea9ead languageName: node linkType: hard @@ -18940,13 +19009,13 @@ __metadata: linkType: hard "@types/react@npm:^18": - version: 18.2.38 - resolution: "@types/react@npm:18.2.38" + version: 18.2.39 + resolution: "@types/react@npm:18.2.39" dependencies: "@types/prop-types": "*" "@types/scheduler": "*" csstype: ^3.0.2 - checksum: 71f8c167173d32252be8b2d3c1c76b3570b94d2fbbd139da86d146be453626f5777e12c2781559119637520dbef9f91cffe968f67b5901618f29226d49fad326 + checksum: 9bcb1f1f060f1bf8f4730fb1c7772d0323a6e707f274efee3b976c40d92af4677df4d88e9135faaacf34e13e02f92ef24eb7d0cbcf7fb75c1883f5623ccb19f4 languageName: node linkType: hard @@ -18997,12 +19066,10 @@ __metadata: languageName: node linkType: hard -"@types/resolve@npm:1.17.1": - version: 1.17.1 - resolution: "@types/resolve@npm:1.17.1" - dependencies: - "@types/node": "*" - checksum: dc6a6df507656004e242dcb02c784479deca516d5f4b58a1707e708022b269ae147e1da0521f3e8ad0d63638869d87e0adc023f0bd5454aa6f72ac66c7525cf5 +"@types/resolve@npm:1.20.2": + version: 1.20.2 + resolution: "@types/resolve@npm:1.20.2" + checksum: 61c2cad2499ffc8eab36e3b773945d337d848d3ac6b7b0a87c805ba814bc838ef2f262fc0f109bfd8d2e0898ff8bd80ad1025f9ff64f1f71d3d4294c9f14e5f6 languageName: node linkType: hard @@ -19065,9 +19132,9 @@ __metadata: linkType: hard "@types/semver@npm:^7.3.12, @types/semver@npm:^7.3.8, @types/semver@npm:^7.5.0": - version: 7.5.5 - resolution: "@types/semver@npm:7.5.5" - checksum: 533e6c93d1262d65f449423d94a445f7f3db0672e7429f21b6a1636d6051dbab3a2989ddcda9b79c69bb37830931d09fc958a65305a891357f5cea3257c297f5 + version: 7.5.6 + resolution: "@types/semver@npm:7.5.6" + checksum: 563a0120ec0efcc326567db2ed920d5d98346f3638b6324ea6b50222b96f02a8add3c51a916b6897b51523aad8ac227d21d3dcf8913559f1bfc6c15b14d23037 languageName: node linkType: hard @@ -19450,15 +19517,15 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/eslint-plugin@npm:6.11.0": - version: 6.11.0 - resolution: "@typescript-eslint/eslint-plugin@npm:6.11.0" +"@typescript-eslint/eslint-plugin@npm:^6.12.0": + version: 6.13.1 + resolution: "@typescript-eslint/eslint-plugin@npm:6.13.1" dependencies: "@eslint-community/regexpp": ^4.5.1 - "@typescript-eslint/scope-manager": 6.11.0 - "@typescript-eslint/type-utils": 6.11.0 - "@typescript-eslint/utils": 6.11.0 - "@typescript-eslint/visitor-keys": 6.11.0 + "@typescript-eslint/scope-manager": 6.13.1 + "@typescript-eslint/type-utils": 6.13.1 + "@typescript-eslint/utils": 6.13.1 + "@typescript-eslint/visitor-keys": 6.13.1 debug: ^4.3.4 graphemer: ^1.4.0 ignore: ^5.2.4 @@ -19471,25 +19538,25 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 8ba9ce7ce8609a044e405baf57cc84d6973d7676950c870288d7eae2dba44b36664e3f4d90b94a4de08e17259fe8baa7790750cd4e5391dbe2a2743497d7fae2 + checksum: 568093d76c200a8502047d74f29300110a59b9f2a5cbf995a6cbe419c803a7ec22220e9592a884401d2dde72c79346b4cc0ee393e7b422924ad4a8a2040af3b0 languageName: node linkType: hard "@typescript-eslint/parser@npm:^6.7.2": - version: 6.11.0 - resolution: "@typescript-eslint/parser@npm:6.11.0" + version: 6.13.1 + resolution: "@typescript-eslint/parser@npm:6.13.1" dependencies: - "@typescript-eslint/scope-manager": 6.11.0 - "@typescript-eslint/types": 6.11.0 - "@typescript-eslint/typescript-estree": 6.11.0 - "@typescript-eslint/visitor-keys": 6.11.0 + "@typescript-eslint/scope-manager": 6.13.1 + "@typescript-eslint/types": 6.13.1 + "@typescript-eslint/typescript-estree": 6.13.1 + "@typescript-eslint/visitor-keys": 6.13.1 debug: ^4.3.4 peerDependencies: eslint: ^7.0.0 || ^8.0.0 peerDependenciesMeta: typescript: optional: true - checksum: e9cb175e3537b82aa8cd39641ecb4e656586f89f8365cf05400b5aa8794dac0c8c10c6aa2fd7c13a684f62c1493f5e41c5534df49d377abe9dc89d861a51195c + checksum: 58b7fef6f2d02c8f4737f9908a8d335a20bee20dba648233a69f28e7b39237791d2b9fbb818e628dcc053ddf16507b161ace7f1139e093d72365f1270c426de3 languageName: node linkType: hard @@ -19503,22 +19570,22 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:6.11.0": - version: 6.11.0 - resolution: "@typescript-eslint/scope-manager@npm:6.11.0" +"@typescript-eslint/scope-manager@npm:6.13.1": + version: 6.13.1 + resolution: "@typescript-eslint/scope-manager@npm:6.13.1" dependencies: - "@typescript-eslint/types": 6.11.0 - "@typescript-eslint/visitor-keys": 6.11.0 - checksum: d219a96fd80fb14176cdcc47b070e870c73ccc0dfb32a8657f6ceaefb613dc0ea240a77250dcfc437d9c9360ca165c2765d4cf8fe689dae7e9eee2c0d6a98a50 + "@typescript-eslint/types": 6.13.1 + "@typescript-eslint/visitor-keys": 6.13.1 + checksum: 109a213f82719e10f8c6a0168f2e105dc1369c7e0c075c1f30af137030fc866a3a585a77ff78a9a3538afc213061c8aedbb4462a91f26cbd90eefbab8b89ea10 languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:6.11.0": - version: 6.11.0 - resolution: "@typescript-eslint/type-utils@npm:6.11.0" +"@typescript-eslint/type-utils@npm:6.13.1": + version: 6.13.1 + resolution: "@typescript-eslint/type-utils@npm:6.13.1" dependencies: - "@typescript-eslint/typescript-estree": 6.11.0 - "@typescript-eslint/utils": 6.11.0 + "@typescript-eslint/typescript-estree": 6.13.1 + "@typescript-eslint/utils": 6.13.1 debug: ^4.3.4 ts-api-utils: ^1.0.1 peerDependencies: @@ -19526,7 +19593,7 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 2effbe62ae3b12f8a88663072f68a5dcb1135d9ee3c09a0d9fcf49b943837c0a5966e907d4a1a15c27ddf82af2fcf7f6e004655d3e1f7a17c21596469771ff7d + checksum: e39d28dd2f3b47a26b4f6aa2c7a301bdd769ce9148d734be93441a813c3d1111eba1d655677355bba5519f3d4dbe93e4ff4e46830216b0302df0070bf7a80057 languageName: node linkType: hard @@ -19537,10 +19604,10 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/types@npm:6.11.0": - version: 6.11.0 - resolution: "@typescript-eslint/types@npm:6.11.0" - checksum: ca8a11320286c9b0759a70ec83b9fd99937c9686fafdd41d8ea09ed7b2fa12e6b342bf65547efe5495926cd04cfc6488315920e3caffd27f12d42cb9a8cf88c8 +"@typescript-eslint/types@npm:6.13.1": + version: 6.13.1 + resolution: "@typescript-eslint/types@npm:6.13.1" + checksum: bb1d52f1646bab9acd3ec874567ffbaaaf7fe4a5f79845bdacbfea46d15698e58d45797da05b08c23f9496a17229b7f2c1363d000fd89ce4e79874fd57ba1d4a languageName: node linkType: hard @@ -19562,12 +19629,12 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:6.11.0": - version: 6.11.0 - resolution: "@typescript-eslint/typescript-estree@npm:6.11.0" +"@typescript-eslint/typescript-estree@npm:6.13.1": + version: 6.13.1 + resolution: "@typescript-eslint/typescript-estree@npm:6.13.1" dependencies: - "@typescript-eslint/types": 6.11.0 - "@typescript-eslint/visitor-keys": 6.11.0 + "@typescript-eslint/types": 6.13.1 + "@typescript-eslint/visitor-keys": 6.13.1 debug: ^4.3.4 globby: ^11.1.0 is-glob: ^4.0.3 @@ -19576,24 +19643,24 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: e137ba7c4cad08853a44d9c40072496ca5f2d440828be9fd2d207a59db56b05a6dcb4756f3ba341ee2ae714de45df80114477946d30801c5a46eed67314fd9c6 + checksum: 09aa0f5cbd60e84df4f58f3d479be352549600b24dbefe75c686ea89252526c52c1c06ce1ae56c0405dd7337002e741c2ba02b71fb1caa3b94a740a70fcc8699 languageName: node linkType: hard -"@typescript-eslint/utils@npm:6.11.0": - version: 6.11.0 - resolution: "@typescript-eslint/utils@npm:6.11.0" +"@typescript-eslint/utils@npm:6.13.1": + version: 6.13.1 + resolution: "@typescript-eslint/utils@npm:6.13.1" dependencies: "@eslint-community/eslint-utils": ^4.4.0 "@types/json-schema": ^7.0.12 "@types/semver": ^7.5.0 - "@typescript-eslint/scope-manager": 6.11.0 - "@typescript-eslint/types": 6.11.0 - "@typescript-eslint/typescript-estree": 6.11.0 + "@typescript-eslint/scope-manager": 6.13.1 + "@typescript-eslint/types": 6.13.1 + "@typescript-eslint/typescript-estree": 6.13.1 semver: ^7.5.4 peerDependencies: eslint: ^7.0.0 || ^8.0.0 - checksum: e90aa2c8c56038a48de65a5303f9e4a4a70bb0d4d0a05cfcd28157fc0f06b2fc186c2e76a495f4540a903ea37577daa1403bab923d940114ec27a6326153d60f + checksum: 14f64840869c8755af4d287cfc74abc424dc139559e87ca1a8b0e850f4fa56311d99dfb61a43dd4433eae5914be12b4b3390e55de1f236dce6701830d17e31c9 languageName: node linkType: hard @@ -19625,19 +19692,19 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:6.11.0": - version: 6.11.0 - resolution: "@typescript-eslint/visitor-keys@npm:6.11.0" +"@typescript-eslint/visitor-keys@npm:6.13.1": + version: 6.13.1 + resolution: "@typescript-eslint/visitor-keys@npm:6.13.1" dependencies: - "@typescript-eslint/types": 6.11.0 + "@typescript-eslint/types": 6.13.1 eslint-visitor-keys: ^3.4.1 - checksum: 6aae9dd79963bbefbf2e310015b909627da541a13ab4d8359eea3c86c34fdbb91e583f65b5a99dee1959f7c5d67b21b45e5a05c63ddb4b82dacd60c890ce8b25 + checksum: d15d362203a2fe995ea62a59d5b44c15c8fb1fb30ff59dd1542a980f75b3b62035303dfb781d83709921613f6ac8cc5bf57b70f6e20d820aec8b7911f07152e9 languageName: node linkType: hard -"@uiw/codemirror-extensions-basic-setup@npm:4.21.20": - version: 4.21.20 - resolution: "@uiw/codemirror-extensions-basic-setup@npm:4.21.20" +"@uiw/codemirror-extensions-basic-setup@npm:4.21.21": + version: 4.21.21 + resolution: "@uiw/codemirror-extensions-basic-setup@npm:4.21.21" dependencies: "@codemirror/autocomplete": ^6.0.0 "@codemirror/commands": ^6.0.0 @@ -19654,19 +19721,19 @@ __metadata: "@codemirror/search": ">=6.0.0" "@codemirror/state": ">=6.0.0" "@codemirror/view": ">=6.0.0" - checksum: 76a6caf05bbcf06c515c680ab952e79b8470ca10c9d8c3166fdb05310232239782b170da276b1dbf33900ca894332fc2aba3bd6955665b2c2580548c7e17cbc0 + checksum: 5d96ec930be286b5e324ee8dd57128171c72a8e333b4b991a5c4f33a05420a638def3776bd1a77c5b88184f7d3cbc6828b5c9a42b3b490f51908bedf69eaa0f0 languageName: node linkType: hard "@uiw/react-codemirror@npm:^4.9.3": - version: 4.21.20 - resolution: "@uiw/react-codemirror@npm:4.21.20" + version: 4.21.21 + resolution: "@uiw/react-codemirror@npm:4.21.21" dependencies: "@babel/runtime": ^7.18.6 "@codemirror/commands": ^6.1.0 "@codemirror/state": ^6.1.1 "@codemirror/theme-one-dark": ^6.0.0 - "@uiw/codemirror-extensions-basic-setup": 4.21.20 + "@uiw/codemirror-extensions-basic-setup": 4.21.21 codemirror: ^6.0.0 peerDependencies: "@babel/runtime": ">=7.11.0" @@ -19676,7 +19743,7 @@ __metadata: codemirror: ">=6.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" - checksum: 63ea348a922c535ff91c8bff1c1dac40ced0227d1c17139142dbc28017bb254cc32428a15686660ec4f4cd6935c574d627b6a4872910357af51da8e90faaf4da + checksum: 6a8500290bf0a739fd345221465b10112e0f758914bc8aa28ce0b9904b76489ae018d390f6a2d7dd7752b21f0ed87ba3fedb45b4185d04e4985abf0eb22e6d75 languageName: node linkType: hard @@ -20608,7 +20675,21 @@ __metadata: languageName: node linkType: hard -"archiver@npm:^5.0.2, archiver@npm:^5.3.1": +"archiver-utils@npm:^4.0.1": + version: 4.0.1 + resolution: "archiver-utils@npm:4.0.1" + dependencies: + glob: ^8.0.0 + graceful-fs: ^4.2.0 + lazystream: ^1.0.0 + lodash: ^4.17.15 + normalize-path: ^3.0.0 + readable-stream: ^3.6.0 + checksum: 2917cdf63a912c74002a4a1e6de3076a4691030b4e722efdd6d862447b61cd64c8b7688d331b1d35f8d4fc661d6e34f91bc1ffc79478fca2e48ad060acece18c + languageName: node + linkType: hard + +"archiver@npm:^5.3.1": version: 5.3.2 resolution: "archiver@npm:5.3.2" dependencies: @@ -20623,6 +20704,21 @@ __metadata: languageName: node linkType: hard +"archiver@npm:^6.0.0": + version: 6.0.1 + resolution: "archiver@npm:6.0.1" + dependencies: + archiver-utils: ^4.0.1 + async: ^3.2.4 + buffer-crc32: ^0.2.1 + readable-stream: ^3.6.0 + readdir-glob: ^1.1.2 + tar-stream: ^3.0.0 + zip-stream: ^5.0.1 + checksum: 20549eef7366173440a86873387412226568744a410626f826998b0dda85fe84e739c542d9db9aca3923b772436eb795eafdff29c2983e683355fdd9faaa0fdb + languageName: node + linkType: hard + "are-we-there-yet@npm:^2.0.0": version: 2.0.0 resolution: "are-we-there-yet@npm:2.0.0" @@ -21174,6 +21270,13 @@ __metadata: languageName: node linkType: hard +"b4a@npm:^1.6.4": + version: 1.6.4 + resolution: "b4a@npm:1.6.4" + checksum: 81b086f9af1f8845fbef4476307236bda3d660c158c201db976f19cdce05f41f93110ab6b12fd7a2696602a490cc43d5410ee36a56d6eef93afb0d6ca69ac3b2 + languageName: node + linkType: hard + "babel-core@npm:^7.0.0-bridge.0": version: 7.0.0-bridge.0 resolution: "babel-core@npm:7.0.0-bridge.0" @@ -21974,10 +22077,10 @@ __metadata: languageName: node linkType: hard -"builtin-modules@npm:^3.0.0": - version: 3.2.0 - resolution: "builtin-modules@npm:3.2.0" - checksum: 0265aa1ba78e1a16f4e18668d815cb43fb364e6a6b8aa9189c6f44c7b894a551a43b323c40206959d2d4b2568c1f2805607ad6c88adc306a776ce6904cca6715 +"builtin-modules@npm:^3.3.0": + version: 3.3.0 + resolution: "builtin-modules@npm:3.3.0" + checksum: db021755d7ed8be048f25668fe2117620861ef6703ea2c65ed2779c9e3636d5c3b82325bd912244293959ff3ae303afa3471f6a15bf5060c103e4cc3a839749d languageName: node linkType: hard @@ -22440,7 +22543,7 @@ __metadata: languageName: node linkType: hard -"ci-info@npm:^3.1.0, ci-info@npm:^3.2.0, ci-info@npm:^3.7.0": +"ci-info@npm:^3.2.0, ci-info@npm:^3.7.0": version: 3.8.0 resolution: "ci-info@npm:3.8.0" checksum: d0a4d3160497cae54294974a7246202244fff031b0a6ea20dd57b10ec510aa17399c41a1b0982142c105f3255aff2173e5c0dd7302ee1b2f28ba3debda375098 @@ -23095,6 +23198,18 @@ __metadata: languageName: node linkType: hard +"compress-commons@npm:^5.0.1": + version: 5.0.1 + resolution: "compress-commons@npm:5.0.1" + dependencies: + crc-32: ^1.2.0 + crc32-stream: ^5.0.0 + normalize-path: ^3.0.0 + readable-stream: ^3.6.0 + checksum: 65a68e56211a8d1dbe9dab0d35f1bd60a4df27aa01e6c3f0883080263e228c460758bab4f083637a380d4a96d2326722972a42ea1951360cc69728a3915f209f + languageName: node + linkType: hard + "compressible@npm:^2.0.12, compressible@npm:~2.0.16": version: 2.0.18 resolution: "compressible@npm:2.0.18" @@ -23552,6 +23667,16 @@ __metadata: languageName: node linkType: hard +"crc32-stream@npm:^5.0.0": + version: 5.0.0 + resolution: "crc32-stream@npm:5.0.0" + dependencies: + crc-32: ^1.2.0 + readable-stream: ^3.4.0 + checksum: 8e5dd04f22f3fbecc623492395107fbed2114f225bd606e39e8ed338f2fc1c454ac02a05741243620ab526473cb867fa86411a44a7ffcd88457cc1c2af82d0bc + languageName: node + linkType: hard + "create-ecdh@npm:^4.0.0": version: 4.0.3 resolution: "create-ecdh@npm:4.0.3" @@ -23793,13 +23918,12 @@ __metadata: languageName: node linkType: hard -"css-in-js-utils@npm:^2.0.0": - version: 2.0.1 - resolution: "css-in-js-utils@npm:2.0.1" +"css-in-js-utils@npm:^3.1.0": + version: 3.1.0 + resolution: "css-in-js-utils@npm:3.1.0" dependencies: - hyphenate-style-name: ^1.0.2 - isobject: ^3.0.1 - checksum: c9964c4708216954c468b69bbee2d971fd759ada4f40637b8ca4d3f79caba4818d0532a4f190ac560227c08742ad063ffec7a30afddc4d96b66a18c3a008f0d8 + hyphenate-style-name: ^1.0.3 + checksum: 066318e918c04a5e5bce46b38fe81052ea6ac051bcc6d3c369a1d59ceb1546cb2b6086901ab5d22be084122ee3732169996a3dfb04d3406eaee205af77aec61b languageName: node linkType: hard @@ -24025,7 +24149,7 @@ __metadata: languageName: node linkType: hard -"csstype@npm:^3.0.2, csstype@npm:^3.0.6, csstype@npm:^3.1.2": +"csstype@npm:^3.0.2, csstype@npm:^3.1.2": version: 3.0.9 resolution: "csstype@npm:3.0.9" checksum: 199f9af7e673f9f188525c3102a329d637ff46c52f6385a4427ff5cb17adcb736189150170a7af7c5701d18d7704bdad130273f4aa7e44c6c4f9967e6115dc93 @@ -25809,31 +25933,31 @@ __metadata: linkType: hard "esbuild@npm:^0.19.0": - version: 0.19.7 - resolution: "esbuild@npm:0.19.7" + version: 0.19.8 + resolution: "esbuild@npm:0.19.8" dependencies: - "@esbuild/android-arm": 0.19.7 - "@esbuild/android-arm64": 0.19.7 - "@esbuild/android-x64": 0.19.7 - "@esbuild/darwin-arm64": 0.19.7 - "@esbuild/darwin-x64": 0.19.7 - "@esbuild/freebsd-arm64": 0.19.7 - "@esbuild/freebsd-x64": 0.19.7 - "@esbuild/linux-arm": 0.19.7 - "@esbuild/linux-arm64": 0.19.7 - "@esbuild/linux-ia32": 0.19.7 - "@esbuild/linux-loong64": 0.19.7 - "@esbuild/linux-mips64el": 0.19.7 - "@esbuild/linux-ppc64": 0.19.7 - "@esbuild/linux-riscv64": 0.19.7 - "@esbuild/linux-s390x": 0.19.7 - "@esbuild/linux-x64": 0.19.7 - "@esbuild/netbsd-x64": 0.19.7 - "@esbuild/openbsd-x64": 0.19.7 - "@esbuild/sunos-x64": 0.19.7 - "@esbuild/win32-arm64": 0.19.7 - "@esbuild/win32-ia32": 0.19.7 - "@esbuild/win32-x64": 0.19.7 + "@esbuild/android-arm": 0.19.8 + "@esbuild/android-arm64": 0.19.8 + "@esbuild/android-x64": 0.19.8 + "@esbuild/darwin-arm64": 0.19.8 + "@esbuild/darwin-x64": 0.19.8 + "@esbuild/freebsd-arm64": 0.19.8 + "@esbuild/freebsd-x64": 0.19.8 + "@esbuild/linux-arm": 0.19.8 + "@esbuild/linux-arm64": 0.19.8 + "@esbuild/linux-ia32": 0.19.8 + "@esbuild/linux-loong64": 0.19.8 + "@esbuild/linux-mips64el": 0.19.8 + "@esbuild/linux-ppc64": 0.19.8 + "@esbuild/linux-riscv64": 0.19.8 + "@esbuild/linux-s390x": 0.19.8 + "@esbuild/linux-x64": 0.19.8 + "@esbuild/netbsd-x64": 0.19.8 + "@esbuild/openbsd-x64": 0.19.8 + "@esbuild/sunos-x64": 0.19.8 + "@esbuild/win32-arm64": 0.19.8 + "@esbuild/win32-ia32": 0.19.8 + "@esbuild/win32-x64": 0.19.8 dependenciesMeta: "@esbuild/android-arm": optional: true @@ -25881,7 +26005,7 @@ __metadata: optional: true bin: esbuild: bin/esbuild - checksum: a5d979224d47ae0cc6685447eb8f1ceaf7b67f5eaeaac0246f4d589ff7d81b08e4502a6245298d948f13e9b571ac8556a6d83b084af24954f762b1cfe59dbe55 + checksum: 1dff99482ecbfcc642ec66c71e4dc5c73ce6aef68e8158a4937890b570e86a95959ac47e0f14785ba70df5a673ae4289df88a162e9759b02367ed28074cee8ba languageName: node linkType: hard @@ -26152,13 +26276,35 @@ __metadata: linkType: hard "eslint-plugin-testing-library@npm:^6.0.0": - version: 6.1.2 - resolution: "eslint-plugin-testing-library@npm:6.1.2" + version: 6.2.0 + resolution: "eslint-plugin-testing-library@npm:6.2.0" dependencies: "@typescript-eslint/utils": ^5.58.0 peerDependencies: eslint: ^7.5.0 || ^8.0.0 - checksum: 74e6b1bdff52e9a3937be1ae0e3ddb6cfedcd97044ac5275be6e7472c77cf8ae0f6e29fd7d3b90aac1318e22850086e73ee8dde7de8d9034be59cdfed9ba7bd7 + checksum: 7af7e0a1eee44c6ba65ce2ae99f8e46ce709a319f4cce778bb0af2dda5828d78f3a81e8989c7b691a8b9b9fef102b56136209aac700038b9e64794600b0d12db + languageName: node + linkType: hard + +"eslint-plugin-unused-imports@npm:^3.0.0": + version: 3.0.0 + resolution: "eslint-plugin-unused-imports@npm:3.0.0" + dependencies: + eslint-rule-composer: ^0.3.0 + peerDependencies: + "@typescript-eslint/eslint-plugin": ^6.0.0 + eslint: ^8.0.0 + peerDependenciesMeta: + "@typescript-eslint/eslint-plugin": + optional: true + checksum: 51666f62cc8dccba2895ced83f3c1e0b78b68c357e17360e156c4db548bfdeda34cbd8725192fb4903f22d5069400fb22ded6039631df01ee82fd618dc307247 + languageName: node + linkType: hard + +"eslint-rule-composer@npm:^0.3.0": + version: 0.3.0 + resolution: "eslint-rule-composer@npm:0.3.0" + checksum: c2f57cded8d1c8f82483e0ce28861214347e24fd79fd4144667974cd334d718f4ba05080aaef2399e3bbe36f7d6632865110227e6b176ed6daa2d676df9281b1 languageName: node linkType: hard @@ -26330,13 +26476,6 @@ __metadata: languageName: node linkType: hard -"estree-walker@npm:^1.0.1": - version: 1.0.1 - resolution: "estree-walker@npm:1.0.1" - checksum: 7e70da539691f6db03a08e7ce94f394ce2eef4180e136d251af299d41f92fb2d28ebcd9a6e393e3728d7970aeb5358705ddf7209d52fbcb2dd4693f95dcf925f - languageName: node - linkType: hard - "estree-walker@npm:^2.0.1, estree-walker@npm:^2.0.2": version: 2.0.2 resolution: "estree-walker@npm:2.0.2" @@ -26463,7 +26602,6 @@ __metadata: "@backstage/plugin-catalog-import": "workspace:^" "@backstage/plugin-catalog-react": "workspace:^" "@backstage/plugin-catalog-unprocessed-entities": "workspace:^" - "@backstage/plugin-circleci": "workspace:^" "@backstage/plugin-cloudbuild": "workspace:^" "@backstage/plugin-code-coverage": "workspace:^" "@backstage/plugin-cost-insights": "workspace:^" @@ -26510,6 +26648,7 @@ __metadata: "@backstage/plugin-user-settings": "workspace:^" "@backstage/test-utils": "workspace:^" "@backstage/theme": "workspace:^" + "@circleci/backstage-plugin": ^0.1.1 "@material-ui/core": ^4.12.2 "@material-ui/icons": ^4.9.1 "@material-ui/lab": 4.0.0-alpha.61 @@ -26572,7 +26711,6 @@ __metadata: "@backstage/plugin-catalog-import": "workspace:^" "@backstage/plugin-catalog-react": "workspace:^" "@backstage/plugin-catalog-unprocessed-entities": "workspace:^" - "@backstage/plugin-circleci": "workspace:^" "@backstage/plugin-cloudbuild": "workspace:^" "@backstage/plugin-code-coverage": "workspace:^" "@backstage/plugin-cost-insights": "workspace:^" @@ -26622,6 +26760,7 @@ __metadata: "@backstage/plugin-user-settings": "workspace:^" "@backstage/test-utils": "workspace:^" "@backstage/theme": "workspace:^" + "@circleci/backstage-plugin": ^0.1.1 "@material-ui/core": ^4.12.2 "@material-ui/icons": ^4.9.1 "@material-ui/lab": 4.0.0-alpha.61 @@ -26650,7 +26789,7 @@ __metadata: react-use: ^17.2.4 vite: ^4.4.9 vite-plugin-html: ^3.2.0 - vite-plugin-node-polyfills: ^0.16.0 + vite-plugin-node-polyfills: ^0.17.0 zen-observable: ^0.10.0 languageName: unknown linkType: soft @@ -27095,6 +27234,13 @@ __metadata: languageName: node linkType: hard +"fast-fifo@npm:^1.1.0, fast-fifo@npm:^1.2.0": + version: 1.3.2 + resolution: "fast-fifo@npm:1.3.2" + checksum: 6bfcba3e4df5af7be3332703b69a7898a8ed7020837ec4395bb341bd96cc3a6d86c3f6071dd98da289618cf2234c70d84b2a6f09a33dd6f988b1ff60d8e54275 + languageName: node + linkType: hard + "fast-glob@npm:^3.2.11, fast-glob@npm:^3.2.12, fast-glob@npm:^3.2.9": version: 3.2.12 resolution: "fast-glob@npm:3.2.12" @@ -27136,6 +27282,13 @@ __metadata: languageName: node linkType: hard +"fast-loops@npm:^1.1.3": + version: 1.1.3 + resolution: "fast-loops@npm:1.1.3" + checksum: b674378ba2ed8364ca1a00768636e88b22201c8d010fa62a8588a4cace04f90bac46714c13cf638be82b03438d2fe813600da32291fb47297a1bd7fa6cef0cee + languageName: node + linkType: hard + "fast-memoize@npm:^2.5.2": version: 2.5.2 resolution: "fast-memoize@npm:2.5.2" @@ -28318,7 +28471,7 @@ __metadata: languageName: node linkType: hard -"glob@npm:^8.0.1, glob@npm:^8.0.3": +"glob@npm:^8.0.0, glob@npm:^8.0.1, glob@npm:^8.0.3": version: 8.1.0 resolution: "glob@npm:8.1.0" dependencies: @@ -29688,12 +29841,13 @@ __metadata: languageName: node linkType: hard -"inline-style-prefixer@npm:^6.0.0": - version: 6.0.0 - resolution: "inline-style-prefixer@npm:6.0.0" +"inline-style-prefixer@npm:^7.0.0": + version: 7.0.0 + resolution: "inline-style-prefixer@npm:7.0.0" dependencies: - css-in-js-utils: ^2.0.0 - checksum: 1331a6184bed03d145e3b9a98363fc7a245590ea1a495df7376a99386737893d4318f8ecf873e2be8431cff1de91512b7a4b9e36326933d5647db0088b63ef4f + css-in-js-utils: ^3.1.0 + fast-loops: ^1.1.3 + checksum: 89fd73eb06e7392e24032ea33b8b33ae7f9a24298f2d9ebbf7b31a3a3934247270047f4f49a454a363aace14e25c3a20fd97465405b0399cc888e5a2bc04ec05 languageName: node linkType: hard @@ -29975,12 +30129,12 @@ __metadata: languageName: node linkType: hard -"is-builtin-module@npm:^3.1.0": - version: 3.1.0 - resolution: "is-builtin-module@npm:3.1.0" +"is-builtin-module@npm:^3.2.1": + version: 3.2.1 + resolution: "is-builtin-module@npm:3.2.1" dependencies: - builtin-modules: ^3.0.0 - checksum: f1e5dd2cd5f252d4d799b20a0c8c4f7e9c399c4d141749af76ca0121058d4062c3015d026f1b1409dd3d2a4ddfb9b15cf6eb9c370fed53fea8652ce35b5e95cb + builtin-modules: ^3.3.0 + checksum: e8f0ffc19a98240bda9c7ada84d846486365af88d14616e737d280d378695c8c448a621dcafc8332dbf0fcd0a17b0763b845400709963fa9151ddffece90ae88 languageName: node linkType: hard @@ -30002,17 +30156,6 @@ __metadata: languageName: node linkType: hard -"is-ci@npm:^3.0.1": - version: 3.0.1 - resolution: "is-ci@npm:3.0.1" - dependencies: - ci-info: ^3.2.0 - bin: - is-ci: bin.js - checksum: 192c66dc7826d58f803ecae624860dccf1899fc1f3ac5505284c0a5cf5f889046ffeb958fa651e5725d5705c5bcb14f055b79150ea5fcad7456a9569de60260e - languageName: node - linkType: hard - "is-core-module@npm:^2.1.0, is-core-module@npm:^2.13.0, is-core-module@npm:^2.13.1, is-core-module@npm:^2.9.0": version: 2.13.1 resolution: "is-core-module@npm:2.13.1" @@ -32604,6 +32747,13 @@ __metadata: languageName: node linkType: hard +"linkifyjs@npm:4.1.3": + version: 4.1.3 + resolution: "linkifyjs@npm:4.1.3" + checksum: 023d467499a717a49ebbfa256a80cb2811a3b038ff2593e5be0fb8a4715b0a63bf80c571838e19e120833d5b9874464f3a1448965c8eebbde8c19458b3a6c6e4 + languageName: node + linkType: hard + "lint-staged@npm:^15.0.0": version: 15.1.0 resolution: "lint-staged@npm:15.1.0" @@ -33228,15 +33378,6 @@ __metadata: languageName: node linkType: hard -"magic-string@npm:^0.27.0": - version: 0.27.0 - resolution: "magic-string@npm:0.27.0" - dependencies: - "@jridgewell/sourcemap-codec": ^1.4.13 - checksum: 273faaa50baadb7a2df6e442eac34ad611304fc08fe16e24fe2e472fd944bfcb73ffb50d2dc972dc04e92784222002af46868cb9698b1be181c81830fd95a13e - languageName: node - linkType: hard - "magic-string@npm:^0.30.3": version: 0.30.5 resolution: "magic-string@npm:0.30.5" @@ -34713,9 +34854,9 @@ __metadata: languageName: node linkType: hard -"msw@npm:^2.0.8": - version: 2.0.8 - resolution: "msw@npm:2.0.8" +"msw@npm:^2.0.0, msw@npm:^2.0.8": + version: 2.0.9 + resolution: "msw@npm:2.0.9" dependencies: "@bundled-es-modules/cookie": ^2.0.0 "@bundled-es-modules/js-levenshtein": ^2.0.1 @@ -34745,7 +34886,7 @@ __metadata: optional: true bin: msw: cli/index.js - checksum: 8737dae4cf516d8c591ad8d3751cef2f8f07d10a1f995b8cd7d19d8e955a29352dd6fc092f04bc0e1d0025663ccc3c64ae7e56e29c8d4a4b13699e3ee4747c46 + checksum: 77bdb10a019f1d382bfc66b3005752e722f2891e9f8a4f9d3d17f5641ca4d8afb02bc67c9325dfd25708702443e0f9b8e48fbc580faa37c21e090425df93089e languageName: node linkType: hard @@ -34841,22 +34982,22 @@ __metadata: languageName: node linkType: hard -"nano-css@npm:^5.3.1": - version: 5.3.1 - resolution: "nano-css@npm:5.3.1" +"nano-css@npm:^5.6.1": + version: 5.6.1 + resolution: "nano-css@npm:5.6.1" dependencies: + "@jridgewell/sourcemap-codec": ^1.4.15 css-tree: ^1.1.2 - csstype: ^3.0.6 + csstype: ^3.1.2 fastest-stable-stringify: ^2.0.2 - inline-style-prefixer: ^6.0.0 - rtl-css-js: ^1.14.0 - sourcemap-codec: ^1.4.8 + inline-style-prefixer: ^7.0.0 + rtl-css-js: ^1.16.1 stacktrace-js: ^2.0.2 - stylis: ^4.0.6 + stylis: ^4.3.0 peerDependencies: react: "*" react-dom: "*" - checksum: 45517871e8c15a00769bfc9eb5e466d9c193b0a7423050eb77eb21003a1ccfd13863f01bf68490be830de019f4a0ae945d5b91eb23c5898d341ac8968ba3f18b + checksum: 735f02c030a9416bb6060503d24f18f2b2c9f43e4893c2d8714508d00f9d114b8a134df3623e94e376b0b1d794b0cacac6a48f8e5fb2b7fa8996071bcad590b8 languageName: node linkType: hard @@ -36511,14 +36652,14 @@ __metadata: languageName: node linkType: hard -"passport@npm:^0.6.0": - version: 0.6.0 - resolution: "passport@npm:0.6.0" +"passport@npm:^0.7.0": + version: 0.7.0 + resolution: "passport@npm:0.7.0" dependencies: passport-strategy: 1.x.x pause: 0.0.1 utils-merge: ^1.0.1 - checksum: ef932ad671d50de34765c7a53cd1e058d8331a82a6df09265a9c6c1168911aee4a7b5215803d0101110ab7f317e096b4954ca7e18fb2c33b9929f0bd17dbe159 + checksum: 5080b46df2df7a84f7ba4a8a20437ce71a1346fd27ab47b62df3251a666af9f3430d6c8a1beda3174f6a9d91edc823b57b88050d423a6cff9831848a2d97725c languageName: node linkType: hard @@ -36705,6 +36846,13 @@ __metadata: languageName: node linkType: hard +"pct-encode@npm:~1.0.0": + version: 1.0.2 + resolution: "pct-encode@npm:1.0.2" + checksum: 11edce15c8a9012cf5fdee006a05f10e3668a755a15aa25b6afbb8cc20d67f600702eb83e5eaca7a98ee78f9b362fb7d9ada9745428dceb6cdc44e0143851509 + languageName: node + linkType: hard + "peek-readable@npm:^4.0.1": version: 4.0.1 resolution: "peek-readable@npm:4.0.1" @@ -37009,27 +37157,27 @@ __metadata: languageName: node linkType: hard -"playwright-core@npm:1.40.0": - version: 1.40.0 - resolution: "playwright-core@npm:1.40.0" +"playwright-core@npm:1.40.1": + version: 1.40.1 + resolution: "playwright-core@npm:1.40.1" bin: playwright-core: cli.js - checksum: 57de5c91a4c404b120ed2af8541b21cdedcbc4f27477341157666d356bbee3b3fab8e61d020f0f450708fa2e8f6dc244b9224cb1985d5426e609cebed15af095 + checksum: 84d92fb9b86e3c225b16b6886bf858eb5059b4e60fa1205ff23336e56a06dcb2eac62650992dede72f406c8e70a7b6a5303e511f9b4bc0b85022ede356a01ee0 languageName: node linkType: hard -"playwright@npm:1.40.0": - version: 1.40.0 - resolution: "playwright@npm:1.40.0" +"playwright@npm:1.40.1": + version: 1.40.1 + resolution: "playwright@npm:1.40.1" dependencies: fsevents: 2.3.2 - playwright-core: 1.40.0 + playwright-core: 1.40.1 dependenciesMeta: fsevents: optional: true bin: playwright: cli.js - checksum: 7ba49e5376a6cfd1d32048dbdb2fd38e09182aa2e4619fdb23d3e6530fa6987f2f3fd34ad1d9d906fb4ec2da69ee7536eeb881982d60750fde809183caa607fc + checksum: 9e36791c1b4a649c104aa365fdd9d049924eeb518c5967c0e921aa38b9b00994aa6ee54784d6c2af194b3b494b6f69772673081ef53c6c4a4b2065af9955c4ba languageName: node linkType: hard @@ -38084,6 +38232,13 @@ __metadata: languageName: node linkType: hard +"queue-tick@npm:^1.0.1": + version: 1.0.1 + resolution: "queue-tick@npm:1.0.1" + checksum: 57c3292814b297f87f792fbeb99ce982813e4e54d7a8bdff65cf53d5c084113913289d4a48ec8bbc964927a74b847554f9f4579df43c969a6c8e0f026457ad01 + languageName: node + linkType: hard + "quick-format-unescaped@npm:^3.0.3": version: 3.0.3 resolution: "quick-format-unescaped@npm:3.0.3" @@ -38956,8 +39111,8 @@ __metadata: linkType: hard "react-use@npm:^17.2.4, react-use@npm:^17.3.1, react-use@npm:^17.3.2, react-use@npm:^17.4.0": - version: 17.4.0 - resolution: "react-use@npm:17.4.0" + version: 17.4.1 + resolution: "react-use@npm:17.4.1" dependencies: "@types/js-cookie": ^2.2.6 "@xobotyi/scrollbar-width": ^1.9.5 @@ -38965,7 +39120,7 @@ __metadata: fast-deep-equal: ^3.1.3 fast-shallow-equal: ^1.0.0 js-cookie: ^2.2.1 - nano-css: ^5.3.1 + nano-css: ^5.6.1 react-universal-interface: ^0.6.2 resize-observer-polyfill: ^1.5.1 screenfull: ^5.1.0 @@ -38976,7 +39131,7 @@ __metadata: peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - checksum: 0889da919b49a186de375ec15d2778b954ae981c523acd17dd496e4a4da7b6190efe7993491e1b85fdd6de3e745d08a4eaba4caa35408d570b5f1de550f35d11 + checksum: 75c4c63b97d50293b021aa3c6c66d3613151ca350e9ed47e0636d49679662e5841e100599d921c63604c9b4d11603261f5a232770069324734bae372718e2391 languageName: node linkType: hard @@ -38991,15 +39146,15 @@ __metadata: linkType: hard "react-window@npm:^1.8.6": - version: 1.8.9 - resolution: "react-window@npm:1.8.9" + version: 1.8.10 + resolution: "react-window@npm:1.8.10" dependencies: "@babel/runtime": ^7.0.0 memoize-one: ">=3.1.1 <6" peerDependencies: react: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 react-dom: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 - checksum: cefa232f4f37269d292529ef15780fb108123d6bb5beee19eeac657e75cf8d4664be66f2da04baf0cb1bc64c5ab3d83a88199c3358f023b78940acd37b0673b2 + checksum: e8830f32e3ad4bf91af9cdc5cead84148c7694ce6abd9fdb447fb609da6cd4bbd0bbc75ff985f78828f4bbbd3ba4cbc98235cc9c056b5e5787578518f7fafbb9 languageName: node linkType: hard @@ -39186,8 +39341,8 @@ __metadata: linkType: hard "recharts@npm:^2.5.0": - version: 2.10.1 - resolution: "recharts@npm:2.10.1" + version: 2.10.3 + resolution: "recharts@npm:2.10.3" dependencies: clsx: ^2.0.0 eventemitter3: ^4.0.1 @@ -39201,7 +39356,7 @@ __metadata: prop-types: ^15.6.0 react: ^16.0.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 - checksum: f1ee4dfe000c18f3c74855ea559e87d805c86f3b127441684187416ff4d22df4f92a6298259dfd9869d355d03291516ac75aaa6620f623b7b79c53f48fe6c9fb + checksum: 503c9fefa8648e0e8834a11ccc09fdcc310d391807438f4c0567b1888d7cd5f7a1f65f020afb981ec54ad6f96d31ebe757ab9025e93782edabc5c741f1aa946a languageName: node linkType: hard @@ -39653,7 +39808,7 @@ __metadata: languageName: node linkType: hard -"resolve@npm:^1.1.6, resolve@npm:^1.10.0, resolve@npm:^1.14.2, resolve@npm:^1.17.0, resolve@npm:^1.19.0, resolve@npm:^1.20.0, resolve@npm:^1.22.4, resolve@npm:~1.22.1": +"resolve@npm:^1.1.6, resolve@npm:^1.10.0, resolve@npm:^1.14.2, resolve@npm:^1.17.0, resolve@npm:^1.19.0, resolve@npm:^1.20.0, resolve@npm:^1.22.1, resolve@npm:^1.22.4, resolve@npm:~1.22.1": version: 1.22.8 resolution: "resolve@npm:1.22.8" dependencies: @@ -39689,7 +39844,7 @@ __metadata: languageName: node linkType: hard -"resolve@patch:resolve@^1.1.6#~builtin, resolve@patch:resolve@^1.10.0#~builtin, resolve@patch:resolve@^1.14.2#~builtin, resolve@patch:resolve@^1.17.0#~builtin, resolve@patch:resolve@^1.19.0#~builtin, resolve@patch:resolve@^1.20.0#~builtin, resolve@patch:resolve@^1.22.4#~builtin, resolve@patch:resolve@~1.22.1#~builtin": +"resolve@patch:resolve@^1.1.6#~builtin, resolve@patch:resolve@^1.10.0#~builtin, resolve@patch:resolve@^1.14.2#~builtin, resolve@patch:resolve@^1.17.0#~builtin, resolve@patch:resolve@^1.19.0#~builtin, resolve@patch:resolve@^1.20.0#~builtin, resolve@patch:resolve@^1.22.1#~builtin, resolve@patch:resolve@^1.22.4#~builtin, resolve@patch:resolve@~1.22.1#~builtin": version: 1.22.8 resolution: "resolve@patch:resolve@npm%3A1.22.8#~builtin::version=1.22.8&hash=07638b" dependencies: @@ -40096,12 +40251,12 @@ __metadata: languageName: node linkType: hard -"rtl-css-js@npm:^1.14.0": - version: 1.14.0 - resolution: "rtl-css-js@npm:1.14.0" +"rtl-css-js@npm:^1.16.1": + version: 1.16.1 + resolution: "rtl-css-js@npm:1.16.1" dependencies: "@babel/runtime": ^7.1.2 - checksum: 46e7f52058d7ac2dc7a6271f6858ce2e05f64be4d6a3eae712b52bf29c4de97fda8e30490c425bed620db87cc0b8a3ab4a457489ab0dba0868724bac27e6f893 + checksum: 7d9ab942098eee565784ccf957f6b7dfa78ea1eec7c6bffedc6641575d274189e90752537c7bdba1f43ae6534648144f467fd6d581527455ba626a4300e62c7a languageName: node linkType: hard @@ -41446,6 +41601,16 @@ __metadata: languageName: node linkType: hard +"streamx@npm:^2.15.0": + version: 2.15.5 + resolution: "streamx@npm:2.15.5" + dependencies: + fast-fifo: ^1.1.0 + queue-tick: ^1.0.1 + checksum: 52e0ec94026d67c9e2e2e1090f05e5b138c2f2822462d9a8ef4a4805625a31d103e55ea5267fcd9bfe041374926424e42aec2dda28a85cb9de42c2a16d416d94 + languageName: node + linkType: hard + "strict-event-emitter@npm:^0.2.4": version: 0.2.8 resolution: "strict-event-emitter@npm:0.2.8" @@ -41816,13 +41981,20 @@ __metadata: languageName: node linkType: hard -"stylis@npm:4.2.0, stylis@npm:^4.0.6": +"stylis@npm:4.2.0": version: 4.2.0 resolution: "stylis@npm:4.2.0" checksum: 0eb6cc1b866dc17a6037d0a82ac7fa877eba6a757443e79e7c4f35bacedbf6421fadcab4363b39667b43355cbaaa570a3cde850f776498e5450f32ed2f9b7584 languageName: node linkType: hard +"stylis@npm:^4.3.0": + version: 4.3.0 + resolution: "stylis@npm:4.3.0" + checksum: 6120de3f03eacf3b5adc8e7919c4cca991089156a6badc5248752a3088106afaaf74996211a6817a7760ebeadca09004048eea31875bd8d4df51386365c50025 + languageName: node + linkType: hard + "subscriptions-transport-ws@npm:^0.11.0": version: 0.11.0 resolution: "subscriptions-transport-ws@npm:0.11.0" @@ -41983,8 +42155,8 @@ __metadata: linkType: hard "swagger-ui-react@npm:^5.0.0": - version: 5.10.0 - resolution: "swagger-ui-react@npm:5.10.0" + version: 5.10.3 + resolution: "swagger-ui-react@npm:5.10.3" dependencies: "@babel/runtime-corejs3": ^7.23.2 "@braintree/sanitize-url": =6.0.4 @@ -42023,7 +42195,7 @@ __metadata: peerDependencies: react: ">=17.0.0" react-dom: ">=17.0.0" - checksum: 71aa42a8f3218eb264ea330122b483583830ca30db9dfb6eacee16210db0c4c529602dda607b3860315a3fe42e56e1142d49c4659c41bdfe675d6ae05e1bcdea + checksum: 505dfb7b6f09b75bd637ac89796a6c4c8f3833b3907463f6824c37732cb4d19d045408fd92070dd1d93416996063bd97749de4397b2fca978df8025f2243b60c languageName: node linkType: hard @@ -42114,6 +42286,17 @@ __metadata: languageName: node linkType: hard +"tar-stream@npm:^3.0.0": + version: 3.1.6 + resolution: "tar-stream@npm:3.1.6" + dependencies: + b4a: ^1.6.4 + fast-fifo: ^1.2.0 + streamx: ^2.15.0 + checksum: f3627f918581976e954ff03cb8d370551053796b82564f8c7ca8fac84c48e4d042026d0854fc222171a34ff9c682b72fae91be9c9b0a112d4c54f9e4f443e9c5 + languageName: node + linkType: hard + "tar@npm:^6.0.2, tar@npm:^6.1.0, tar@npm:^6.1.11, tar@npm:^6.1.12, tar@npm:^6.1.2": version: 6.2.0 resolution: "tar@npm:6.2.0" @@ -43535,6 +43718,15 @@ __metadata: languageName: node linkType: hard +"uri-template@npm:^2.0.0": + version: 2.0.0 + resolution: "uri-template@npm:2.0.0" + dependencies: + pct-encode: ~1.0.0 + checksum: 6eb3254368ca11330502525c6c0ab42af3cb646bfc96a4021666d6ac6653ede1ac0df7fde84a2e35e7f03f42d91b41251963122cfb3de9b54b84bc0ef3583ffc + languageName: node + linkType: hard + "urijs@npm:^1.19.10, urijs@npm:^1.19.11": version: 1.19.11 resolution: "urijs@npm:1.19.11" @@ -44035,17 +44227,17 @@ __metadata: languageName: node linkType: hard -"vite-plugin-node-polyfills@npm:^0.16.0": - version: 0.16.0 - resolution: "vite-plugin-node-polyfills@npm:0.16.0" +"vite-plugin-node-polyfills@npm:^0.17.0": + version: 0.17.0 + resolution: "vite-plugin-node-polyfills@npm:0.17.0" dependencies: "@rollup/plugin-inject": ^5.0.5 buffer-polyfill: "npm:buffer@^6.0.3" node-stdlib-browser: ^1.2.0 process: ^0.11.10 peerDependencies: - vite: ^2.0.0 || ^3.0.0 || ^4.0.0 - checksum: c4b3767e29a7c95efccde7d8adeac7963fb9b30677d88e348eda1c1d407c0d821d58c9ea651cb5db0aa010c93fd1971d8b671fea2417edd15a707861be772861 + vite: ^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 + checksum: 760d29c25adfa72b5fc731c1bb0826f9c6cc495afc856d38f634ac811566b158a11b1ea968e5d9d536889072470666e9b933ca38ea0c533854375806503b1729 languageName: node linkType: hard @@ -44444,9 +44636,9 @@ __metadata: linkType: hard "whatwg-fetch@npm:>=0.10.0": - version: 3.6.2 - resolution: "whatwg-fetch@npm:3.6.2" - checksum: ee976b7249e7791edb0d0a62cd806b29006ad7ec3a3d89145921ad8c00a3a67e4be8f3fb3ec6bc7b58498724fd568d11aeeeea1f7827e7e1e5eae6c8a275afed + version: 3.6.19 + resolution: "whatwg-fetch@npm:3.6.19" + checksum: 2896bc9ca867ea514392c73e2a272f65d5c4916248fe0837a9df5b1b92f247047bc76cf7c29c28a01ac6c5fb4314021d2718958c8a08292a96d56f72b2f56806 languageName: node linkType: hard @@ -44980,7 +45172,7 @@ __metadata: languageName: node linkType: hard -"yaml@npm:2.3.4, yaml@npm:^2.0.0, yaml@npm:^2.0.0-10, yaml@npm:^2.1.1, yaml@npm:^2.2.1, yaml@npm:^2.2.2, yaml@npm:^2.3.2": +"yaml@npm:2.3.4, yaml@npm:^2.0.0, yaml@npm:^2.0.0-10, yaml@npm:^2.1.1, yaml@npm:^2.2.1, yaml@npm:^2.2.2, yaml@npm:^2.3.2, yaml@npm:^2.3.3": version: 2.3.4 resolution: "yaml@npm:2.3.4" checksum: e6d1dae1c6383bcc8ba11796eef3b8c02d5082911c6723efeeb5ba50fc8e881df18d645e64de68e421b577296000bea9c75d6d9097c2f6699da3ae0406c030d8 @@ -45242,12 +45434,23 @@ __metadata: languageName: node linkType: hard +"zip-stream@npm:^5.0.1": + version: 5.0.1 + resolution: "zip-stream@npm:5.0.1" + dependencies: + archiver-utils: ^4.0.1 + compress-commons: ^5.0.1 + readable-stream: ^3.6.0 + checksum: 116cee5a2c1ecce7aa440b665470653f58ef56670c6aafa1b5491c9f9335992352145502af5fa865ac82f46336905e37fb7cbc649c2be72e2152c6b91802995c + languageName: node + linkType: hard + "zod-to-json-schema@npm:^3.20.4, zod-to-json-schema@npm:^3.21.4": - version: 3.22.0 - resolution: "zod-to-json-schema@npm:3.22.0" + version: 3.22.1 + resolution: "zod-to-json-schema@npm:3.22.1" peerDependencies: zod: ^3.22.4 - checksum: 79ba2f120e16bb38a44b6c20a3afa5e61c40f2d48749b08be68692005af1aed897c808f59be1fe722c2c40c5c3e9c38cd8ca4bfefdae089822de5948fe7d7cc5 + checksum: 7c0cdcf0acac81a9b69b26e44bc45e43aefacd5759e12f89856f0a4b957b66be27560671473ef7ab4a5883a871b6990a7f61fce3cee96ecad3823c5b7e524fa5 languageName: node linkType: hard