diff --git a/.changeset/afraid-mangos-sip.md b/.changeset/afraid-mangos-sip.md deleted file mode 100644 index 5cf1b72dc5..0000000000 --- a/.changeset/afraid-mangos-sip.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-kubernetes': patch ---- - -Add support to fetch data for Stateful Sets and display an accordion in the same way as with Deployments diff --git a/.changeset/beige-deers-remember.md b/.changeset/beige-deers-remember.md deleted file mode 100644 index 4fcba4f0c6..0000000000 --- a/.changeset/beige-deers-remember.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-cost-insights': patch ---- - -Add missing `export` in configuration schema. diff --git a/.changeset/beige-horses-scream.md b/.changeset/beige-horses-scream.md new file mode 100644 index 0000000000..2034491108 --- /dev/null +++ b/.changeset/beige-horses-scream.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-github-pull-requests-board': patch +--- + +Fix bug on fetching teams repositories where were being filtered by type service unnecessarily diff --git a/.changeset/blue-roses-give.md b/.changeset/blue-roses-give.md deleted file mode 100644 index 2479c33e46..0000000000 --- a/.changeset/blue-roses-give.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -'@backstage/plugin-catalog-react': patch ---- - -Accessibility updates: - -- Wrapped the `EntityLifecyclePicker`, `EntityOwnerPicker`, `EntityTagPicker`, in `label` elements -- Changed group name `Typography` component to `span` (from default `h6`), added `aria-label` to the `List` component, and `role` of `menuitem` to the container of the `MenuItem` component diff --git a/.changeset/chilled-mirrors-grab.md b/.changeset/chilled-mirrors-grab.md new file mode 100644 index 0000000000..ecc95680e2 --- /dev/null +++ b/.changeset/chilled-mirrors-grab.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-common': patch +--- + +Fix parsing of S3 URLs for the default region. diff --git a/.changeset/curvy-weeks-matter.md b/.changeset/curvy-weeks-matter.md new file mode 100644 index 0000000000..a0a9c43076 --- /dev/null +++ b/.changeset/curvy-weeks-matter.md @@ -0,0 +1,8 @@ +--- +'@backstage/backend-common': patch +'@backstage/catalog-model': patch +'@backstage/core-components': patch +'@backstage/plugin-techdocs-react': patch +--- + +Updated JSDoc to be MDX compatible. diff --git a/.changeset/eleven-mice-collect.md b/.changeset/eleven-mice-collect.md new file mode 100644 index 0000000000..66c4591d8e --- /dev/null +++ b/.changeset/eleven-mice-collect.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend': minor +--- + +Added a new `/v2/dry-run` endpoint that allows for a synchronous dry run of a provided template. A `supportsDryRun` option has been added to `createTemplateAction`, which signals whether the action should be executed during dry runs. When enabled, the action context will have the new `isDryRun` property set to signal if the action is being executed during a dry run. diff --git a/.changeset/fluffy-candles-learn.md b/.changeset/fluffy-candles-learn.md deleted file mode 100644 index 6fe3f1e09b..0000000000 --- a/.changeset/fluffy-candles-learn.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog': patch ---- - -Updates the `isKind`, `รฌsComponentType`, and `isNamespace` to allow an array of possible values diff --git a/.changeset/fresh-items-punch.md b/.changeset/fresh-items-punch.md deleted file mode 100644 index 1893dc7661..0000000000 --- a/.changeset/fresh-items-punch.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/cli': patch ---- - -Updated Lockfile to support new versions of yarn as well as the legacy 1 version diff --git a/.changeset/funny-suns-pay.md b/.changeset/funny-suns-pay.md deleted file mode 100644 index b736d34cc1..0000000000 --- a/.changeset/funny-suns-pay.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/backend-tasks': patch ---- - -Allow tasks that fail to retry on a loop emitting a warning log every time it fails with the amount of attempts it has diff --git a/.changeset/great-roses-pump.md b/.changeset/great-roses-pump.md new file mode 100644 index 0000000000..16a49bd464 --- /dev/null +++ b/.changeset/great-roses-pump.md @@ -0,0 +1,23 @@ +--- +'@backstage/plugin-catalog': patch +--- + +Previously, the color of the Entity Context Menu (in the Entity Page Header) was hardcoded as `white`. + +This was an issue for themes that use a header with a white background. By default, the color of the icon is now `theme.palette.bursts.fontColor`. + +It can now also be overridden in the theme, which is only necessary if the header title, subtitle and three-dots icon need to have different colors. For example: + +```typescript +export function createThemeOverrides(theme: BackstageTheme): Overrides { + return { + PluginCatalogEntityContextMenu: { + button: { + color: 'blue', + }, + }, + ... + }, + ... + } +``` diff --git a/.changeset/large-kangaroos-poke.md b/.changeset/large-kangaroos-poke.md new file mode 100644 index 0000000000..3a9444cfa1 --- /dev/null +++ b/.changeset/large-kangaroos-poke.md @@ -0,0 +1,13 @@ +--- +'@backstage/create-app': patch +--- + +Added an explicit `node-gyp` dependency to the root `package.json`. This is to work around a bug in older versions of `node-gyp` that causes Python execution to fail on macOS. + +You can add this workaround to your existing project by adding `node-gyp` as a `devDependency` in your root `package.json` file: + +```diff + "devDependencies": { ++ "node-gyp": "^9.0.0" + }, +``` diff --git a/.changeset/loud-jars-kick.md b/.changeset/loud-jars-kick.md deleted file mode 100644 index b53006f7f9..0000000000 --- a/.changeset/loud-jars-kick.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-allure': patch ---- - -Add export for `isAllureReportAvailable` and `ALLURE_PROJECT_ID_ANNOTATION` so it can be used outside of plugin diff --git a/.changeset/loud-walls-itch.md b/.changeset/loud-walls-itch.md deleted file mode 100644 index eff8c8b5c6..0000000000 --- a/.changeset/loud-walls-itch.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/create-app': patch ---- - -Updated the `--version` flag to output the version of the current backstage release instead of the version of create-app. diff --git a/.changeset/modern-ducks-lay.md b/.changeset/modern-ducks-lay.md new file mode 100644 index 0000000000..bd62c309ba --- /dev/null +++ b/.changeset/modern-ducks-lay.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-stack-overflow': patch +--- + +- Publicly exports `StackOverflowIcon`. +- `HomePageStackOverflowQuestions` accepts optional icon property. diff --git a/.changeset/nasty-zoos-cross.md b/.changeset/nasty-zoos-cross.md new file mode 100644 index 0000000000..7a658d7209 --- /dev/null +++ b/.changeset/nasty-zoos-cross.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-github-pull-requests-board': patch +--- + +Support namespaced teams and fetch all kinds diff --git a/.changeset/nervous-gorillas-approve.md b/.changeset/nervous-gorillas-approve.md deleted file mode 100644 index 8c0d742b7b..0000000000 --- a/.changeset/nervous-gorillas-approve.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/plugin-catalog-backend-module-aws': patch -'@backstage/plugin-catalog-backend-module-gerrit': patch ---- - -Inline config interfaces diff --git a/.changeset/nervous-humans-sip.md b/.changeset/nervous-humans-sip.md new file mode 100644 index 0000000000..aa1ca2c60a --- /dev/null +++ b/.changeset/nervous-humans-sip.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-app-api': patch +--- + +The `signOut` method of the `IdentityApi` will now navigate the user back to the base URL of the app as indicated by the `app.baseUrl` config. diff --git a/.changeset/olive-rats-rest.md b/.changeset/olive-rats-rest.md deleted file mode 100644 index 46e76f7c17..0000000000 --- a/.changeset/olive-rats-rest.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -'@backstage/create-app': patch ---- - -Accessibility updates: - -- Added `aria-label` to the sidebar Logo link. To enable this for an existing app, please make the following changes: - -`packages/app/src/components/Root/Root.tsx` - -```diff -const SidebarLogo = () => { - const classes = useSidebarLogoStyles(); - const { isOpen } = useContext(SidebarContext); - - return ( -
- - {isOpen ? : } - -
- ); -}; -``` diff --git a/.changeset/plenty-garlics-shop.md b/.changeset/plenty-garlics-shop.md deleted file mode 100644 index cccbae0109..0000000000 --- a/.changeset/plenty-garlics-shop.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-kubernetes-common': minor ---- - -Add support to fetch data for Stateful Sets diff --git a/.changeset/polite-eagles-invite.md b/.changeset/polite-eagles-invite.md new file mode 100644 index 0000000000..f9c5073a83 --- /dev/null +++ b/.changeset/polite-eagles-invite.md @@ -0,0 +1,7 @@ +--- +'@backstage/plugin-scaffolder': minor +--- + +A new template editor has been added which is accessible via the context menu on the top right hand corner of the Create page. It allows you to load a template from a local directory, edit it with a preview, execute it in dry-run mode, and view the results. Note that the [File System Access API](https://developer.mozilla.org/en-US/docs/Web/API/File_System_Access_API) must be supported by your browser for this to be available. + +To support the new template editor the `ScaffolderApi` now has an optional `dryRun` method, which is implemented by the default `ScaffolderClient`. diff --git a/.changeset/polite-spiders-pay.md b/.changeset/polite-spiders-pay.md deleted file mode 100644 index 41528aff6b..0000000000 --- a/.changeset/polite-spiders-pay.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-auth-backend': patch ---- - -Added configurable algorithm field for TokenFactory diff --git a/.changeset/pre.json b/.changeset/pre.json index aff34f172d..500bf4ac83 100644 --- a/.changeset/pre.json +++ b/.changeset/pre.json @@ -2,157 +2,161 @@ "mode": "pre", "tag": "next", "initialVersions": { - "example-app": "0.2.71", - "@backstage/app-defaults": "1.0.2", - "example-backend": "0.2.71", - "@backstage/backend-common": "0.13.3", - "@backstage/backend-tasks": "0.3.1", - "@backstage/backend-test-utils": "0.1.24", - "@backstage/catalog-client": "1.0.2", - "@backstage/catalog-model": "1.0.2", - "@backstage/cli": "0.17.1", + "example-app": "0.2.72", + "@backstage/app-defaults": "1.0.3", + "example-backend": "0.2.72", + "@backstage/backend-common": "0.14.0", + "@backstage/backend-tasks": "0.3.2", + "@backstage/backend-test-utils": "0.1.25", + "@backstage/catalog-client": "1.0.3", + "@backstage/catalog-model": "1.0.3", + "@backstage/cli": "0.17.2", "@backstage/cli-common": "0.1.9", "@backstage/codemods": "0.1.38", "@backstage/config": "1.0.1", - "@backstage/config-loader": "1.1.1", - "@backstage/core-app-api": "1.0.2", - "@backstage/core-components": "0.9.4", - "@backstage/core-plugin-api": "1.0.2", - "@backstage/create-app": "0.4.27", - "@backstage/dev-utils": "1.0.2", + "@backstage/config-loader": "1.1.2", + "@backstage/core-app-api": "1.0.3", + "@backstage/core-components": "0.9.5", + "@backstage/core-plugin-api": "1.0.3", + "@backstage/create-app": "0.4.28", + "@backstage/dev-utils": "1.0.3", "e2e-test": "0.2.0", "@backstage/errors": "1.0.0", - "@backstage/integration": "1.2.0", - "@backstage/integration-react": "1.1.0", - "@backstage/release-manifests": "0.0.3", - "@backstage/search-common": "0.3.4", - "@techdocs/cli": "1.1.1", - "techdocs-cli-embedded-app": "0.2.70", - "@backstage/techdocs-common": "0.11.15", - "@backstage/test-utils": "1.1.0", + "@backstage/integration": "1.2.1", + "@backstage/integration-react": "1.1.1", + "@backstage/release-manifests": "0.0.4", + "@techdocs/cli": "1.1.2", + "techdocs-cli-embedded-app": "0.2.71", + "@backstage/test-utils": "1.1.1", "@backstage/theme": "0.2.15", "@backstage/types": "1.0.0", "@backstage/version-bridge": "1.0.1", - "@backstage/plugin-adr": "0.1.0", - "@backstage/plugin-adr-backend": "0.1.0", - "@backstage/plugin-adr-common": "0.1.0", - "@backstage/plugin-airbrake": "0.3.5", - "@backstage/plugin-airbrake-backend": "0.2.5", - "@backstage/plugin-allure": "0.1.21", - "@backstage/plugin-analytics-module-ga": "0.1.16", - "@backstage/plugin-apache-airflow": "0.1.13", - "@backstage/plugin-api-docs": "0.8.5", - "@backstage/plugin-app-backend": "0.3.32", - "@backstage/plugin-auth-backend": "0.14.0", - "@backstage/plugin-auth-node": "0.2.1", - "@backstage/plugin-azure-devops": "0.1.21", - "@backstage/plugin-azure-devops-backend": "0.3.11", + "@backstage/plugin-adr": "0.1.1", + "@backstage/plugin-adr-backend": "0.1.1", + "@backstage/plugin-adr-common": "0.1.1", + "@backstage/plugin-airbrake": "0.3.6", + "@backstage/plugin-airbrake-backend": "0.2.6", + "@backstage/plugin-allure": "0.1.22", + "@backstage/plugin-analytics-module-ga": "0.1.17", + "@backstage/plugin-apache-airflow": "0.1.14", + "@backstage/plugin-api-docs": "0.8.6", + "@backstage/plugin-app-backend": "0.3.33", + "@backstage/plugin-auth-backend": "0.14.1", + "@backstage/plugin-auth-node": "0.2.2", + "@backstage/plugin-azure-devops": "0.1.22", + "@backstage/plugin-azure-devops-backend": "0.3.12", "@backstage/plugin-azure-devops-common": "0.2.3", - "@backstage/plugin-badges": "0.2.29", - "@backstage/plugin-badges-backend": "0.1.26", - "@backstage/plugin-bazaar": "0.1.20", - "@backstage/plugin-bazaar-backend": "0.1.16", - "@backstage/plugin-bitrise": "0.1.32", - "@backstage/plugin-catalog": "1.2.0", - "@backstage/plugin-catalog-backend": "1.1.2", - "@backstage/plugin-catalog-backend-module-aws": "0.1.5", - "@backstage/plugin-catalog-backend-module-azure": "0.1.3", - "@backstage/plugin-catalog-backend-module-bitbucket": "0.1.3", - "@backstage/plugin-catalog-backend-module-gerrit": "0.1.0", - "@backstage/plugin-catalog-backend-module-github": "0.1.3", - "@backstage/plugin-catalog-backend-module-gitlab": "0.1.3", - "@backstage/plugin-catalog-backend-module-ldap": "0.4.3", - "@backstage/plugin-catalog-backend-module-msgraph": "0.3.2", - "@backstage/plugin-catalog-common": "1.0.2", - "@backstage/plugin-catalog-graph": "0.2.17", - "@backstage/plugin-catalog-graphql": "0.3.9", - "@backstage/plugin-catalog-import": "0.8.8", - "@backstage/plugin-catalog-react": "1.1.0", - "@backstage/plugin-cicd-statistics": "0.1.7", - "@backstage/plugin-cicd-statistics-module-gitlab": "0.1.1", - "@backstage/plugin-circleci": "0.3.5", - "@backstage/plugin-cloudbuild": "0.3.5", - "@backstage/plugin-code-climate": "0.1.5", - "@backstage/plugin-code-coverage": "0.1.32", - "@backstage/plugin-code-coverage-backend": "0.1.30", - "@backstage/plugin-codescene": "0.1.0", - "@backstage/plugin-config-schema": "0.1.28", - "@backstage/plugin-cost-insights": "0.11.27", - "@internal/plugin-todo-list": "1.0.1", - "@internal/plugin-todo-list-backend": "1.0.1", - "@internal/plugin-todo-list-common": "1.0.1", - "@backstage/plugin-explore": "0.3.36", - "@backstage/plugin-explore-react": "0.0.17", - "@backstage/plugin-firehydrant": "0.1.22", - "@backstage/plugin-fossa": "0.2.37", - "@backstage/plugin-gcalendar": "0.3.1", - "@backstage/plugin-gcp-projects": "0.3.24", - "@backstage/plugin-git-release-manager": "0.3.18", - "@backstage/plugin-github-actions": "0.5.5", - "@backstage/plugin-github-deployments": "0.1.36", - "@backstage/plugin-gitops-profiles": "0.3.23", - "@backstage/plugin-gocd": "0.1.11", - "@backstage/plugin-graphiql": "0.2.37", - "@backstage/plugin-graphql-backend": "0.1.22", - "@backstage/plugin-home": "0.4.21", - "@backstage/plugin-ilert": "0.1.31", - "@backstage/plugin-jenkins": "0.7.4", - "@backstage/plugin-jenkins-backend": "0.1.22", - "@backstage/plugin-jenkins-common": "0.1.4", - "@backstage/plugin-kafka": "0.3.5", - "@backstage/plugin-kafka-backend": "0.2.25", - "@backstage/plugin-kubernetes": "0.6.5", - "@backstage/plugin-kubernetes-backend": "0.5.1", - "@backstage/plugin-kubernetes-common": "0.2.10", - "@backstage/plugin-lighthouse": "0.3.5", - "@backstage/plugin-newrelic": "0.3.23", - "@backstage/plugin-newrelic-dashboard": "0.1.13", - "@backstage/plugin-org": "0.5.5", - "@backstage/plugin-pagerduty": "0.3.32", - "@backstage/plugin-periskop": "0.1.3", - "@backstage/plugin-periskop-backend": "0.1.3", - "@backstage/plugin-permission-backend": "0.5.7", - "@backstage/plugin-permission-common": "0.6.1", - "@backstage/plugin-permission-node": "0.6.1", - "@backstage/plugin-permission-react": "0.4.1", - "@backstage/plugin-proxy-backend": "0.2.26", - "@backstage/plugin-rollbar": "0.4.5", - "@backstage/plugin-rollbar-backend": "0.1.29", - "@backstage/plugin-scaffolder": "1.2.0", - "@backstage/plugin-scaffolder-backend": "1.2.0", - "@backstage/plugin-scaffolder-backend-module-cookiecutter": "0.2.7", - "@backstage/plugin-scaffolder-backend-module-rails": "0.4.0", - "@backstage/plugin-scaffolder-backend-module-yeoman": "0.2.5", - "@backstage/plugin-scaffolder-common": "1.1.0", - "@backstage/plugin-search": "0.8.1", - "@backstage/plugin-search-backend": "0.5.2", - "@backstage/plugin-search-backend-module-elasticsearch": "0.1.4", - "@backstage/plugin-search-backend-module-pg": "0.3.3", - "@backstage/plugin-search-backend-node": "0.6.1", - "@backstage/plugin-search-common": "0.3.4", - "@backstage/plugin-search-react": "0.2.0", - "@backstage/plugin-sentry": "0.3.43", - "@backstage/plugin-shortcuts": "0.2.6", - "@backstage/plugin-sonarqube": "0.3.5", - "@backstage/plugin-splunk-on-call": "0.3.29", - "@backstage/plugin-stack-overflow": "0.1.1", - "@backstage/plugin-stack-overflow-backend": "0.1.1", - "@backstage/plugin-tech-insights": "0.2.1", - "@backstage/plugin-tech-insights-backend": "0.4.0", - "@backstage/plugin-tech-insights-backend-module-jsonfc": "0.1.16", + "@backstage/plugin-badges": "0.2.30", + "@backstage/plugin-badges-backend": "0.1.27", + "@backstage/plugin-bazaar": "0.1.21", + "@backstage/plugin-bazaar-backend": "0.1.17", + "@backstage/plugin-bitbucket-cloud-common": "0.1.0", + "@backstage/plugin-bitrise": "0.1.33", + "@backstage/plugin-catalog": "1.3.0", + "@backstage/plugin-catalog-backend": "1.2.0", + "@backstage/plugin-catalog-backend-module-aws": "0.1.6", + "@backstage/plugin-catalog-backend-module-azure": "0.1.4", + "@backstage/plugin-catalog-backend-module-bitbucket": "0.2.0", + "@backstage/plugin-catalog-backend-module-bitbucket-cloud": "0.1.0", + "@backstage/plugin-catalog-backend-module-gerrit": "0.1.1", + "@backstage/plugin-catalog-backend-module-github": "0.1.4", + "@backstage/plugin-catalog-backend-module-gitlab": "0.1.4", + "@backstage/plugin-catalog-backend-module-ldap": "0.5.0", + "@backstage/plugin-catalog-backend-module-msgraph": "0.3.3", + "@backstage/plugin-catalog-common": "1.0.3", + "@backstage/plugin-catalog-graph": "0.2.18", + "@backstage/plugin-catalog-graphql": "0.3.10", + "@backstage/plugin-catalog-import": "0.8.9", + "@backstage/plugin-catalog-react": "1.1.1", + "@backstage/plugin-cicd-statistics": "0.1.8", + "@backstage/plugin-cicd-statistics-module-gitlab": "0.1.2", + "@backstage/plugin-circleci": "0.3.6", + "@backstage/plugin-cloudbuild": "0.3.6", + "@backstage/plugin-code-climate": "0.1.6", + "@backstage/plugin-code-coverage": "0.1.33", + "@backstage/plugin-code-coverage-backend": "0.1.31", + "@backstage/plugin-codescene": "0.1.1", + "@backstage/plugin-config-schema": "0.1.29", + "@backstage/plugin-cost-insights": "0.11.28", + "@backstage/plugin-dynatrace": "0.1.0", + "@internal/plugin-todo-list": "1.0.2", + "@internal/plugin-todo-list-backend": "1.0.2", + "@internal/plugin-todo-list-common": "1.0.2", + "@backstage/plugin-explore": "0.3.37", + "@backstage/plugin-explore-react": "0.0.18", + "@backstage/plugin-firehydrant": "0.1.23", + "@backstage/plugin-fossa": "0.2.38", + "@backstage/plugin-gcalendar": "0.3.2", + "@backstage/plugin-gcp-projects": "0.3.25", + "@backstage/plugin-git-release-manager": "0.3.19", + "@backstage/plugin-github-actions": "0.5.6", + "@backstage/plugin-github-deployments": "0.1.37", + "@backstage/plugin-github-pull-requests-board": "0.1.0", + "@backstage/plugin-gitops-profiles": "0.3.24", + "@backstage/plugin-gocd": "0.1.12", + "@backstage/plugin-graphiql": "0.2.38", + "@backstage/plugin-graphql-backend": "0.1.23", + "@backstage/plugin-home": "0.4.22", + "@backstage/plugin-ilert": "0.1.32", + "@backstage/plugin-jenkins": "0.7.5", + "@backstage/plugin-jenkins-backend": "0.1.23", + "@backstage/plugin-jenkins-common": "0.1.5", + "@backstage/plugin-kafka": "0.3.6", + "@backstage/plugin-kafka-backend": "0.2.26", + "@backstage/plugin-kubernetes": "0.6.6", + "@backstage/plugin-kubernetes-backend": "0.6.0", + "@backstage/plugin-kubernetes-common": "0.3.0", + "@backstage/plugin-lighthouse": "0.3.6", + "@backstage/plugin-newrelic": "0.3.24", + "@backstage/plugin-newrelic-dashboard": "0.1.14", + "@backstage/plugin-org": "0.5.6", + "@backstage/plugin-pagerduty": "0.4.0", + "@backstage/plugin-periskop": "0.1.4", + "@backstage/plugin-periskop-backend": "0.1.4", + "@backstage/plugin-permission-backend": "0.5.8", + "@backstage/plugin-permission-common": "0.6.2", + "@backstage/plugin-permission-node": "0.6.2", + "@backstage/plugin-permission-react": "0.4.2", + "@backstage/plugin-proxy-backend": "0.2.27", + "@backstage/plugin-rollbar": "0.4.6", + "@backstage/plugin-rollbar-backend": "0.1.30", + "@backstage/plugin-scaffolder": "1.3.0", + "@backstage/plugin-scaffolder-backend": "1.3.0", + "@backstage/plugin-scaffolder-backend-module-cookiecutter": "0.2.8", + "@backstage/plugin-scaffolder-backend-module-rails": "0.4.1", + "@backstage/plugin-scaffolder-backend-module-yeoman": "0.2.6", + "@backstage/plugin-scaffolder-common": "1.1.1", + "@backstage/plugin-search": "0.9.0", + "@backstage/plugin-search-backend": "0.5.3", + "@backstage/plugin-search-backend-module-elasticsearch": "0.1.5", + "@backstage/plugin-search-backend-module-pg": "0.3.4", + "@backstage/plugin-search-backend-node": "0.6.2", + "@backstage/plugin-search-common": "0.3.5", + "@backstage/plugin-search-react": "0.2.1", + "@backstage/plugin-sentry": "0.3.44", + "@backstage/plugin-shortcuts": "0.2.7", + "@backstage/plugin-sonarqube": "0.3.6", + "@backstage/plugin-splunk-on-call": "0.3.30", + "@backstage/plugin-stack-overflow": "0.1.2", + "@backstage/plugin-stack-overflow-backend": "0.1.2", + "@backstage/plugin-tech-insights": "0.2.2", + "@backstage/plugin-tech-insights-backend": "0.4.1", + "@backstage/plugin-tech-insights-backend-module-jsonfc": "0.1.17", "@backstage/plugin-tech-insights-common": "0.2.4", - "@backstage/plugin-tech-insights-node": "0.3.0", - "@backstage/plugin-tech-radar": "0.5.12", - "@backstage/plugin-techdocs": "1.1.1", - "@backstage/plugin-techdocs-addons-test-utils": "1.0.0", - "@backstage/plugin-techdocs-backend": "1.1.1", - "@backstage/plugin-techdocs-module-addons-contrib": "1.0.0", - "@backstage/plugin-techdocs-node": "1.1.1", - "@backstage/plugin-techdocs-react": "1.0.0", - "@backstage/plugin-todo": "0.2.7", - "@backstage/plugin-todo-backend": "0.1.29", - "@backstage/plugin-user-settings": "0.4.4", - "@backstage/plugin-xcmetrics": "0.2.25" + "@backstage/plugin-tech-insights-node": "0.3.1", + "@backstage/plugin-tech-radar": "0.5.13", + "@backstage/plugin-techdocs": "1.2.0", + "@backstage/plugin-techdocs-addons-test-utils": "1.0.1", + "@backstage/plugin-techdocs-backend": "1.1.2", + "@backstage/plugin-techdocs-module-addons-contrib": "1.0.1", + "@backstage/plugin-techdocs-node": "1.1.2", + "@backstage/plugin-techdocs-react": "1.0.1", + "@backstage/plugin-todo": "0.2.8", + "@backstage/plugin-todo-backend": "0.1.30", + "@backstage/plugin-user-settings": "0.4.5", + "@backstage/plugin-vault": "0.1.0", + "@backstage/plugin-vault-backend": "0.1.0", + "@backstage/plugin-xcmetrics": "0.2.26" }, "changesets": [] } diff --git a/.changeset/purple-beans-march.md b/.changeset/purple-beans-march.md new file mode 100644 index 0000000000..f4a3b45ab2 --- /dev/null +++ b/.changeset/purple-beans-march.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +Fix `webpack-dev-server` deprecations. diff --git a/.changeset/quick-ladybugs-try.md b/.changeset/quick-ladybugs-try.md deleted file mode 100644 index aad1b200b6..0000000000 --- a/.changeset/quick-ladybugs-try.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@backstage/plugin-catalog': patch ---- - -Accessibility updates: - -- Added screen reader elements to describe default table `Action` buttons diff --git a/.changeset/red-games-decide.md b/.changeset/red-games-decide.md new file mode 100644 index 0000000000..ca5ff580b4 --- /dev/null +++ b/.changeset/red-games-decide.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-api-docs-module-protoc-gen-doc': major +--- + +Added the new `grpcDocsApiWidget` to render `protoc-gen-doc` generated descriptors by the `grpc-docs` package. diff --git a/.changeset/reject-failed-index-tasks.md b/.changeset/reject-failed-index-tasks.md deleted file mode 100644 index 3c669ce545..0000000000 --- a/.changeset/reject-failed-index-tasks.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/search-backend-node': patch ---- - -propagate indexing errors so they don't appear successful to the task scheduler diff --git a/.changeset/renovate-9454dab.md b/.changeset/renovate-9454dab.md new file mode 100644 index 0000000000..1f8401e10f --- /dev/null +++ b/.changeset/renovate-9454dab.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-auth-backend': patch +--- + +Updated dependency `google-auth-library` to `^8.0.0`. diff --git a/.changeset/renovate-ad175cc.md b/.changeset/renovate-ad175cc.md deleted file mode 100644 index 72e278146e..0000000000 --- a/.changeset/renovate-ad175cc.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@backstage/core-components': patch -'@backstage/plugin-gcp-projects': patch -'@backstage/plugin-techdocs-module-addons-contrib': patch ---- - -Updated dependency `@react-hookz/web` to `^14.0.0`. diff --git a/.changeset/rude-llamas-lie.md b/.changeset/rude-llamas-lie.md new file mode 100644 index 0000000000..918d61da6d --- /dev/null +++ b/.changeset/rude-llamas-lie.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-vault': patch +--- + +Export missing parameters and added them to the api-report. Also adapted the API to the expected response from the backend diff --git a/.changeset/scaffolder-form-context.md b/.changeset/scaffolder-form-context.md deleted file mode 100644 index 9b0e19ebd0..0000000000 --- a/.changeset/scaffolder-form-context.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -'@backstage/plugin-scaffolder': minor ---- - -Get data of other fields in Form from a custom field in template Scaffolder. -following: - -```tsx -const CustomFieldExtensionComponent = (props: FieldExtensionComponentProps) => { - const { formData } = props.formContext; - ... -}; - -const CustomFieldExtension = scaffolderPlugin.provide( - createScaffolderFieldExtension({ - name: ..., - component: CustomFieldExtensionComponent, - validation: ... - }) -); -``` diff --git a/.changeset/shaggy-melons-drive.md b/.changeset/shaggy-melons-drive.md new file mode 100644 index 0000000000..b5674493f4 --- /dev/null +++ b/.changeset/shaggy-melons-drive.md @@ -0,0 +1,5 @@ +--- +'@backstage/catalog-model': minor +--- + +Adding an optional type field to entity links to group and categorize links diff --git a/.changeset/sharp-planes-turn.md b/.changeset/sharp-planes-turn.md new file mode 100644 index 0000000000..47f6a6176d --- /dev/null +++ b/.changeset/sharp-planes-turn.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-vault-backend': minor +--- + +Throw exceptions instead of swallow them, remove some exported types from the `api-report`, small changes in the API responses & expose the vault `baseUrl` to the frontend as well diff --git a/.changeset/short-jokes-applaud.md b/.changeset/short-jokes-applaud.md deleted file mode 100644 index b1b4f55858..0000000000 --- a/.changeset/short-jokes-applaud.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -'@backstage/core-components': patch ---- - -Accessibility updates: - -- Added `aria-label` to the `Select` component -- Changed heading level used in the header of `Table` component diff --git a/.changeset/short-olives-train.md b/.changeset/short-olives-train.md new file mode 100644 index 0000000000..594d95f073 --- /dev/null +++ b/.changeset/short-olives-train.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-kubernetes-backend': minor +'@backstage/plugin-kubernetes-common': minor +--- + +Refactor `KubernetesObjectsProvider` with new methods, `KubernetesServiceLocator` now takes an `Entity` instead of `serviceId` diff --git a/.changeset/silly-wombats-flash.md b/.changeset/silly-wombats-flash.md deleted file mode 100644 index d75196df76..0000000000 --- a/.changeset/silly-wombats-flash.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-adr': patch ---- - -Adding term highlighting support to `AdrSearchResultListItem` diff --git a/.changeset/sixty-plums-kick.md b/.changeset/sixty-plums-kick.md deleted file mode 100644 index 5c11d9aaca..0000000000 --- a/.changeset/sixty-plums-kick.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-kubernetes': patch ---- - -Fix for HPA matching when deploying same HPA in multiple namespaces diff --git a/.changeset/slimy-elephants-attend.md b/.changeset/slimy-elephants-attend.md deleted file mode 100644 index 4add8f18a3..0000000000 --- a/.changeset/slimy-elephants-attend.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -'@backstage/plugin-catalog-backend': minor ---- - -Allow array as non-spread arguments at the `CatalogBuilder`. - -```typescript -builder.addEntityProvider(...getArrayOfProviders()); -``` - -can be simplified to - -```typescript -builder.addEntityProvider(getArrayOfProviders()); -``` diff --git a/.changeset/smooth-sheep-hide.md b/.changeset/smooth-sheep-hide.md new file mode 100644 index 0000000000..8a65602ca9 --- /dev/null +++ b/.changeset/smooth-sheep-hide.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-azure-devops': patch +--- + +Exporting azureDevOpsApiRef, AzureGitTagsIcon, and all hooks for the benefit of other plugins. diff --git a/.changeset/spotty-goats-look.md b/.changeset/spotty-goats-look.md deleted file mode 100644 index 0759afcd57..0000000000 --- a/.changeset/spotty-goats-look.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend': patch ---- - -Adds more of an explanation when the `publish:github` scaffolder action fails to create a repository. diff --git a/.changeset/sweet-plants-sparkle.md b/.changeset/sweet-plants-sparkle.md new file mode 100644 index 0000000000..69e7647849 --- /dev/null +++ b/.changeset/sweet-plants-sparkle.md @@ -0,0 +1,5 @@ +--- +'@backstage/integration': patch +--- + +Allow frontend visibility for `integrations` itself. diff --git a/.changeset/tame-guests-wave.md b/.changeset/tame-guests-wave.md new file mode 100644 index 0000000000..bbf757084d --- /dev/null +++ b/.changeset/tame-guests-wave.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-azure-devops-backend': patch +--- + +Added comments to example to help avoid confusion as to where lines need to be added diff --git a/.changeset/tasty-snails-boil.md b/.changeset/tasty-snails-boil.md deleted file mode 100644 index d5b73d3b1b..0000000000 --- a/.changeset/tasty-snails-boil.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-auth-node': patch ---- - -Added configurable algorithms array for IdentityClient diff --git a/.changeset/techdocs-gorgeous-plants-sniff.md b/.changeset/techdocs-gorgeous-plants-sniff.md new file mode 100644 index 0000000000..f5453f35be --- /dev/null +++ b/.changeset/techdocs-gorgeous-plants-sniff.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-techdocs': patch +--- + +Reorder browser tab title in Techdocs pages to have the site name first. diff --git a/.changeset/techdocs-paws-study.md b/.changeset/techdocs-paws-study.md deleted file mode 100644 index adbc8d1b3d..0000000000 --- a/.changeset/techdocs-paws-study.md +++ /dev/null @@ -1,64 +0,0 @@ ---- -'@backstage/plugin-techdocs-backend': patch ---- - -Output logs from a TechDocs build to a logging transport in addition to existing -frontend event stream, for capturing these logs to other sources. - -This allows users to capture debugging information around why tech docs fail to build -without needing to rely on end users capturing information from their web browser. - -The most common use case is to log to the same place as the rest of the backend -application logs. - -Sample usage: - -``` -import { DockerContainerRunner } from '@backstage/backend-common'; -import { - createRouter, - Generators, - Preparers, - Publisher, -} from '@backstage/plugin-techdocs-backend'; -import Docker from 'dockerode'; -import { Router } from 'express'; -import { PluginEnvironment } from '../types'; - -export default async function createPlugin( - env: PluginEnvironment, -): Promise { - const preparers = await Preparers.fromConfig(env.config, { - logger: env.logger, - reader: env.reader, - }); - - const dockerClient = new Docker(); - const containerRunner = new DockerContainerRunner({ dockerClient }); - - const generators = await Generators.fromConfig(env.config, { - logger: env.logger, - containerRunner, - }); - - const publisher = await Publisher.fromConfig(env.config, { - logger: env.logger, - discovery: env.discovery, - }); - - await publisher.getReadiness(); - - return await createRouter({ - preparers, - generators, - publisher, - logger: env.logger, - // Passing a buildLogTransport as a parameter in createRouter will enable - // capturing build logs to a backend log stream - buildLogTransport: env.logger, - config: env.config, - discovery: env.discovery, - cache: env.cache, - }); -} -``` diff --git a/.changeset/techdocs-swans-check.md b/.changeset/techdocs-swans-check.md deleted file mode 100644 index 750648ff8a..0000000000 --- a/.changeset/techdocs-swans-check.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -'@backstage/plugin-techdocs': patch ---- - -Packages a set of tweaks to the TechDocs addons rendering process: - -- Prevents displaying sidebars until page styles are loaded and the sidebar position is updated; -- Prevents new sidebar locations from being created every time the reader page is rendered if these locations already exist; -- Centers the styles loaded event to avoid having multiple locations setting the opacity style in Shadow Dom causing the screen to flash multiple times. diff --git a/.changeset/techdocs-the-whole-pulse.md b/.changeset/techdocs-the-whole-pulse.md new file mode 100644 index 0000000000..b54fb080ae --- /dev/null +++ b/.changeset/techdocs-the-whole-pulse.md @@ -0,0 +1,5 @@ +--- +'@techdocs/cli': patch +--- + +Fixed a bug that prevented docker images from being pulled by default when generating TechDocs. diff --git a/.changeset/techdocs-ways-type.md b/.changeset/techdocs-ways-type.md deleted file mode 100644 index 6e147f62a7..0000000000 --- a/.changeset/techdocs-ways-type.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-techdocs': patch ---- - -Change the `EntityDocsPage` path to be more specific and also add integration tests for `sub-routes` on this page. diff --git a/.changeset/techdocs-wolves-carry.md b/.changeset/techdocs-wolves-carry.md deleted file mode 100644 index 01436ee15f..0000000000 --- a/.changeset/techdocs-wolves-carry.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -'@backstage/plugin-techdocs-react': patch ---- - -Creates a `TechDocsShadowDom` component that takes a tree of elements and an `onAppend` handler: - -- Calls the `onAppend` handler when appending the element tree to the shadow root; -- Also dispatches an event when styles are loaded to let transformers know that the computed styles are ready to be consumed. diff --git a/.changeset/thick-radios-drive.md b/.changeset/thick-radios-drive.md new file mode 100644 index 0000000000..130030be59 --- /dev/null +++ b/.changeset/thick-radios-drive.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-adr': patch +--- + +Track discover event and result rank for `AdrSearchResultListItem` diff --git a/.changeset/unlucky-lies-pretend.md b/.changeset/unlucky-lies-pretend.md deleted file mode 100644 index a52f2f394e..0000000000 --- a/.changeset/unlucky-lies-pretend.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-kubernetes-backend': minor ---- - -Add support to fetch data for Stateful Sets from Kubernetes diff --git a/.changeset/unlucky-stingrays-juggle.md b/.changeset/unlucky-stingrays-juggle.md new file mode 100644 index 0000000000..f2e83f7163 --- /dev/null +++ b/.changeset/unlucky-stingrays-juggle.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-import': patch +--- + +Support use without `integrations` or only integrations without frontend visible properties (e.g., `bitbucketCloud`) being configured by checking `integrations.github` directly without attempting to load `integrations`. diff --git a/.changeset/weak-bananas-deliver.md b/.changeset/weak-bananas-deliver.md new file mode 100644 index 0000000000..f46c7b7bfe --- /dev/null +++ b/.changeset/weak-bananas-deliver.md @@ -0,0 +1,28 @@ +--- +'@backstage/plugin-pagerduty': minor +--- + +Introduces a new annotation `pagerduty.com/service-id` that can be used instead of the `pagerduty.com/integration-key` annotation. +_Note: If both annotations are specified on a given Entity, then the `pagerduty.com/integration-key` annotation will be prefered_ + +**BREAKING** The `PagerDutyClient.fromConfig` static method now expects a `FetchApi` compatible object and has been refactored to +accept 2 arguments: config and ClientApiDependencies +The `PagerDutyClient` now relies on a `fetchApi` being available to execute `fetch` requests. + +**BREAKING** A new query method `getServiceByEntity` that is used to query for Services by either the `integrationKey` or `serviceId` +annotation values if they are defined. The `integrationKey` value is preferred currently over `serviceId`. As such, the previous +`getServiceByIntegrationKey` method has been removed. + +**BREAKING** The return values for each Client query method has been changed to return an object instead of raw values. +For example, the `getIncidentsByServiceId` query method now returns an object in the shape of `{ incidents: Incident[] }` +instead of just `Incident[]`. +This same pattern goes for `getChangeEventsByServiceId` and `getOnCallByPolicyId` functions. + +**BREAKING** All public exported types that relate to entities within PagerDuty have been prefixed with `PagerDuty` (e.g. `ServicesResponse` is now `PagerDutyServicesResponse` and `User` is now `PagerDutyUser`) + +In addition, various enhancements/bug fixes were introduced: + +- The `PagerDutyCard` component now wraps error and loading messages with an `InfoCard` to contain errors/messages. This enforces a consistent experience on the EntityPage +- If no service can be found for the provided integration key, a new Error Message Empty State component will be shown instead of an error alert +- Introduces the `fetchApi` to replace standard `window.fetch` + - ensures that Identity Authorization is respected and provided in API requests diff --git a/.changeset/wet-dolphins-act.md b/.changeset/wet-dolphins-act.md new file mode 100644 index 0000000000..b975f464cb --- /dev/null +++ b/.changeset/wet-dolphins-act.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-github-actions': patch +--- + +Minor cleanup of the API surface. diff --git a/.changeset/wicked-icons-grin.md b/.changeset/wicked-icons-grin.md new file mode 100644 index 0000000000..8d26a9255e --- /dev/null +++ b/.changeset/wicked-icons-grin.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder': patch +--- + +Swap usage of `MaterialTable` with `Table` from `core-components` diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index b4f825d221..3401dc5b2b 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -14,10 +14,8 @@ yarn.lock @backstage/reviewers @backst /docs/assets/search @backstage/reviewers @backstage/techdocs-core /docs/features/search @backstage/reviewers @backstage/techdocs-core /docs/features/techdocs @backstage/reviewers @backstage/techdocs-core -/packages/search-common @backstage/reviewers @backstage/techdocs-core /packages/techdocs-cli @backstage/reviewers @backstage/techdocs-core /packages/techdocs-cli-embedded-app @backstage/reviewers @backstage/techdocs-core -/packages/techdocs-common @backstage/reviewers @backstage/techdocs-core /plugins/allure @backstage/reviewers @deepak-bhardwaj-ps /plugins/apache-airflow @backstage/reviewers @cmpadden /plugins/api-docs @backstage/reviewers @backstage/sda-se-reviewers @@ -42,14 +40,16 @@ yarn.lock @backstage/reviewers @backst /plugins/jenkins-backend @backstage/reviewers @timja /plugins/kafka @backstage/reviewers @nirga /plugins/kafka-backend @backstage/reviewers @nirga +/plugins/kubernetes @backstage/reviewers @backstage/warpspeed +/plugins/kubernetes-* @backstage/reviewers @backstage/warpspeed /plugins/newrelic-dashboard @backstage/reviewers @mufaddal7 /plugins/scaffolder-backend-module-rails @backstage/reviewers @angeliski /plugins/scaffolder-backend-module-yeoman @backstage/reviewers @pawelmitka /plugins/search @backstage/reviewers @backstage/techdocs-core /plugins/search-* @backstage/reviewers @backstage/techdocs-core +/plugins/sonarqube @backstage/reviewers @backstage/sda-se-reviewers /plugins/stack-overflow @backstage/reviewers @backstage/techdocs-core /plugins/stack-overflow-backend @backstage/reviewers @backstage/techdocs-core -/plugins/sonarqube @backstage/reviewers @backstage/sda-se-reviewers /plugins/techdocs @backstage/reviewers @backstage/techdocs-core /plugins/techdocs-* @backstage/reviewers @backstage/techdocs-core /tech-insights-backend @backstage/reviewers @xantier @iain-b diff --git a/.github/vale/Vocab/Backstage/accept.txt b/.github/vale/Vocab/Backstage/accept.txt index 7631640199..8428dc2d0e 100644 --- a/.github/vale/Vocab/Backstage/accept.txt +++ b/.github/vale/Vocab/Backstage/accept.txt @@ -85,6 +85,8 @@ dockerfiles Dockerize dockerode Docusaurus +dynatrace +Dynatrace ecco env Env @@ -275,6 +277,7 @@ scrollbar seb semlas semver +serializable Serverless shoutout siloed @@ -301,6 +304,7 @@ superfences Superfences superset supertype +storable talkdesk Talkdesk tasklist diff --git a/.github/workflows/verify_docs-quality.yml b/.github/workflows/verify_docs-quality.yml index fe8849a085..736f09571f 100644 --- a/.github/workflows/verify_docs-quality.yml +++ b/.github/workflows/verify_docs-quality.yml @@ -21,7 +21,7 @@ jobs: run: echo "::set-output name=args::$(node scripts/check-docs-quality.js --ci-args)" - name: documentation quality check - uses: errata-ai/vale-action@v1.5.0 + uses: errata-ai/vale-action@v2.0.1 with: # This also contains --config=.github/vale/config.ini ... :/ files: '${{ steps.generate.outputs.args }}' diff --git a/.github/workflows/verify_e2e-windows.yml b/.github/workflows/verify_e2e-windows.yml index 129c246a73..934ef8f06c 100644 --- a/.github/workflows/verify_e2e-windows.yml +++ b/.github/workflows/verify_e2e-windows.yml @@ -56,3 +56,4 @@ jobs: run: yarn e2e-test run env: DEBUG: zombie + CYPRESS_VERIFY_TIMEOUT: 600000 diff --git a/ADOPTERS.md b/ADOPTERS.md index 3915aee949..10b605a8d4 100644 --- a/ADOPTERS.md +++ b/ADOPTERS.md @@ -21,7 +21,7 @@ _If you're using Backstage in your organization, please try to add your company | [Fiverr](https://www.fiverr.com) | [@nirga](https://github.com/nirga) | Unifying separate tools that developers are using today (i.e. monitoring, dead letter queues management, etc.) into a single platform. | | [Zalando SE](https://www.zalando.de) | [@leviferreira](https://github.com/leviferreira) | Building V2 of the Internal Development Portal. | | [LegalZoom](https://legalzoom.com) | [@backjo](https://github.com/backjo) | Developer portal - hub for all engineering projects and metadata. | -| [Expedia Group](https://www.expediagroup.com) | [Mike Turner](mailto:miturner@expediagroup.com), [Sneha Kumar](mailto:snkumar@expediagroup.com), [@guillermomanzo](https://github.com/guillermomanzo), [Erik Lindgren](https://github.com/lindgren) | EG Common Developer Toolkit | +| [Expedia Group](https://www.expediagroup.com) | [@guillermomanzo](https://github.com/guillermomanzo), [Sheena Sharma](mailto:shesharma@expediagroup.com) | EG Common Developer Toolkit | | [Paddle.com](https://paddle.com) | [Ioannis Georgoulas](https://github.com/geototti21) | Developer portal (Tech Docs, Service Catalog, Internal Tooling), we use vanilla Backstage FE and custom BE implementation in Go | | [Acast.com](https://acast.com) | [Olle Lundberg](https://github.com/lndbrg) | Developer portal with tech docs, service catalog and a bunch of other internal tooling | | [Lunar](https://lunar.app) | [Jacob Valdemar](https://github.com/JacobValdemar) | Internal developer portal for service overview and insights, API documentation, technical guides, onboarding guides and RFC's. | @@ -36,7 +36,7 @@ _If you're using Backstage in your organization, please try to add your company | [PagerDuty](https://www.pagerduty.com/) | [Mark Shaw](https://github.com/markshawtoronto) | Developer portal, initially focused on software templates and tech-docs. | | [MoonShiner](https://moonshiner.at) | [Fabian Hippmann](https://github.com/FabianHippmann) | Developer portal - helps us keep track of our customer projects, onboard new developers & improve our development process ๐ŸŒ•๐Ÿš€๐Ÿง‘โ€๐Ÿš€ | | [FundApps](https://www.fundapps.co/) | [Elliot Greenwood](https://github.com/egnwd) | Developer Portal - A place for us to keep track of our projects and documentation for all services and processes | -| [DAZN](https://dazn.com/) | [Lou Bichard](https://twitter.com/loujaybee), [Marco Crivellaro](https://github.com/crivetechie), [Alex Hollerith](mailto:alex.hollerith@dazn.com) | Ingesting all of DAZN's repos for the catalog, migrating our internal platform apps (pull request boards, release information, inner source marketplace etc) to Backstage plugins (where applicable). | +| [DAZN](https://dazn.com/) | [Lou Bichard](https://twitter.com/loujaybee), [Marco Crivellaro](https://github.com/crivetechie), [Alex Hollerith](mailto:alex.hollerith@dazn.com), [Kamil Wolny](https://github.com/mrwolny) | Ingesting all of DAZN's repos for the catalog, migrating our internal platform apps (pull request boards, release information, inner source marketplace etc) to Backstage plugins (where applicable). | | [HelloFresh](https://www.hellofresh.de/) | [@iammuho](https://github.com/iammuho), [@ElenaForester](https://github.com/ElenaForester), [@diegomarangoni](https://github.com/diegomarangoni) | Our developer portal at HelloFresh - Spread across an organisation of 500+ engineers globally. | | [FactSet](https://www.factset.com/) | [@kuangp](https://github.com/kuangp) | Developer portal to provide discoverability to all internal components, APIs, documentation, and scaffold templates with integrations to our internal infrastructure tools. | | [Workrise](https://www.workrise.com/) | [Michael Rode](https://github.com/michaelrode) | Developer portal, main gateway to our infrastructure, documentation and internal tooling. | @@ -122,8 +122,64 @@ _If you're using Backstage in your organization, please try to add your company | [World Fuel Services](https://www.wfscorp.com/) | [Anirudh Kurapathi](https://github.com/anirudhkurapati), [Alex Kwon](https://github.com/alexkwon), [Lester Hernandez](https://github.com/lhernandez-wfscorp), [Avi Boru](https://github.com/aviboru), [Vardhan Annapureddy](https://github.com/harshaaws) | Internal Developer Portal, service catalog product, API's, Software Templates, tech docs and more. | | [leboncoin](https://www.leboncoin.fr/) | [Andy Ladjadj](https://github.com/aladjadj) | Centralize our multiple UI in a single portal. Simplify onbording, new features and harmonize how people search information. Core features (catalog,api,docs,scafolder) are good to start the adoption. status: internal beta. | | [Contentful](https://www.contentful.com) | [James Bourne](https://github.com/jamesmbourne) | Centralized documentation of service ownership, APIs, and documentation, and new service creation with a custom scaffolder - [full case study with Roadie](https://roadie.io/case-studies/maintaining-velocity-through-hypergrowth-contentful/). | -| [Back Market](https://www.backmarket.com) | [Sami Farhat](https://github.com/skfarhat) | Internal Developer Portal featuring catalog, tech-radar, ownership management, component creation (scaffolder) and centralized infrastructure management -- probably more to come. | -| [Avalia Systems](https://avalia.io) | [Olivier Liechti](https://github.com/wasadigi), [Fabio Velloso](https://github.com/fabiovelloso) | Innersource, software analytics, knowledge base for 360 software assessments, collaborative applications, hub for tracking and sharing IP assets. | -| [Albert Heijn](https://ah.technology) | [Joost Hofman](https://github.com/joosthofman), [Reindrich Geerman](https://github.com/reinst) | Single point of entry for all our engineers (Developer portal), Tech radar, catalog, templates (paved roads) and tech documentation. | -| [Wise, formerly TransferWise](https://wise.com) | [Andrew Beveridge](https://github.com/beveradb) | It's early days for us, we're trying to start small with catalog, tech docs and unified search. Future ambitious vision includes scaffolder for one-click component addition, building out integrations with CI/CD tooling, kubernetes clusters, monitoring/alerting tooling etc. and aiming for a frictionless "golden path" for engineers! ๐Ÿš€ | - +| [Back Market](https://www.backmarket.com) | [Sami Farhat](https://github.com/skfarhat) | Internal Developer Portal featuring catalog, tech-radar, ownership management, component creation (scaffolder) and centralized infrastructure management -- probably more to come. | +| [Avalia Systems](https://avalia.io) | [Olivier Liechti](https://github.com/wasadigi), [Fabio Velloso](https://github.com/fabiovelloso) | Innersource, software analytics, knowledge base for 360 software assessments, collaborative applications, hub for tracking and sharing IP assets. | +| [Albert Heijn](https://ah.technology) | [Joost Hofman](https://github.com/joosthofman), [Reindrich Geerman](https://github.com/reinst) | Single point of entry for all our engineers (Developer portal), Tech radar, catalog, templates (paved roads) and tech documentation. | +| [Wise, formerly TransferWise](https://wise.com) | [Andrew Beveridge](https://github.com/beveradb) | It's early days for us, we're trying to start small with catalog, tech docs and unified search. Future ambitious vision includes scaffolder for one-click component addition, building out integrations with CI/CD tooling, kubernetes clusters, monitoring/alerting tooling etc. and aiming for a frictionless "golden path" for engineers! ๐Ÿš€ | +| [Happy Money](http://happymoney.com/) | [Akshit Lomash](mailto:alomash@happymoney.com) | We are moving from a monolith to microservices-based architecture. We are developing a developer portal based on Backstage to create a service catalog for our new services. All the services created are onboarded Backstage and engineering teams are using a cookie-cutter-based template from backstage to initiate a new service. | +| [Lightspeed](http://lightspeedhq.com/) | [Marcus Crane](mailto:marcus.crane@lightspeedhq.com) | We use it within our X-Series division (https://vendhq.com) to catalog ~100+ systems and ~350 components! | +| [Siemens](https://www.siemens.com/global/en.html) | [Nizar Chaouch](mailto:nizar.chaouch@siemens.com) | We are using Backstage as our Developer portal | +| [The Warehouse Group](https://www.thewarehouse.co.nz) | [Matt Law](mailto:matt.law@thewarehouse.co.nz) | Backstage enables us to bootstrap our middleware environment of new services for our Dev teams in a matter of seconds. CI, CD, testing, logging, deployments are all taken care of to get them up and running in less than 60 seconds. | +| [Tink](https://tink.com/) | [Sebastian Olsson](https://github.com/Sebelino), [Bล‚aลผej Szum](https://github.com/blazejszumtink), [Anders Eurenius Runvald](https://github.com/anders-er-at-tink) | Internal developer portal which provides templates for creating new Java or Go microservices seamlessly. Also includes a tech radar and a visualization of our CD pipeline. | +| [Brandwatch](https://brandwatch.com) | [Stefan Buck](https://github.com/stefanbuck) | Our primary focus is on the service catalog. Backstage is replacing our homemade service catalog. The switch was quite simple due to the catalog processor API. | +| [Laybuy](https://www.laybuy.com) | [Chris Simmons](https://github.com/contrarianchris) | Backstage is the heart of Laybuyโ€™s new centralised Development Platform, bringing disparate development tools and experiences into a single easy-to-use portal. It simplifies software and API discovery, project scaffolding, and technical documentation, enabling us to embrace golden path development and automate software standards. | +| [Sendinblue](https://engineering.sendinblue.com/) | [Tanguy Antoine](mailto:antoine.tanguy@sendinblue.com) | Helps us drive the change at scale. Puts light on services, resources, and dependencies. One tool that rules them all through plugins we created for that purpose. We are aiming to put Backstage at the center of every developer's work (Actionable items, Debugging, Monitoring, Provisioning, etc...) to improve their happiness | +| [SafetyCulture](https://safetyculture.com/) | [@R-cen](https://github.com/R-cen), [@lachlancooper](https://github.com/lachlancooper), [@hkf57](https://github.com/hkf57) | Internal developer portal to provide a centralized place for engineers to see an overview of their team's services and information related to the service from other systems. Initially focused on the software catalog, techdocs and search. | +| [Sana Life Science](https://sanalifescience.com) | [Joe Hillyard](mailto:joe@sanalifescience.com) | API Catalog, Tools Management & Control Hub | +| [Ndustrial](https://ndustrial.io) | [Jonathan Skubic](mailto:jonathan@ndustrial.io) | Software Project Catalog | +| [TUI Musement](https://www.musement.com/uk/) | [Simone Fumagalli](mailto:simone.fumagalli@musement.com) | We are importing our catalog into it to keep it under control. The next step is start using templates | +| [Kambi AB](https://www.kambi.com) | [Martin Norum](mailto:martin.norum@kambi.com) | We want to kick ass at speed, so we're currently building up a catalog of our existing software, and looking into how Backstage can support us in our journey towards autonomous product teams. Both to improve speed to market and operational awareness. | +| [ANZ](https://www.anz.com.au/personal/) | [Elliot Jackson](mailto:elliot.jackson@anz.com) | Catalog, tech docs and automation | +| [Genie Solutions](https://www.geniesolutionssoftware.com.au) | [Zainab Bagasrawala](mailto:zainabbagasrawala@geniesolutions.com.au) | Developer Portal to track our projects, documentation, observability tools and more | +| [MadeiraMadeira](https://www.madeiramadeira.com.br) | [Paulo Eduardo Peixoto](mailto:paulo.peixoto@madeiramadeira.com.br) | As a support tool for developers, following the principles of "Developer Experience". In order to make the developer's day to day more practical, efficient and, why not, happy. | +| [Sonatype](https://www.sonatype.com) | [Srikar Ananthula](mailto:sananthula@sonatype.com) | Centralize services used internally with many plugins | +| [CVS Health](https://www.cvshealth.com) | [Ari Ben-Elazar](mailto:abenelazar@gmail.com) | Cataloging and documenting our service offerings to offer our internal developers a better operational journey | +| [Yatra.com](https://www.yatra.com) | [Matiur Rahman Maitur](mailto:arifrahman4u@gmail.com) | Easy to find out Project details, ownership, dependent services, Documentation, it is very useful for developer. | +| [Yotpo](https://www.yotpo.com) | [Liran Yogev](mailto:lyogev@yotpo.com) | Services exploration, documentation and project generator | +| [Mainsail Industries](https://www.mainsailindustries.com) | [Brad Sollar](mailto:brad@mainsailindustries.com) | Internal tool management and docs | +| [Prisma](https://prismamp.com) | [Sebastian Gravina](mailto:sgravina@prismamp.com) | Is part of our IDP | +| [Syndetic](https://syndetic.io) | [John Feminella](mailto:robots+swag@syndetic.io) | We're working with multiple F100 clients for digital transformation and Backstage is a key part of our landing and acceleration strategy. | +| [Imagine Learning](https://www.imaginelearning.com/en/us) | [Jared Stehler](mailto:jared.stehler@imaginelearning.com) | Software catalog, product dependency visualization | +| [Alef Education](https://www.alefeducation.com) | [Belal Juma](mailto:belal.juma@alefeducation.com) | We use backstage as a Service Catalog and rely on the TechDocs feature | +| [Zego](https://www.zego.com) | [Sean Kenny](mailto:sean.kenny@zego.com) | Single pane of glass for organisational and operational information for all services across our systems. | +| [Absa Group Limited](https://www.absa.africa/absaafrica/) | [Chris Kieser](mailto:chris.kieser@absa.africa) | Developer portal for all development needs - security, AWS, k8s, build and deployment pipelines and more | +| [Nutrien Ag Solutions](https://www.nutrienagsolutions.com.au) | [Jan Quijano](mailto:jan.quijano@nutrien.com.au) | Software Project Catalog | +| [Lendingkart](https://www.lendingkart.com/) | [Dinesh Rajpoot](mailto:dinesh.rajpoot@lendingkart.com) | Service catalog, Software templates to enforce best practices and tech insights to track mandates & migrations. | +| [Meltwater](https://underthehood.meltwater.com) | [@spier](https://github.com/spier), [@remen](https://github.com/remen) | Improving developer experience by centralizing documentation and internal APIs. Goal: Foster InnerSource collaboration and speed up onboarding time in our 500+ people Product & Engineering org. | +| [Doctolib](https://doctolib.engineering/) | [@djiit](https://github.com/djiit) | Rails modularization effort awareness, tech organization discoverability. Improving the daily workflows and collaboration processes of our engineers. | +| [Twilio](https://www.twilio.com) | [Kyle Smith](https://github.com/knksmith57) | Developer portal, universal software catalog, and centralized taxonomy platform. | +| [OVHcloud](https://www.ovhcloud.com/fr/) | [Jean-Philippe Blary](https://github.com/blaryjp), [Arnaud Bauer](mailto:arnaud.bauer@ovhcloud.com), [Flavien Chantelot](https://github.com/Dorn-) | We're providing Backstage to our collaborators to ease their daily jobs, and let them extends it using plugins. | +| [Procter & Gamble](https://us.pg.com/) | [Binita Nayak](https://github.com/binitan), [Josh Rose](https://github.com/joshuarose), [RJ Winkler](https://github.com/rjwink) | P&G leverages Backstage to build internal developer portal to ensure developers' happiness. This developer portal shall act as single source of information needed by development teams to seamlessly create, find and maintain their software components/resources/documentation. | +| [SANS Institute](https://www.sans.org) | [Christopher Klewin](mailto:cklewin@sans.org) | Developer portal for centralized visibility, reporting, and tooling across multiple organizations. | +| [Okay](https://www.okayhq.com/) | [Tomas Barreto](mailto:tomas@okayhq.com) | Service catalog, developer portal, and technical documentation | +| [Kaluza](https://www.kaluza.com) | [James Condren](mailto:james.condron@kaluza.com) | To provide an automated golden path to developers, with a focus on discovery and documentation | +| [LinkedIn](https://linkedin.com) | [Joshua Lawrence](mailto:jlawrence@linkedin.com) | We are building a platform for internal web tools | +| [Forto](https://forto.com) | [Rodolfo Matos](mailto:rodolfo.matos@forto.com) | Still in a experimental phase/assessing the organisational fit. We will be using it mostly a developer portal -- pretty standard use case. | +| [BetterUp](https://betterup.com) | [Jordan Hochenbaum](mailto:jordan.hochenbaum@betterup.co) | We're starting to use Backstage as the central hub for service discovery, documentation, and develop experience. | +| [warung pintar](https://warungpintar.co.id/) | [Muhammad Rafly Andrianza](mailto:rafly.andrianza@warungpintar.co) | Initial Work Developer Portal with TechRadar, Service Catalogue, TechDocs, anything about platform & infrastructure resources. | +| [RD](https://rd.com.br/) | [Michael Silva](mailto:midsilva@rd.com.br) | We are building our developer portal. Software catalog, Tech Radar and Scaffolding are among the initial features. | +| [AEB](https://www.aeb.com/) | [David Fankhรคnel](mailto:dfl@aeb.com) | Central developer platform for creating new apps via templates, getting an overview via software catalog, etc | +| [SALTO Systems](https://saltosystems.com) | [Ian Cowley](mailto:i.cowley@saltosystems.com) | Currently using Backstage as an internal documentation portal. | +| [Lummo](https://lummo.com) | [Anjul Sahu](mailto:anjul@lummo.com) | We are building the internal developer portal using Backstage and bringing up all integrations and service information at one place. | +| [Frontside](https://frontside.com) | [Taras Mankovski](mailto:taras@frontside.com) | +| [Stepstone](https://www.stepstone.com/en/) | [Neil Kennedy](mailto:neil.kennedy@stepstone.com) | StepStone is using Backstage to solve problems around ownership and visibility of our applications. We have thousands of repos, multiple legacy systems and a growing platform that is hard to maintain. Backstage is forming the centre of our push to embrace the chaos. | +| [idwall](https://idwall.co) | [Rodrigo Catรฃo Araujo](mailto:rodrigo@idwall.co) | Developer Portal for internal engineers to access service catalog, documentation, observability, infrastructure and internal tooling. | +| [Jaguar Land Rover](https://www.jaguarlandrover.com) | [Josh Walker](mailto:jwalke18@jaguarlandrover.com) | Users can request a Gitlab user, which creates a commit with the Terraform code. | +| [Glovo](http://glovoapp.com/) | [Yaser Toutah](mailto:yaser.toutah@glovoapp.com) | Developer Portal to improve our Developer Experience, identify ownership and track metadata for our services and tools. It's our Service Catalog. In addition to that, we use it for Service Creation, and much more. | +| [Dixa](https://dixa.com) | [Jens Mรธller](mailto:jsc@dixa.com) | We are in early stages, but using it to get overview of our repositories and ownership of these. We want among many things to use it for compliance and easier access to key metrics for our repos. | +| [Notino](https://notino.com) | [Jan Remunda](mailto:jan.remunda@notino.com) | Backstage is our developer portal. We use it as service catalog and for technical documentation. | +| [Polarpoint](https://polarpoint.io/) | [Surj Bains](https://github.com/polarpoint-io) | We are using Backstage as our Developer portal as well as for hosting our DevOps portal for software catalog. | +| [Niche](https://niche.com) | [Zach Romitz](mailto:zach.romitz@niche.com) | We are using the Software Catalog, Software Templates, API documentation, and Techdocs to try and centralize service information. | +| [Mercedes-Benz.io](https://www.mercedes-benz.io/) | [Manuel Santos](https://github.com/manusant) | At Mercedes-Benz we use it as a developer portal with software catalog, TechDocs, Scaffolding and custom plugins. It provides an overview of our tech ecosystem to our product development teams. The portal also serves as a way to foster collaboration among the numerous companies of the Mercedes-Benz Group. +| [Funding Circle](https://www.fundingcircle.com/) | [Ariel Pacciaroni](https://github.com/arielpacciaroni) | We are building the internal developer portal using Backstage project and centralizing all services information at one place. The portal helps us track down repositories ownership as well as direct access to key information on every component. + diff --git a/app-config.yaml b/app-config.yaml index eb51c59847..a56a3df9d1 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -115,6 +115,11 @@ proxy: headers: Authorization: Basic ${GOCD_AUTH_CREDENTIALS} + '/dynatrace': + target: https://your.dynatrace.instance.com/api/v2 + headers: + Authorization: 'Api-Token ${DYNATRACE_ACCESS_TOKEN}' + organization: name: My Company @@ -131,6 +136,9 @@ techdocs: publisher: type: 'local' # Alternatives - 'googleGcs' or 'awsS3' or 'azureBlobStorage' or 'openStackSwift'. Read documentation for using alternatives. +dynatrace: + baseUrl: https://your.dynatrace.instance.com + sentry: organization: my-company @@ -163,26 +171,26 @@ integrations: github: - host: github.com token: ${GITHUB_TOKEN} - ### Example for how to add your GitHub Enterprise instance using the API: - # - host: ghe.example.net - # apiBaseUrl: https://ghe.example.net/api/v3 - # token: ${GHE_TOKEN} - ### Example for how to add your GitHub Enterprise instance using raw HTTP fetches (token is optional): - # - host: ghe.example.net - # rawBaseUrl: https://ghe.example.net/raw - # token: ${GHE_TOKEN} + ### Example for how to add your GitHub Enterprise instance using the API: + # - host: ghe.example.net + # apiBaseUrl: https://ghe.example.net/api/v3 + # token: ${GHE_TOKEN} + ### Example for how to add your GitHub Enterprise instance using raw HTTP fetches (token is optional): + # - host: ghe.example.net + # rawBaseUrl: https://ghe.example.net/raw + # token: ${GHE_TOKEN} gitlab: - host: gitlab.com token: ${GITLAB_TOKEN} - bitbucket: - - host: bitbucket.org - username: ${BITBUCKET_USERNAME} - appPassword: ${BITBUCKET_APP_PASSWORD} - ### Example for how to add your bitbucket server instance using the API: - # - host: server.bitbucket.com - # apiBaseUrl: server.bitbucket.com - # username: ${BITBUCKET_SERVER_USERNAME} - # appPassword: ${BITBUCKET_SERVER_APP_PASSWORD} + ### Example for how to add a bitbucket cloud integration + # bitbucketCloud: + # - username: ${BITBUCKET_USERNAME} + # appPassword: ${BITBUCKET_APP_PASSWORD} + ### Example for how to add your bitbucket server instance using the API: + # - host: server.bitbucket.com + # apiBaseUrl: server.bitbucket.com + # username: ${BITBUCKET_SERVER_USERNAME} + # appPassword: ${BITBUCKET_SERVER_APP_PASSWORD} azure: - host: dev.azure.com token: ${AZURE_TOKEN} diff --git a/contrib/chart/backstage/Chart.lock b/contrib/chart/backstage/Chart.lock index b06f1ea1cb..6220a481dd 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: 11.2.4 -digest: sha256:782c8593a80e332b19f736d48f4635e424bbf0575ed9d587c0a301cccfcedce8 -generated: "2022-05-19T10:46:51.441725486Z" + version: 9.8.12 +digest: sha256:549b9a0cdf7b2e0ad949ebad853a467bf320928970a946fb0ef7e13e9bdb7a10 +generated: "2022-05-20T08:15:48.301491565Z" diff --git a/contrib/chart/backstage/Chart.yaml b/contrib/chart/backstage/Chart.yaml index 3776f4a41c..9770ef665c 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: 11.2.4 + version: 9.8.12 repository: https://charts.bitnami.com/bitnami maintainers: diff --git a/contrib/docker/cookiecutter-with-jinja2-extensions/Dockerfile b/contrib/docker/cookiecutter-with-jinja2-extensions/Dockerfile index b854a00675..e41d324730 100644 --- a/contrib/docker/cookiecutter-with-jinja2-extensions/Dockerfile +++ b/contrib/docker/cookiecutter-with-jinja2-extensions/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.15 +FROM alpine:3.16 RUN apk add --update \ git \ diff --git a/contrib/docs/tutorials/prometheus-metrics-output.png b/contrib/docs/tutorials/prometheus-metrics-output.png new file mode 100644 index 0000000000..f7fac739ac Binary files /dev/null and b/contrib/docs/tutorials/prometheus-metrics-output.png differ diff --git a/contrib/docs/tutorials/prometheus-metrics.md b/contrib/docs/tutorials/prometheus-metrics.md new file mode 100644 index 0000000000..5b7dc61df3 --- /dev/null +++ b/contrib/docs/tutorials/prometheus-metrics.md @@ -0,0 +1,108 @@ +# Prometheus + +## Overview + +This is a small tutorial that goes over how to setup your Backstage instance to output metrics in a format that can be pulled in by Prometheus. + +## How to Setup Prometheus Metrics + +1. First we need to add the needed dependencies to the `package.json` in the `\packages\backend`: + + ```diff + // packages/backend/package.json + "dependencies": { + + "express-prom-bundle": "^6.3.6", + + "prom-client": "^14.0.1", + ``` + +2. Now we want to run `yarn install` from the root of the project to get those dependencies in place +3. Then we need to add a handler for the metrics by creating a file called `metrics.ts` in the `\packages\backend\src` folder +4. Next we add the following content to the `metrics.ts` file: + + ```ts + // packages/backend/src/metrics.ts + import { useHotCleanup } from '@backstage/backend-common'; + import { RequestHandler } from 'express'; + import promBundle from 'express-prom-bundle'; + import prom from 'prom-client'; + import * as url from 'url'; + + const rootRegEx = new RegExp('^/([^/]*)/.*'); + const apiRegEx = new RegExp('^/api/([^/]*)/.*'); + + export function normalizePath(req: any): string { + const path = url.parse(req.originalUrl || req.url).pathname || '/'; + + // Capture /api/ and the plugin name + if (apiRegEx.test(path)) { + return path.replace(apiRegEx, '/api/$1'); + } + + // Only the first path segment at root level + return path.replace(rootRegEx, '/$1'); + } + + /** + * Adds a /metrics endpoint, register default runtime metrics and instrument the router. + */ + export function metricsHandler(): RequestHandler { + // We can only initialize the metrics once and have to clean them up between hot reloads + useHotCleanup(module, () => prom.register.clear()); + + return promBundle({ + includeMethod: true, + includePath: true, + // Using includePath alone is problematic, as it will include path labels with high + // cardinality (e.g. path params). Instead we would have to template them. However, this + // is difficult, as every backend plugin might use different routes. Instead we only take + // the first directory of the path, to have at least an idea how each plugin performs: + normalizePath, + promClient: { collectDefaultMetrics: {} }, + }); + } + ``` + +5. Now we will extend the router configuration with the `metricsHandler`: + + ```diff + +import { metricsHandler } from './metrics'; + + ... + + const service = createServiceBuilder(module) + .loadConfig(config) + .addRouter('', await healthcheck(healthcheckEnv)) + + .addRouter('', metricsHandler()) + .addRouter('/api', apiRouter); + ``` + +6. You now have everything setup, from the `\packages\backend` folder run `yarn start` this will start up the backend +7. Now in a browser load up `http://localhost:7007/metrics`, if everything went smoothly you should see metrics in your browser something like this: + + ![Prometheus Metrics Example Output](prometheus-metrics-output.png) + +## Metrics + +The following sections goes over the included and experimental metrics available once you have completed this tutorial + +## Included + +This tutorials uses the [`express-prom-bundle`](https://github.com/jochen-schweizer/express-prom-bundle) and the [`prom-client`](https://github.com/siimon/prom-client) to make this all work. They both come with some built in metrics: + +- `express-prom-bundle` comes with 2 metrics: + - `up`: this normally will be just 1 + - `http_request_duration_seconds`: http latency histogram/summary labeled with `status_code`, `method` and `path` +- `prom-client` comes with a collection of metrics around memory, CPU, processes, etc. You can see the supported metrics in the `prom-client's` [`lib/metrics`](https://github.com/siimon/prom-client/tree/master/lib/metrics) folder. + +### Experimental + +There are some custom metrics that have been added to Backstage will be output for you, these are currently deemed experimental and may be changed or removed in a future release. Here is a rough list, again subject to changes: + +- `catalog_entities_count`: Total amount of entities in the catalog +- `catalog_registered_locations_count`: Total amount of registered locations in the catalog +- `catalog_relations_count`: Total amount of relations between entities +- `catalog_stitched_entities_count`: Amount of entities stitched +- `catalog_processed_entities_count`: Amount of entities processed +- `catalog_processing_duration_seconds`: Time spent executing the full processing flow +- `catalog_processors_duration_seconds`: Time spent executing catalog processors +- `catalog_processing_queue_delay_seconds`: The amount of delay between being scheduled for processing, and the start of actually being processed diff --git a/contrib/search/confluence/ConfluenceCollator.md b/contrib/search/confluence/ConfluenceCollator.md index f636ae88ea..7d7c8641f3 100644 --- a/contrib/search/confluence/ConfluenceCollator.md +++ b/contrib/search/confluence/ConfluenceCollator.md @@ -1,7 +1,7 @@ ConfluenceCollator.ts reference ```ts -import { DocumentCollator } from '@backstage/search-common'; +import { DocumentCollator } from '@backstage/plugin-search-common'; import fetch from 'cross-fetch'; export class ConfluenceCollator implements DocumentCollator { diff --git a/contrib/search/confluence/ConfluenceResultListItem.md b/contrib/search/confluence/ConfluenceResultListItem.md index ba7c4f91a9..059048d731 100644 --- a/contrib/search/confluence/ConfluenceResultListItem.md +++ b/contrib/search/confluence/ConfluenceResultListItem.md @@ -3,7 +3,7 @@ ConfluenceResultListItem.tsx reference ```tsx import React from 'react'; import { Link } from '@backstage/core-components'; -import { IndexableDocument } from '@backstage/search-common'; +import { IndexableDocument } from '@backstage/plugin-search-common'; import { Divider, ListItem, diff --git a/cypress/package.json b/cypress/package.json index 6a231326c4..cc66949d28 100644 --- a/cypress/package.json +++ b/cypress/package.json @@ -5,7 +5,7 @@ "license": "MIT", "private": true, "dependencies": { - "cypress": "^7.3.0", + "cypress": "^10.0.0", "typescript": "^4.1.3" } } diff --git a/cypress/src/integration/plugins/catalog.spec.ts b/cypress/src/integration/plugins/catalog.spec.ts index adf9b9a384..50ef3145e5 100644 --- a/cypress/src/integration/plugins/catalog.spec.ts +++ b/cypress/src/integration/plugins/catalog.spec.ts @@ -82,5 +82,118 @@ describe('Catalog', () => { .contains('Sub-page 1') .should('be.visible'); }); + + it('Should render addons on docs tab homepage', () => { + cy.loginAsGuest(); + + cy.visit('/catalog'); + + cy.contains('techdocs-e2e-fixture').click(); + + cy.location().should(loc => { + expect(loc.pathname).to.eq( + '/catalog/default/component/techdocs-e2e-fixture', + ); + }); + + cy.getCatalogDocsTab().click(); + + cy.wait(300); + + cy.getTechDocsShadowRoot() + .find('h1') + .contains('Home page') + .should('be.visible'); + + // highlight a snippet of text + cy.getTechDocsShadowRoot() + .find('article > p') + .then($el => { + const el = $el[0]; + const document = el.ownerDocument; + const range = document.createRange(); + range.selectNodeContents(el); + document?.getSelection()?.removeAllRanges(); + document?.getSelection()?.addRange(range); + }); + + cy.document().trigger('selectionchange'); + + // wait for new issue default debounce time + cy.wait(600); + + // assert that the new issue button has a right url + cy.getTechDocsShadowRoot() + .contains('Open new Github issue') + .should( + 'have.attr', + 'href', + 'https://github.com/backstage/backstage/issues/new?title=Documentation%20feedback%3A%20This%20is%20a%20basic%20documentation%20used%20for%20end-to-end%20tests.&body=%23%23%20Documentation%20Feedback%20%F0%9F%93%9D%0A%0A%20%23%23%23%23%20The%20highlighted%20text%3A%20%0A%0A%20%3E%20This%20is%20a%20basic%20documentation%20used%20for%20end-to-end%20tests.%0A%0A%20%23%23%23%23%20The%20comment%20on%20the%20text%3A%20%0A%20_%3Ereplace%20this%20line%20with%20your%20comment%3C_%0A%0A%20___%0ABackstage%20URL%3A%20%3Chttp%3A%2F%2Flocalhost%3A7007%2Fcatalog%2Fdefault%2Fcomponent%2Ftechdocs-e2e-fixture%2Fdocs%3E%20%0AMarkdown%20URL%3A%20%3Chttps%3A%2F%2Fgithub.com%2Fbackstage%2Fbackstage%2Fblob%2Fmaster%2Fcypress%2Ffixtures%2Fdocs%2Findex.md%3E', + ); + }); + + it('Should render addons on docs tab sup-page', () => { + cy.loginAsGuest(); + + cy.visit('/catalog'); + + cy.contains('techdocs-e2e-fixture').click(); + + cy.location().should(loc => { + expect(loc.pathname).to.eq( + '/catalog/default/component/techdocs-e2e-fixture', + ); + }); + + cy.getCatalogDocsTab().click(); + + cy.wait(300); + + cy.getTechDocsShadowRoot() + .find('h1') + .contains('Home page') + .should('be.visible'); + + cy.getTechDocsShadowRoot().within(() => { + cy.getTechDocsNavigation().find('a').contains('Sub-page 1').click(); + }); + + cy.location().should(loc => { + expect(loc.pathname).to.eq( + '/catalog/default/component/techdocs-e2e-fixture/docs/sub-page-one/', + ); + }); + + cy.getTechDocsShadowRoot() + .find('h1') + .contains('Sub-page 1') + .should('be.visible'); + + // highlight a snippet of text + cy.getTechDocsShadowRoot() + .find('#section-11') + .then($el => { + const el = $el[0]; + const document = el.ownerDocument; + const range = document.createRange(); + range.selectNodeContents(el); + document?.getSelection()?.removeAllRanges(); + document?.getSelection()?.addRange(range); + }); + + cy.document().trigger('selectionchange'); + + // wait for new issue default debounce time + cy.wait(600); + + // assert that the new issue button has a right url + cy.getTechDocsShadowRoot() + .contains('Open new Github issue') + .should( + 'have.attr', + 'href', + 'https://github.com/backstage/backstage/issues/new?title=Documentation%20feedback%3A%20Section%201.1%C2%B6&body=%23%23%20Documentation%20Feedback%20%F0%9F%93%9D%0A%0A%20%23%23%23%23%20The%20highlighted%20text%3A%20%0A%0A%20%3E%20Section%201.1%C2%B6%0A%0A%20%23%23%23%23%20The%20comment%20on%20the%20text%3A%20%0A%20_%3Ereplace%20this%20line%20with%20your%20comment%3C_%0A%0A%20___%0ABackstage%20URL%3A%20%3Chttp%3A%2F%2Flocalhost%3A7007%2Fcatalog%2Fdefault%2Fcomponent%2Ftechdocs-e2e-fixture%2Fdocs%2Fsub-page-one%2F%3E%20%0AMarkdown%20URL%3A%20%3Chttps%3A%2F%2Fgithub.com%2Fbackstage%2Fbackstage%2Fblob%2Fmaster%2Fcypress%2Ffixtures%2Fdocs%2Fsub-page-one.md%3E', + ); + }); }); }); diff --git a/cypress/src/integration/plugins/techdocs.spec.ts b/cypress/src/integration/plugins/techdocs.spec.ts index 1e69ba46ad..31094cb128 100644 --- a/cypress/src/integration/plugins/techdocs.spec.ts +++ b/cypress/src/integration/plugins/techdocs.spec.ts @@ -99,7 +99,7 @@ describe('TechDocs', () => { }); describe('Rendering TechDocs Addons', () => { - it('should render a content addon', () => { + it('should render a content addon in homepage', () => { cy.visit('/docs/default/Component/techdocs-e2e-fixture'); cy.contains('e2e Fixture Documentation'); @@ -130,6 +130,43 @@ describe('TechDocs', () => { 'https://github.com/backstage/backstage/issues/new?title=Documentation%20feedback%3A%20This%20is%20a%20basic%20documentation%20used%20for%20end-to-end%20tests.&body=%23%23%20Documentation%20Feedback%20%F0%9F%93%9D%0A%0A%20%23%23%23%23%20The%20highlighted%20text%3A%20%0A%0A%20%3E%20This%20is%20a%20basic%20documentation%20used%20for%20end-to-end%20tests.%0A%0A%20%23%23%23%23%20The%20comment%20on%20the%20text%3A%20%0A%20_%3Ereplace%20this%20line%20with%20your%20comment%3C_%0A%0A%20___%0ABackstage%20URL%3A%20%3Chttp%3A%2F%2Flocalhost%3A7007%2Fdocs%2Fdefault%2FComponent%2Ftechdocs-e2e-fixture%3E%20%0AMarkdown%20URL%3A%20%3Chttps%3A%2F%2Fgithub.com%2Fbackstage%2Fbackstage%2Fblob%2Fmaster%2Fcypress%2Ffixtures%2Fdocs%2Findex.md%3E', ); }); + + it('should render a content addon in sub-pages', () => { + cy.visit('/docs/default/Component/techdocs-e2e-fixture'); + + cy.contains('e2e Fixture Documentation'); + + // open sub-page + cy.getTechDocsShadowRoot().within(() => { + cy.getTechDocsNavigation().find('a').contains('Sub-page 1').click(); + }); + + // highlight a snippet of text + cy.getTechDocsShadowRoot() + .find('#section-11') + .then($el => { + const el = $el[0]; + const document = el.ownerDocument; + const range = document.createRange(); + range.selectNodeContents(el); + document?.getSelection()?.removeAllRanges(); + document?.getSelection()?.addRange(range); + }); + + cy.document().trigger('selectionchange'); + + // wait for new issue default debounce time + cy.wait(600); + + // assert that the new issue button has a right url + cy.getTechDocsShadowRoot() + .contains('Open new Github issue') + .should( + 'have.attr', + 'href', + 'https://github.com/backstage/backstage/issues/new?title=Documentation%20feedback%3A%20Section%201.1%C2%B6&body=%23%23%20Documentation%20Feedback%20%F0%9F%93%9D%0A%0A%20%23%23%23%23%20The%20highlighted%20text%3A%20%0A%0A%20%3E%20Section%201.1%C2%B6%0A%0A%20%23%23%23%23%20The%20comment%20on%20the%20text%3A%20%0A%20_%3Ereplace%20this%20line%20with%20your%20comment%3C_%0A%0A%20___%0ABackstage%20URL%3A%20%3Chttp%3A%2F%2Flocalhost%3A7007%2Fdocs%2Fdefault%2FComponent%2Ftechdocs-e2e-fixture%2Fsub-page-one%2F%3E%20%0AMarkdown%20URL%3A%20%3Chttps%3A%2F%2Fgithub.com%2Fbackstage%2Fbackstage%2Fblob%2Fmaster%2Fcypress%2Ffixtures%2Fdocs%2Fsub-page-one.md%3E', + ); + }); }); describe('Navigating within TechDocs', () => { diff --git a/cypress/yarn.lock b/cypress/yarn.lock index c667cc6c03..0fc9da99b6 100644 --- a/cypress/yarn.lock +++ b/cypress/yarn.lock @@ -7,7 +7,7 @@ resolved "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== -"@cypress/request@^2.88.5": +"@cypress/request@^2.88.10": version "2.88.10" resolved "https://registry.npmjs.org/@cypress/request/-/request-2.88.10.tgz#b66d76b07f860d3a4b8d7a0604d020c662752cce" integrity sha512-Zp7F+R93N0yZyG34GutyTNr+okam7s/Fzc1+i3kcqOP8vk6OuajuE9qZJ6Rs+10/1JFtXFYMdyarnU1rZuJesg== @@ -40,19 +40,19 @@ lodash.once "^4.1.1" "@types/node@*": - version "17.0.25" - resolved "https://registry.npmjs.org/@types/node/-/node-17.0.25.tgz#527051f3c2f77aa52e5dc74e45a3da5fb2301448" - integrity sha512-wANk6fBrUwdpY4isjWrKTufkrXdu1D2YHCot2fD/DfWxF5sMrVSA+KN7ydckvaTCh0HiqX9IVl0L5/ZoXg5M7w== + version "18.0.0" + resolved "https://registry.npmjs.org/@types/node/-/node-18.0.0.tgz#67c7b724e1bcdd7a8821ce0d5ee184d3b4dd525a" + integrity sha512-cHlGmko4gWLVI27cGJntjs/Sj8th9aYwplmZFwmmgYQQvL5NUsgVJG7OddLvNfLqYS31KFN0s3qlaD9qCaxACA== "@types/node@^14.14.31": - version "14.18.13" - resolved "https://registry.npmjs.org/@types/node/-/node-14.18.13.tgz#6ad4d9db59e6b3faf98dcfe4ca9d2aec84443277" - integrity sha512-Z6/KzgyWOga3pJNS42A+zayjhPbf2zM3hegRQaOPnLOzEi86VV++6FLDWgR1LGrVCRufP/ph2daa3tEa5br1zA== + version "14.18.21" + resolved "https://registry.npmjs.org/@types/node/-/node-14.18.21.tgz#0155ee46f6be28b2ff0342ca1a9b9fd4468bef41" + integrity sha512-x5W9s+8P4XteaxT/jKF0PSb7XEvo5VmqEWgsMlyeY4ZlLK8I6aH6g5TPPyDlLAep+GYf4kefb7HFyc7PAO3m+Q== -"@types/sinonjs__fake-timers@^6.0.2": - version "6.0.4" - resolved "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.4.tgz#0ecc1b9259b76598ef01942f547904ce61a6a77d" - integrity sha512-IFQTJARgMUBF+xVd2b+hIgXWrZEjND3vJtRCvIelcFB5SIXfjV4bOHbHJ0eXKh+0COrBRc8MqteKAz/j88rE0A== +"@types/sinonjs__fake-timers@8.1.1": + version "8.1.1" + resolved "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz#b49c2c70150141a15e0fa7e79cf1f92a72934ce3" + integrity sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g== "@types/sizzle@^2.3.2": version "2.3.3" @@ -75,9 +75,9 @@ aggregate-error@^3.0.0: indent-string "^4.0.0" ansi-colors@^4.1.1: - version "4.1.1" - resolved "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" - integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== + version "4.1.3" + resolved "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b" + integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw== ansi-escapes@^4.3.0: version "4.3.2" @@ -113,7 +113,7 @@ asn1@~0.2.3: assert-plus@1.0.0, assert-plus@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" - integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= + integrity sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw== astral-regex@^2.0.0: version "2.0.0" @@ -121,14 +121,14 @@ astral-regex@^2.0.0: integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== async@^3.2.0: - version "3.2.3" - resolved "https://registry.npmjs.org/async/-/async-3.2.3.tgz#ac53dafd3f4720ee9e8a160628f18ea91df196c9" - integrity sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g== + version "3.2.4" + resolved "https://registry.npmjs.org/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c" + integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ== asynckit@^0.4.0: version "0.4.0" resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== at-least-node@^1.0.0: version "1.0.0" @@ -138,7 +138,7 @@ at-least-node@^1.0.0: aws-sign2@~0.7.0: version "0.7.0" resolved "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" - integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= + integrity sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA== aws4@^1.8.0: version "1.11.0" @@ -150,10 +150,15 @@ balanced-match@^1.0.0: resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +base64-js@^1.3.1: + version "1.5.1" + resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + bcrypt-pbkdf@^1.0.0: version "1.0.2" resolved "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" - integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= + integrity sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w== dependencies: tweetnacl "^0.14.3" @@ -178,7 +183,15 @@ brace-expansion@^1.1.7: buffer-crc32@~0.2.3: version "0.2.13" resolved "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" - integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= + integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ== + +buffer@^5.6.0: + version "5.7.1" + resolved "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" cachedir@^2.3.0: version "2.3.0" @@ -188,7 +201,7 @@ cachedir@^2.3.0: caseless@~0.12.0: version "0.12.0" resolved "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" - integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= + integrity sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw== chalk@^4.1.0: version "4.1.2" @@ -201,12 +214,12 @@ chalk@^4.1.0: check-more-types@^2.24.0: version "2.24.0" resolved "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz#1420ffb10fd444dcfc79b43891bbfffd32a84600" - integrity sha1-FCD/sQ/URNz8ebQ4kbv//TKoRgA= + integrity sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA== ci-info@^3.2.0: - version "3.3.0" - resolved "https://registry.npmjs.org/ci-info/-/ci-info-3.3.0.tgz#b4ed1fb6818dea4803a55c623041f9165d2066b2" - integrity sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw== + version "3.3.2" + resolved "https://registry.npmjs.org/ci-info/-/ci-info-3.3.2.tgz#6d2967ffa407466481c6c90b6e16b3098f080128" + integrity sha512-xmDt/QIAdeZ9+nfdPsaBCpMvHNLFiLdjj59qjqn+6iPe6YmHGQ35sBnQ8uslRBXFmXkiZQOJRjvQeoGppoTjjg== clean-stack@^2.0.0: version "2.2.0" @@ -220,7 +233,7 @@ cli-cursor@^3.1.0: dependencies: restore-cursor "^3.1.0" -cli-table3@~0.6.0: +cli-table3@~0.6.1: version "0.6.2" resolved "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.2.tgz#aaf5df9d8b5bf12634dc8b3040806a0c07120d2a" integrity sha512-QyavHCaIC80cMivimWu4aWHilIpiDpfm3hGmqAmXVL1UsnbLuBSMd21hTX6VY4ZSDSM73ESLeF8TOYId3rBTbw== @@ -250,9 +263,9 @@ color-name@~1.1.4: integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== colorette@^2.0.16: - version "2.0.16" - resolved "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz#713b9af84fdb000139f04546bd4a93f62a5085da" - integrity sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g== + version "2.0.19" + resolved "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz#cdf044f47ad41a0f4b56b3a0d5b4e6e1a2d5a798" + integrity sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ== combined-stream@^1.0.6, combined-stream@~1.0.6: version "1.0.8" @@ -274,12 +287,12 @@ common-tags@^1.8.0: concat-map@0.0.1: version "0.0.1" resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== core-util-is@1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" - integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= + integrity sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ== cross-spawn@^7.0.0: version "7.0.3" @@ -290,24 +303,25 @@ cross-spawn@^7.0.0: shebang-command "^2.0.0" which "^2.0.1" -cypress@^7.3.0: - version "7.7.0" - resolved "https://registry.npmjs.org/cypress/-/cypress-7.7.0.tgz#0839ae28e5520536f9667d6c9ae81496b3836e64" - integrity sha512-uYBYXNoI5ym0UxROwhQXWTi8JbUEjpC6l/bzoGZNxoKGsLrC1SDPgIDJMgLX/MeEdPL0UInXLDUWN/rSyZUCjQ== +cypress@^10.0.0: + version "10.1.0" + resolved "https://registry.npmjs.org/cypress/-/cypress-10.1.0.tgz#6514a26c721822a02bc194e9a7f72c3142aea174" + integrity sha512-aQ4JVZVib4Xd9FZW8IRZfKelUvqF4y5A+oUbNvn8TlsBmEfIg3m5Xd6Mt6PVU/jHiVJ9Psl905B3ZPnrDcmyuQ== dependencies: - "@cypress/request" "^2.88.5" + "@cypress/request" "^2.88.10" "@cypress/xvfb" "^1.2.4" "@types/node" "^14.14.31" - "@types/sinonjs__fake-timers" "^6.0.2" + "@types/sinonjs__fake-timers" "8.1.1" "@types/sizzle" "^2.3.2" arch "^2.2.0" blob-util "^2.0.2" bluebird "^3.7.2" + buffer "^5.6.0" cachedir "^2.3.0" chalk "^4.1.0" check-more-types "^2.24.0" cli-cursor "^3.1.0" - cli-table3 "~0.6.0" + cli-table3 "~0.6.1" commander "^5.1.0" common-tags "^1.8.0" dayjs "^1.10.4" @@ -326,28 +340,28 @@ cypress@^7.3.0: listr2 "^3.8.3" lodash "^4.17.21" log-symbols "^4.0.0" - minimist "^1.2.5" + minimist "^1.2.6" ospath "^1.2.2" pretty-bytes "^5.6.0" - ramda "~0.27.1" + proxy-from-env "1.0.0" request-progress "^3.0.0" + semver "^7.3.2" supports-color "^8.1.1" tmp "~0.2.1" untildify "^4.0.0" - url "^0.11.0" yauzl "^2.10.0" dashdash@^1.12.0: version "1.14.1" resolved "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" - integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= + integrity sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g== dependencies: assert-plus "^1.0.0" dayjs@^1.10.4: - version "1.11.1" - resolved "https://registry.npmjs.org/dayjs/-/dayjs-1.11.1.tgz#90b33a3dda3417258d48ad2771b415def6545eb0" - integrity sha512-ER7EjqVAMkRRsxNCC5YqJ9d9VQYuWdGt7aiH2qA5R5wt8ZmWaP2dLUSIK6y/kVzLMlmh1Tvu5xUf4M/wdGJ5KA== + version "1.11.3" + resolved "https://registry.npmjs.org/dayjs/-/dayjs-1.11.3.tgz#4754eb694a624057b9ad2224b67b15d552589258" + integrity sha512-xxwlswWOlGhzgQ4TKzASQkUhqERI3egRNqgV4ScR8wlANA/A9tZ7miXa44vTTKEq5l7vWoL5G57bG3zA+Kow0A== debug@^3.1.0: version "3.2.7" @@ -366,12 +380,12 @@ debug@^4.1.1, debug@^4.3.2: delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== ecc-jsbn@~0.1.1: version "0.1.2" resolved "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" - integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= + integrity sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw== dependencies: jsbn "~0.1.0" safer-buffer "^2.1.0" @@ -398,7 +412,7 @@ enquirer@^2.3.6: escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" - integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== eventemitter2@^6.4.3: version "6.4.5" @@ -446,7 +460,7 @@ extract-zip@2.0.1: extsprintf@1.3.0: version "1.3.0" resolved "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" - integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= + integrity sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g== extsprintf@^1.2.0: version "1.4.1" @@ -456,7 +470,7 @@ extsprintf@^1.2.0: fd-slicer@~1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" - integrity sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4= + integrity sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g== dependencies: pend "~1.2.0" @@ -470,7 +484,7 @@ figures@^3.2.0: forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" - integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= + integrity sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw== form-data@~2.3.2: version "2.3.3" @@ -494,7 +508,7 @@ fs-extra@^9.1.0: fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== get-stream@^5.0.0, get-stream@^5.1.0: version "5.2.0" @@ -513,19 +527,19 @@ getos@^3.2.1: getpass@^0.1.1: version "0.1.7" resolved "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" - integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= + integrity sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng== dependencies: assert-plus "^1.0.0" glob@^7.1.3: - version "7.2.0" - resolved "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" - integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== + version "7.2.3" + resolved "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" inherits "2" - minimatch "^3.0.4" + minimatch "^3.1.1" once "^1.3.0" path-is-absolute "^1.0.0" @@ -560,6 +574,11 @@ human-signals@^1.1.1: resolved "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== +ieee754@^1.1.13: + version "1.2.1" + resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + indent-string@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" @@ -568,7 +587,7 @@ indent-string@^4.0.0: inflight@^1.0.4: version "1.0.6" resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== dependencies: once "^1.3.0" wrappy "1" @@ -616,7 +635,7 @@ is-stream@^2.0.0: is-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" - integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= + integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA== is-unicode-supported@^0.1.0: version "0.1.0" @@ -626,17 +645,17 @@ is-unicode-supported@^0.1.0: isexe@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== isstream@~0.1.2: version "0.1.2" resolved "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" - integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= + integrity sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g== jsbn@~0.1.0: version "0.1.1" resolved "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" - integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= + integrity sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg== json-schema@0.4.0: version "0.4.0" @@ -646,7 +665,7 @@ json-schema@0.4.0: json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" - integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= + integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== jsonfile@^6.0.1: version "6.1.0" @@ -670,7 +689,7 @@ jsprim@^2.0.2: lazy-ass@^1.6.0: version "1.6.0" resolved "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz#7999655e8646c17f089fdd187d150d3324d54513" - integrity sha1-eZllXoZGwX8In90YfRUNMyTVRRM= + integrity sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw== listr2@^3.8.3: version "3.14.0" @@ -689,7 +708,7 @@ listr2@^3.8.3: lodash.once@^4.1.1: version "4.1.1" resolved "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" - integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w= + integrity sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg== lodash@^4.17.21: version "4.17.21" @@ -714,6 +733,13 @@ log-update@^4.0.0: slice-ansi "^4.0.0" wrap-ansi "^6.2.0" +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + merge-stream@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" @@ -736,14 +762,14 @@ mimic-fn@^2.1.0: resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== -minimatch@^3.0.4: +minimatch@^3.1.1: version "3.1.2" resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== dependencies: brace-expansion "^1.1.7" -minimist@^1.2.5: +minimist@^1.2.6: version "1.2.6" resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== @@ -768,7 +794,7 @@ npm-run-path@^4.0.0: once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== dependencies: wrappy "1" @@ -782,7 +808,7 @@ onetime@^5.1.0: ospath@^1.2.2: version "1.2.2" resolved "https://registry.npmjs.org/ospath/-/ospath-1.2.2.tgz#1276639774a3f8ef2572f7fe4280e0ea4550c07b" - integrity sha1-EnZjl3Sj+O8lcvf+QoDg6kVQwHs= + integrity sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA== p-map@^4.0.0: version "4.0.0" @@ -794,7 +820,7 @@ p-map@^4.0.0: path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== path-key@^3.0.0, path-key@^3.1.0: version "3.1.1" @@ -804,23 +830,28 @@ path-key@^3.0.0, path-key@^3.1.0: pend@~1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" - integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= + integrity sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg== performance-now@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" - integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= + integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow== pify@^2.2.0: version "2.3.0" resolved "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" - integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= + integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== pretty-bytes@^5.6.0: version "5.6.0" resolved "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb" integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg== +proxy-from-env@1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz#33c50398f70ea7eb96d21f7b817630a55791c7ee" + integrity sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A== + psl@^1.1.28: version "1.8.0" resolved "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" @@ -834,11 +865,6 @@ pump@^3.0.0: end-of-stream "^1.1.0" once "^1.3.1" -punycode@1.3.2: - version "1.3.2" - resolved "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" - integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0= - punycode@^2.1.1: version "2.1.1" resolved "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" @@ -849,20 +875,10 @@ qs@~6.5.2: resolved "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz#3aeeffc91967ef6e35c0e488ef46fb296ab76aad" integrity sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA== -querystring@0.2.0: - version "0.2.0" - resolved "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" - integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= - -ramda@~0.27.1: - version "0.27.2" - resolved "https://registry.npmjs.org/ramda/-/ramda-0.27.2.tgz#84463226f7f36dc33592f6f4ed6374c48306c3f1" - integrity sha512-SbiLPU40JuJniHexQSAgad32hfwd+DRUdwF2PlVuI5RZD0/vahUco7R8vD86J/tcEKKF9vZrUVwgtmGCqlCKyA== - request-progress@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/request-progress/-/request-progress-3.0.0.tgz#4ca754081c7fec63f505e4faa825aa06cd669dbe" - integrity sha1-TKdUCBx/7GP1BeT6qCWqBs1mnb4= + integrity sha512-MnWzEHHaxHO2iWiQuHrUPBi/1WeBf5PkxQqNyNvLl9VAYSdXkP8tQ3pBSeCPD+yw0v0Aq1zosWLz0BdeXpWwZg== dependencies: throttleit "^1.0.0" @@ -903,6 +919,13 @@ safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== +semver@^7.3.2: + version "7.3.7" + resolved "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" + integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== + dependencies: + lru-cache "^6.0.0" + shebang-command@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" @@ -991,12 +1014,12 @@ supports-color@^8.1.1: throttleit@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/throttleit/-/throttleit-1.0.0.tgz#9e785836daf46743145a5984b6268d828528ac6c" - integrity sha1-nnhYNtr0Z0MUWlmEtiaNgoUorGw= + integrity sha512-rkTVqu6IjfQ/6+uNuuc3sZek4CEYxTJom3IktzgdSxcZqdARuebbA/f4QmAxMQIxqq9ZLEUkSYqvuk1I6VKq4g== through@^2.3.8: version "2.3.8" resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" - integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= + integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== tmp@~0.2.1: version "0.2.1" @@ -1021,14 +1044,14 @@ tslib@^2.1.0: tunnel-agent@^0.6.0: version "0.6.0" resolved "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" - integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= + integrity sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w== dependencies: safe-buffer "^5.0.1" tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" resolved "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" - integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= + integrity sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA== type-fest@^0.21.3: version "0.21.3" @@ -1036,9 +1059,9 @@ type-fest@^0.21.3: integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== typescript@^4.1.3: - version "4.6.4" - resolved "https://registry.npmjs.org/typescript/-/typescript-4.6.4.tgz#caa78bbc3a59e6a5c510d35703f6a09877ce45e9" - integrity sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg== + version "4.7.4" + resolved "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz#1a88596d1cf47d59507a1bcdfb5b9dfe4d488235" + integrity sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ== universalify@^2.0.0: version "2.0.0" @@ -1050,14 +1073,6 @@ untildify@^4.0.0: resolved "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz#2bc947b953652487e4600949fb091e3ae8cd919b" integrity sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw== -url@^0.11.0: - version "0.11.0" - resolved "https://registry.npmjs.org/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" - integrity sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE= - dependencies: - punycode "1.3.2" - querystring "0.2.0" - uuid@^8.3.2: version "8.3.2" resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" @@ -1066,7 +1081,7 @@ uuid@^8.3.2: verror@1.10.0: version "1.10.0" resolved "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" - integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= + integrity sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw== dependencies: assert-plus "^1.0.0" core-util-is "1.0.2" @@ -1100,12 +1115,17 @@ wrap-ansi@^7.0.0: wrappy@1: version "1.0.2" resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== yauzl@^2.10.0: version "2.10.0" resolved "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" - integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk= + integrity sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g== dependencies: buffer-crc32 "~0.2.3" fd-slicer "~1.1.0" diff --git a/docs/assets/permission/disabled-unregister-entity.png b/docs/assets/permissions/disabled-unregister-entity.png similarity index 100% rename from docs/assets/permission/disabled-unregister-entity.png rename to docs/assets/permissions/disabled-unregister-entity.png diff --git a/docs/assets/permission/permission-framework-overview.drawio.svg b/docs/assets/permissions/permission-framework-overview.drawio.svg similarity index 100% rename from docs/assets/permission/permission-framework-overview.drawio.svg rename to docs/assets/permissions/permission-framework-overview.drawio.svg diff --git a/docs/assets/permission/permission-todo-list-page.png b/docs/assets/permissions/permission-todo-list-page.png similarity index 100% rename from docs/assets/permission/permission-todo-list-page.png rename to docs/assets/permissions/permission-todo-list-page.png diff --git a/docs/assets/search/architecture.drawio.svg b/docs/assets/search/architecture.drawio.svg index 709a008b43..b85541b804 100644 --- a/docs/assets/search/architecture.drawio.svg +++ b/docs/assets/search/architecture.drawio.svg @@ -1,17 +1,36 @@ - + + + + + +
+
+
+
+ +
+
+
+
+
+
+
+ +
+
- + -
+
@@ -21,7 +40,7 @@
- + @@ -103,11 +122,11 @@ - + -
+
@backstage/plugin-xyz @@ -115,7 +134,7 @@
- + @backstage/plugin-xyz @@ -251,10 +270,7 @@ - - - - + @@ -274,11 +290,14 @@ - + + + + -
+
@@ -288,18 +307,18 @@
- + Search API - - + + -
+
@@ -309,7 +328,7 @@
- + Components @@ -824,11 +843,11 @@ - + -
+
@@ -844,16 +863,19 @@
- + Individual frontend... - + + + + -
+
@@ -863,20 +885,79 @@
- + Search Cont... - - - - - - - - - + + + + + + + + + + + + + +
+
+
+ @backstage/plugin-search-react +
+
+
+
+ + @backstage/plugin-search-react + +
+
+ + + + + + + + +
+
+
+ + Components + +
+
+
+
+ + Components + +
+
+ + + + +
+
+
+ + Search Client + +
+
+
+
+ + Search Client + +
+
diff --git a/docs/assets/software-catalog/software-model-core-entities.drawio.svg b/docs/assets/software-catalog/software-model-core-entities.drawio.svg old mode 100644 new mode 100755 index 2260e5502e..1a8f63f9b2 --- a/docs/assets/software-catalog/software-model-core-entities.drawio.svg +++ b/docs/assets/software-catalog/software-model-core-entities.drawio.svg @@ -1,3 +1,4 @@ + -
dependsOn
dependsOn
Resource
(e.g. SQL Database, S3 bucket, ...)
Resource...
consumesAPI
consumesAPI
API
(e.g. OpenAPI, gRPC API, Avro, Dataset, ...)
API...
providesAPI
providesAPI
Component
(e.g. backend service, data pipeline ...)
Component...
Viewer does not support full SVG 1.1
\ No newline at end of file +
dependsOn
dependsOn
Resource
(e.g. SQL Database, S3 bucket, ...)
Resource...
consumesAPI
consumesAPI
API
(e.g. OpenAPI, gRPC API, Avro, Dataset, ...)
API...
providesAPI
providesAPI
Component
(e.g. backend service, data pipeline ...)
Component...
dependsOn
dependsOn
Text is not SVG - cannot display
\ No newline at end of file diff --git a/docs/assets/software-catalog/software-model-entities.drawio.svg b/docs/assets/software-catalog/software-model-entities.drawio.svg old mode 100644 new mode 100755 index 7b8b88f224..e14cc93e62 --- a/docs/assets/software-catalog/software-model-entities.drawio.svg +++ b/docs/assets/software-catalog/software-model-entities.drawio.svg @@ -1,3 +1,4 @@ + -
Domain
Domain
partOf
partOf
System
System
dependsOn
dependsOn
partOf
partOf
Resource
(e.g. SQL Database, S3 bucket, ...)
Resource...
consumesAPI
consumesAPI
API
(e.g. OpenAPI, gRPC API, Avro, Dataset, ...)
API...
providesAPI
providesAPI
partOf
partOf
Component
(e.g. backend service, data pipeline ...)
Component...
partOf
partOf
Viewer does not support full SVG 1.1
\ No newline at end of file +
Domain
Domain
partOf
partOf
System
System
dependsOn
dependsOn
partOf
partOf
Resource
(e.g. SQL Database, S3 bucket, ...)
Resource...
consumesAPI
consumesAPI
API
(e.g. OpenAPI, gRPC API, Avro, Dataset, ...)
API...
providesAPI
providesAPI
partOf
partOf
Component
(e.g. backend service, data pipeline ...)
Component...
partOf
partOf
dependsOn
dependsOn
Text is not SVG - cannot display
\ No newline at end of file diff --git a/docs/auth/add-auth-provider.md b/docs/auth/add-auth-provider.md index bd2bcd1694..eb8973bec1 100644 --- a/docs/auth/add-auth-provider.md +++ b/docs/auth/add-auth-provider.md @@ -1,9 +1,113 @@ --- id: add-auth-provider -title: Adding authentication providers -description: Documentation on Adding authentication providers +title: Contributing New Providers +description: Documentation on adding new authentication providers --- +> NOTE: The primary audience for this documentation are contributors to the main +> Backstage project that want to add support for new authentication providers. +> While you can follow it to implement your own custom providers it is much +> more advanced than using our built-in providers. + +## How Does Authentication Work? + +The Backstage application can use various external authentication providers for +authentication. An external provider is wrapped using an +`AuthProviderRouteHandlers` interface for handling authentication. This +interface consists of four methods. Each of these methods is hosted at an +endpoint (by default) `/api/auth/[provider]/method`, where `method` performs a +certain operation as follows: + +``` + /auth/[provider]/start -> Initiate a login from the web page + /auth/[provider]/handler/frame -> Handle a finished authentication operation + /auth/[provider]/refresh -> Refresh the validity of a login + /auth/[provider]/logout -> Log out a logged-in user +``` + +The flow is as follows: + +1. A user attempts to sign in. +2. A popup window is opened, pointing to the `auth` endpoint. That endpoint does + initial preparations and then re-directs the user to an external + authenticator, still inside the popup. +3. The authenticator validates the user and returns the result of the validation + (success OR failure), to the wrapper's endpoint (`handler/frame`). +4. The `handler/frame` rendered webpage will issue the appropriate response to + the webpage that opened the popup window, and the popup is closed. +5. The user signs out by clicking on a UI interface and the webpage makes a + request to logout the user. + +## Implementing Your Own Auth Wrapper + +The core interface of any auth wrapper is the `AuthProviderRouteHandlers` +interface. This interface has four methods corresponding to the API described in +the initial section. Any auth wrapper will have to implement this interface. + +When initiating a login, a pop-up window is created by the frontend, to allow +the user to initiate a login. This login request is done to the `/start` +endpoint which is handled by the `start` method. + +The `start` method re-directs to the external auth provider who authenticates +the request and re-directs the request to the `/frame/handler` endpoint, which +is handled by the `frameHandler` method. + +The `frameHandler` returns an HTML response, containing a script that does a +`postMessage` to the frontend window, containing the result of the request. +The `WebMessageResponse` type is the message sent by the `postMessage` to the +frontend. + +A `postMessageResponse` utility function wraps the logic of generating a +`postMessage` response that ensures that CORS is successfully handled. This +function takes an `express.Response`, a `WebMessageResponse` and the URL of the +frontend (`appOrigin`) as parameters and return an HTML page with the script and +the message. + +There is a helper class for [OAuth2](https://oauth.net/2/) based authentication providers, [OAuthAdapter](../reference/plugin-auth-backend.oauthadapter.md). This class implements the `AuthProviderRouteHandlers` interface +for you, and instead requires you to implement [OAuthHandlers](../reference/plugin-auth-backend.oauthhandlers.md), which +is significantly easier. + +### Auth Environment Separation + +The concept of an `env` is core to the way the auth backend works. It uses an +`env` query parameter to identify the environment in which the application is +running (`development`, `staging`, `production`, etc). Each runtime can +simultaneously support multiple environments at the same time and the right +handler for each request is identified and dispatched to, based on the `env` +parameter. + +`OAuthEnvironmentHandler` is a utility wrapper for an `OAuthHandlers` that +implements the `AuthProviderRouteHandlers` interface while supporting multiple +`env`s. + +To instantiate OAuth providers (the same but for different environments), use +`OAuthEnvironmentHandler.mapConfig`. It's a helper to iterate over a +configuration object that is a map of environments to configurations. See one of +the existing OAuth providers for an example of how it is used. + +Given the following configuration: + +```yaml +development: + clientId: abc + clientSecret: secret +production: + clientId: xyz + clientSecret: supersecret +``` + +The `OAuthEnvironmentHandler.mapConfig(config, envConfig => ...)` call will +split the config by the top level `development` and `production` keys, and pass +on each block as `envConfig`. + +For convenience, the `AuthProviderFactory` is a factory function that has to be +implemented which can then generate a `AuthProviderRouteHandlers` for a given +provider. + +All of the supported providers provide an `AuthProviderFactory` that returns an +`OAuthEnvironmentHandler`, capable of handling authentication for multiple +environments. + ## Passport We chose [Passport](http://www.passportjs.org/) as our authentication platform @@ -46,13 +150,13 @@ plugins/auth-backend/src/providers/providerA **`plugins/auth-backend/src/providers/providerA/provider.ts`** defines the provider class which implements a handler for the chosen framework. -#### Adding an OAuth based provider +### Adding an OAuth based provider If we're adding an `OAuth` based provider we would implement the -[OAuthProviderHandlers](#OAuthProviderHandlers) interface. By implementing this +`OAuthHandlers` interface. By implementing this interface we can use the `OAuthProvider` class provided by `lib/oauth`, meaning we don't need to implement the full -[AuthProviderRouteHandlers](#AuthProviderRouteHandlers) interface that providers +`AuthProviderRouteHandlers` interface that providers otherwise need to implement. The provider class takes the provider's options as a class parameter. It also @@ -65,7 +169,7 @@ export type ProviderAProviderOptions = OAuthProviderOptions & { // extra options here } -export class ProviderAAuthProvider implements OAuthProviderHandlers { +export class ProviderAAuthProvider implements OAuthHandlers { private readonly _strategy: ProviderAStrategy; constructor(options: ProviderAProviderOptions) { @@ -87,13 +191,10 @@ export class ProviderAAuthProvider implements OAuthProviderHandlers { } ``` -#### Adding an non-OAuth based provider - -_**Note**: We have prioritized OAuth-based providers and non-OAuth providers -should be considered experimental._ +### Adding an non-OAuth based provider An non-`OAuth` based provider could implement -[AuthProviderRouteHandlers](#AuthProviderRouteHandlers) instead. +`AuthProviderRouteHandlers` instead. ```ts type ProviderAOptions = { @@ -119,13 +220,19 @@ export class ProviderAAuthProvider implements AuthProviderRouteHandlers { } ``` -#### Factory function +#### Integration Wrapper -Each provider exports a factory function that instantiates the provider. The -factory should implement [AuthProviderFactory](#AuthProviderFactory), which +Each provider exports an object that provides a way to create new instances +of the provider, along with related utilities like predefined sign-in resolvers. + +The object is created using `createAuthProviderIntegration`, with the most +important part being the `create` method that acts as the factory function +for our provider. + +The factory should return an implementation of `AuthProviderFactory`, which passes in a object with utilities for configuration, logging, token issuing, etc. The factory should return an implementation of -[AuthProviderRouteHandlers](#AuthProviderRouteHandlers). +`AuthProviderRouteHandlers`. The factory is what decides the mapping from [static configuration](../conf/index.md) to the creation of auth providers. For @@ -133,48 +240,70 @@ example, OAuth providers use `OAuthEnvironmentHandler` to allow for multiple different configurations, one for each environment, which looks like this; ```ts -export const createOktaProvider: AuthProviderFactory = ({ - globalConfig, - config, - tokenIssuer, -}) => - OAuthEnvironmentHandler.mapConfig(config, envConfig => { - // read options from config - const clientId = envConfig.getString('clientId'); - const clientSecret = envConfig.getString('clientSecret'); +export const okta = createAuthProviderIntegration({ + create(options?: { + /** + * The profile transformation function used to verify and convert the auth response + * into the profile that will be presented to the user. + */ + authHandler?: AuthHandler; - // instantiate our OAuthProviderHandlers implementation - const provider = new OktaAuthProvider({ - audience, - clientId, - clientSecret, - callbackUrl, - }); + /** + * Configure sign-in for this provider, without it the provider can not be used to sign users in. + */ + signIn?: { + /** + * Maps an auth result to a Backstage identity for the user. + */ + resolver: SignInResolver; + }; + }) { + return ({ providerId, globalConfig, config, resolverContext }) => + OAuthEnvironmentHandler.mapConfig(config, envConfig => { + // read options from config + const clientId = envConfig.getString('clientId'); + const clientSecret = envConfig.getString('clientSecret'); - // Wrap the OAuthProviderHandlers with OAuthProvider, which implements AuthProviderRouteHandlers - return OAuthProvider.fromConfig(globalConfig, provider, { - providerId, - tokenIssuer, - }); - }); + // Use provided auth handler, or create a default one + const authHandler: AuthHandler = options?.authHandler + ? options.authHandler + : async ({ fullProfile, params }) => ({ + profile: makeProfileInfo(fullProfile, params.id_token), + }); + + // instantiate our OAuthHandlers implementation + const provider = new OktaAuthProvider({ + audience, + clientId, + clientSecret, + callbackUrl, + authHandler, + signInResolver: options?.signIn?.resolver, + resolverContext, + }); + + // Wrap the OAuthHandlers with OAuthProvider, which implements AuthProviderRouteHandlers + return OAuthProvider.fromConfig(globalConfig, provider, { + providerId, + tokenIssuer, + }); + }); + }, + resolvers: { + /** + * Looks up the user by matching their email local part to the entity name. + */ + emailLocalPartMatchingUserEntityName: () => commonByEmailLocalPartResolver, + + // ... additional predefined resolvers + }, +}); ``` The purpose of the different environments is to allow for a single auth-backend to serve as the authentication service for multiple different frontend environments, such as local development, staging, and production. -The factory function for other providers can be a lot simpler, as they might not -have configuration for each environment. Looking something like this: - -```ts -export const createProviderAProvider: AuthProviderFactory = ({ config }) => { - const a = config.getString('a'); - const b = config.getString('b'); - - return new ProviderAAuthProvider({ a, b }); -}; -``` - #### Verify Callback > Strategies require what is known as a verify callback. The purpose of a verify @@ -202,7 +331,7 @@ export { createProviderAProvider } from './provider'; **`plugins/auth-backend/src/providers/factories.ts`** When the `auth-backend` starts it sets up routing for all the available providers by calling -`createAuthProviderRouter` on each provider. You need to import the factory +the factory function of each provider. You need to import the factory function from the provider and add it to the factory: ```ts @@ -232,51 +361,3 @@ You can `curl -i localhost:7007/api/auth/providerA/start` and which should provide a `302` redirect with a `Location` header. Paste the URL from that header into a web browser and you should be able to trigger the authorization flow. - ---- - -##### OAuthProviderHandlers - -```ts -export interface OAuthProviderHandlers { - start( - req: express.Request, - options: Record, - ): Promise; - handler(req: express.Request): Promise<{ - response: AuthResponse; - refreshToken?: string; - }>; - refresh?( - refreshToken: string, - scope: string, - ): Promise>; - logout?(): Promise; -} -``` - -##### AuthProviderRouteHandlers - -```ts -export interface AuthProviderRouteHandlers { - start(req: express.Request, res: express.Response): Promise; - frameHandler(req: express.Request, res: express.Response): Promise; - refresh?(req: express.Request, res: express.Response): Promise; - logout(req: express.Request, res: express.Response): Promise; -} -``` - -##### AuthProviderFactory - -```ts -export type AuthProviderFactoryOptions = { - globalConfig: AuthProviderConfig; - config: Config; - logger: Logger; - tokenIssuer: TokenIssuer; -}; - -export type AuthProviderFactory = ( - options: AuthProviderFactoryOptions, -) => AuthProviderRouteHandlers; -``` diff --git a/docs/auth/auth-backend-classes.md b/docs/auth/auth-backend-classes.md deleted file mode 100644 index 5b5950f6c3..0000000000 --- a/docs/auth/auth-backend-classes.md +++ /dev/null @@ -1,206 +0,0 @@ ---- -id: auth-backend-classes -title: Auth backend classes -description: Documentation on Auth backend classes ---- - -## How Does Authentication Work? - -The Backstage application can use various external authentication providers for -authentication. An external provider is wrapped using an -`AuthProviderRouteHandlers` interface for handling authentication. This -interface consists of four methods. Each of these methods is hosted at an -endpoint (by default) `/api/auth/[provider]/method`, where `method` performs a -certain operation as follows: - -``` - /auth/[provider]/start -> Initiate a login from the web page - /auth/[provider]/handler/frame -> Handle a finished authentication operation - /auth/[provider]/refresh -> Refresh the validity of a login - /auth/[provider]/logout -> Log out a logged-in user -``` - -The flow is as follows: - -1. A user attempts to sign in. -2. A popup window is opened, pointing to the `auth` endpoint. That endpoint does - initial preparations and then re-directs the user to an external - authenticator, still inside the popup. -3. The authenticator validates the user and returns the result of the validation - (success OR failure), to the wrapper's endpoint (`handler/frame`). -4. The `handler/frame` rendered bยดwebpage will issue the appropriate response to - the webpage that opened the popup window, and the popup is closed. -5. The user signs out by clicking on a UI interface and the webpage makes a - request to logout the user. - -There are currently two different classes for two authentication mechanisms that -implement this interface: an `OAuthAdapter` for [OAuth](https://oauth.net/2/) -based mechanisms and a `SAMLAuthProvider` for -[SAML](http://docs.oasis-open.org/security/saml/Post2.0/sstc-saml-tech-overview-2.0.html). - -If you do not have an `OAuth2` or `SAML` based authentication provider, look in -the section [below](#implementing-your-own-auth-wrapper). - -### OAuth Mechanisms - -For more information on how these methods are used and for which purpose, refer -to the [OAuth documentation](oauth.md). - -Currently OAuth is assumed to be the de facto authentication mechanism for -Backstage based applications. - -Backstage comes with a "batteries-included" set of supported commonly used OAuth -providers: Okta, GitHub, Google, GitLab, and a generic OAuth2 provider. For a -list of available providers, look at the available wrappers in -`backstage/plugins/auth-backend/src/providers/`. - -All of these use the **authorization flow** of OAuth2 to implement -authentication. - -If your authentication provider is any of the above mentioned providers, you can -configure them by setting the right variables in `app-config.yaml` under the -`auth` section. - -### SAML - -The SAML Provider is currently under development. Additional validation and -profile handling is still required before use in production. - -To configure the SAML Auth provider, look at the configuration parameters -supported by -[Passport-SAML](https://github.com/node-saml/passport-saml#config-parameter-details) -under the `auth.providers.saml` key - -For security reasons, validate that the response from the IdP is indeed signed -by also providing the `cert` configuration. - -### Configuration - -Each authentication provider (except SAML) needs six parameters: an OAuth client -ID, a client secret, an authorization endpoint, a token endpoint, an optional -list of scopes (as a string separated by spaces) that may be required by the -OAuth2 Server to enable end-user sign-on, and an app origin. The app origin is -the URL at which the frontend of the application is hosted, and it is read from -the `app.baseUrl` config. This is required because the application opens a popup -window to perform the authentication, and once the flow is completed, the popup -window sends a `postMessage` to the frontend application to indicate the result -of the operation. Also this URL is used to verify that authentication requests -are coming from only this endpoint. - -These values are configured via the `app-config.yaml` present in the root of -your app folder. - -``` -auth: - providers: - google: - development: - clientId: ${AUTH_GOOGLE_CLIENT_ID} - clientSecret: ${AUTH_GOOGLE_CLIENT_SECRET} - github: - development: - clientId: ${AUTH_GITHUB_CLIENT_ID} - clientSecret: ${AUTH_GITHUB_CLIENT_SECRET} - enterpriseInstanceUrl: ${AUTH_GITHUB_ENTERPRISE_INSTANCE_URL} - gitlab: - development: - clientId: ${AUTH_GITLAB_CLIENT_ID} - oauth2: - development: - clientId: ${AUTH_OAUTH2_CLIENT_ID} - clientSecret: ${AUTH_OAUTH2_CLIENT_SECRET} - authorizationUrl: ${AUTH_OAUTH2_AUTH_URL} - tokenUrl: ${AUTH_OAUTH2_TOKEN_URL} - scope: ${AUTH_OAUTH2_SCOPE} - saml: - entryPoint: ${AUTH_SAML_ENTRY_POINT} - issuer: ${AUTH_SAML_ISSUER} - ... -``` - -## Implementing Your Own Auth Wrapper - -The core interface of any auth wrapper is the `AuthProviderRouteHandlers` -interface. This interface has four methods corresponding to the API described in -the initial section. Any auth wrapper will have to implement this interface. - -When initiating a login, a pop-up window is created by the frontend, to allow -the user to initiate a login. This login request is done to the `/start` -endpoint which is handled by the `start` method. - -The `start` method re-directs to the external auth provider who authenticates -the request and re-directs the request to the `/frame/handler` endpoint, which -is handled by the `frameHandler` method. - -The `frameHandler` returns an HTML response, containing a script that does a -`postMessage` to the frontend's window, containing the result of the request. -The `WebMessageResponse` type is the message sent by the `postMessage` to the -frontend. - -A `postMessageResponse` utility function wraps the logic of generating a -`postMessage` response that ensures that CORS is successfully handled. This -function takes an `express.Response`, a `WebMessageResponse` and the URL of the -frontend (`appOrigin`) as parameters and return an HTML page with the script and -the message. - -### OAuth Wrapping Interfaces. - -Each OAuth external provider is supported by a corresponding -[Passport](https://github.com/jaredhanson/passport) strategy. For a generic -OAuth2 provider, passport has a `passport-oauth2` strategy. The strategy class -handles the implementation details of working with each provider. - -Each strategy is wrapped by an `OAuthHandlers` interface. - -This interface cannot be directly used as an Express HTTP request handler. To do -so, `OAuthHandlers` are wrapped in an `OAuthAdapter`, which implements the -`AuthProviderRouterHandlers` interface. - -#### Env - -The concept of an `env` is core to the way the auth backend works. It uses an -`env` query parameter to identify the environment in which the application is -running (`development`, `staging`, `production`, etc). Each runtime can -simultaneously support multiple environments at the same time and the right -handler for each request is identified and dispatched to, based on the `env` -parameter. - -`OAuthEnvironmentHandler` is a utility wrapper for an `OAuthHandlers` that -implements the `AuthProviderRouteHandlers` interface while supporting multiple -`env`s. - -To instantiate OAuth providers (the same but for different environments), use -`OAuthEnvironmentHandler.mapConfig`. It's a helper to iterate over a -configuration object that is a map of environments to configurations. See one of -the existing OAuth providers for an example of how it is used. - -Given the following configuration: - -```yaml -development: - clientId: abc - clientSecret: secret -production: - clientId: xyz - clientSecret: supersecret -``` - -The `OAuthEnvironmentHandler.mapConfig(config, envConfig => ...)` call will -split the config by the top level `development` and `production` keys, and pass -on each block as `envConfig`. - -For convenience, the `AuthProviderFactory` is a factory function that has to be -implemented which can then generate a `AuthProviderRouteHandlers` for a given -provider. - -All of the supported providers provide an `AuthProviderFactory` that returns an -`OAuthEnvironmentHandler`, capable of handling authentication for multiple -environments. - -### OAuth2 Provider - -The `oauth2` provider abstracts a generic **OAuth2 + OIDC** based authentication -provider. What this means is that after the application has been given -permission by the user, the `authorization code` will be exchanged for an -`access_token`, a `refresh_token` and an `id_token`. This `id_token` is used to -obtain an email id of the user, which is then used for creating the session. diff --git a/docs/auth/identity-resolver.md b/docs/auth/identity-resolver.md index 686c9a9dab..b314954105 100644 --- a/docs/auth/identity-resolver.md +++ b/docs/auth/identity-resolver.md @@ -12,6 +12,37 @@ If you want to use an auth provider to sign in users, you need to explicitly con it have sign-in enabled and also tell it how the external identities should be mapped to user identities within Backstage. +## Quick Start + +> See [providers](../reference/plugin-auth-backend.providers.md) +> for a full list of auth providers and their built-in sign-in resolvers. + +Backstage projects created with `npx @backstage/create-app` come configured with a +sign-in resolver for GitHub guest access. This resolver makes all users share +a single "guest" identity and is only intended as a minimum requirement to quickly +get up and running. You can replace `github` for any of the other providers if you need. + +This resolver should not be used in production, as it uses a single shared identity, +and has no restrictions on who is able to sign-in. Be sure to read through the rest +of this page to understand the Backstage identity system once you need to install +a resolver for your production environment. + +The guest resolver can be useful for testing purposes too, and it looks like this: + +```ts +signIn: { + resolver(_, ctx) { + const userRef = 'user:default/guest' + return ctx.issueToken({ + claims: { + sub: userRef, + ent: [userRef], + }, + }), + }, +}, +``` + ## Backstage User Identity A user identity within Backstage is built up from two pieces of information, a @@ -59,6 +90,11 @@ the given auth provider, as well as a context object that contains various helpe for looking up users and issuing tokens. There are also a number of built-in sign-in resolvers that can be used, which are covered a bit further down. +Note that while it possible to configure multiple auth providers to be used for sign-in, +you should take care when doing so. It is best to make sure that the different auth +providers either do not have any user overlap, or that any users that are able to log +in with multiple providers always end up with the same Backstage identity. + ### Custom Resolver Example Let's look at an example of a custom sign-in resolver for the Google auth provider. @@ -210,6 +246,11 @@ is that it can be tricky to determine the ownership references, although it can be achieved for example through a lookup to an external service. You typically want to at least use the user itself as a lone ownership reference. +Because we no longer use the catalog as an allow-list of users, it is often important +that you limit what users are allowed to sign in. This could be a simple email domain +check like in the example below, or you might for example look up the GitHub organizations +that the user belongs to using the user access token in the provided result object. + ```ts import { DEFAULT_NAMESPACE, stringifyEntityRef, } from '@backstage/catalog-model'; @@ -220,8 +261,14 @@ async ({ profile }, ctx) => { 'Login failed, user profile does not contain an email', ); } - // We again use the local part of the email as the user name. - const [localPart] = profile.email.split('@'); + // Split the email into the local part and the domain. + const [localPart, domain] = profile.email.split('@'); + + // Next we verify the email domain. It is recommended to include this + // kind of check if you don't look up the user in an external service. + if (domain !== 'acme.org') { + throw new Error('Login failed, user email domain check failed'); + } // By using `stringifyEntityRef` we ensure that the reference is formatted correctly const userEntityRef = stringifyEntityRef({ diff --git a/docs/auth/index.md b/docs/auth/index.md index c34d244886..e713e722ef 100644 --- a/docs/auth/index.md +++ b/docs/auth/index.md @@ -185,6 +185,24 @@ const app = createApp({ When using multiple auth providers like this, it's important that you configure the different sign-in resolvers so that they resolve to the same identity regardless of the method used. +## Scaffolder Configuration (Software Templates) + +If you want to use the authentication capabilities of the [Repository Picker](../features/software-templates/writing-templates.md#the-repository-picker) inside your software templates you will need to configure the [`ScmAuthApi`](https://backstage.io/docs/reference/integration-react.scmauthapi) alongside your authentication provider. It is an API used to authenticate towards different SCM systems in a generic way, based on what resource is being accessed. + +To set it up, you'll need to add an API factory entry to `packages/app/src/apis.ts`. The example below sets up the `ScmAuthApi` for an already configured GitLab authentication provider: + +```ts +createApiFactory({ + api: scmAuthApiRef, + deps: { + gitlabAuthApi: gitlabAuthApiRef, + }, + factory: ({ gitlabAuthApi }) => ScmAuth.forGitlab(gitlabAuthApi), +}); +``` + +In case you are using a custom authentication providers, you might need to add a [custom `ScmAuthApi` implementation](./index.md#custom-scmauthapi-implementation). + ## For Plugin Developers The Backstage frontend core APIs provide a set of Utility APIs for plugin developers diff --git a/docs/auth/oauth2-proxy/provider.md b/docs/auth/oauth2-proxy/provider.md index 966596e268..ca3c40f5db 100644 --- a/docs/auth/oauth2-proxy/provider.md +++ b/docs/auth/oauth2-proxy/provider.md @@ -6,7 +6,7 @@ description: Adding OAuth2Proxy as an authentication provider in Backstage --- The Backstage `@backstage/plugin-auth-backend` package comes with an -`oauth2proxy` authentication provider that can authenticate users by using a +`oauth2Proxy` authentication provider that can authenticate users by using a [oauth2-proxy](https://github.com/oauth2-proxy/oauth2-proxy) in front of an actual Backstage instance. This enables to reuse existing authentications within a cluster. In general the `oauth2-proxy` supports all OpenID Connect providers, @@ -21,17 +21,17 @@ The provider configuration can be added to your `app-config.yaml` under the root ```yaml auth: providers: - oauth2proxy: {} + oauth2Proxy: {} ``` Right now no configuration options are supported, but the empty object is needed to enable the provider in the auth backend. -To use the `oauth2proxy` provider you must also configure it with a sign-in resolver. +To use the `oauth2Proxy` provider you must also configure it with a sign-in resolver. For more information about the sign-in process in general, see the [Sign-in Identities and Resolvers](../identity-resolver.md) documentation. -For the `oauth2proxy` provider, the sign-in result is quite different than other providers. +For the `oauth2Proxy` provider, the sign-in result is quite different than other providers. Because it's a proxy provider that can be configured to forward information through arbitrary headers, the auth result simply just gives you access to the HTTP headers of the incoming request. Using these you can either extract the information directly, @@ -40,19 +40,22 @@ or grab ID or access tokens to look up additional information and/or validate th A simple sign-in resolver might for example look like this: ```ts -providers.oauth2Proxy.create({ - signIn: { - async resolver({ result }, ctx) { - const name = result.getHeader('x-forwarded-user'); - if (!name) { - throw new Error('Request did not contain a user') - } - return ctx.signInWithCatalogUser({ - entityRef: { name }, - }); +providerFactories: { + ...defaultAuthProviderFactories, + oauth2Proxy: providers.oauth2Proxy.create({ + signIn: { + async resolver({ result }, ctx) { + const name = result.getHeader('x-forwarded-user'); + if (!name) { + throw new Error('Request did not contain a user') + } + return ctx.signInWithCatalogUser({ + entityRef: { name }, + }); + }, }, - }, -}), + }), +}, ``` ## Adding the provider to the Backstage frontend @@ -65,7 +68,7 @@ installed in `packages/app/src/App.tsx` like this: const app = createApp({ components: { -+ SignInPage: props => , ++ SignInPage: props => , ``` -See the [Sign-In with Proxy Providers](../index.md#sign-in-with-proxy-providers) section for more information. +See [Sign-In with Proxy Providers](../index.md#sign-in-with-proxy-providers) for pointers on how to set up the sign-in page to also work smoothly for local development. diff --git a/docs/deployment/helm.md b/docs/deployment/helm.md deleted file mode 100644 index cbfcb1bc57..0000000000 --- a/docs/deployment/helm.md +++ /dev/null @@ -1,62 +0,0 @@ ---- -id: helm -title: Deploying with Helm -description: How to deploy Backstage with Helm and Kubernetes -sidebar_label: Helm ---- - -An example Backstage app can be deployed in Kubernetes using the -[Backstage Helm charts](https://github.com/backstage/backstage/tree/master/contrib/chart/backstage). - -First, choose a DNS name where Backstage will be hosted, and create a YAML file -for your custom configuration. - -```yaml -appConfig: - app: - baseUrl: https://backstage.mydomain.com - title: Backstage - backend: - baseUrl: https://backstage.mydomain.com - cors: - origin: https://backstage.mydomain.com - lighthouse: - baseUrl: https://backstage.mydomain.com/lighthouse-api - techdocs: - storageUrl: https://backstage.mydomain.com/api/techdocs/static/docs - requestUrl: https://backstage.mydomain.com/api/techdocs -``` - -Then use it to run: - -```bash -git clone https://github.com/backstage/backstage.git -cd backstage/contrib/chart/backstage -helm dependency update -helm install -f backstage-mydomain.yaml backstage . -``` - -This command will deploy the following pieces: - -- Backstage frontend -- Backstage backend with scaffolder and auth plugins -- (optional) a PostgreSQL instance -- lighthouse plugin -- ingress - -After a few minutes Backstage should be up and running in your cluster under the -DNS specified earlier. - -Make sure to create the appropriate DNS entry in your infrastructure. To find -the public IP address run: - -```bash -$ kubectl get ingress -NAME HOSTS ADDRESS PORTS AGE -backstage-ingress * 123.1.2.3 80 17m -``` - -> **NOTE**: this is not a production ready deployment. - -For more information on how to customize the deployment check the -[README](https://github.com/backstage/backstage/tree/master/contrib/chart/backstage/README.md). diff --git a/docs/deployment/index.md b/docs/deployment/index.md index 621870ea6f..da2c29ba23 100644 --- a/docs/deployment/index.md +++ b/docs/deployment/index.md @@ -29,9 +29,7 @@ This method is covered in [Building a Docker image](docker.md) and There is also an example of deploying on [Heroku](heroku.md), which only requires the first two steps. -An example of deploying Backstage with a [Helm chart](helm.md), a common pattern -in AWS, is also available. There is also a contrib guide to deploying Backstage -with +There is also a contrib guide to deploying Backstage with [AWS Fargate and Aurora PostgreSQL](https://github.com/backstage/backstage/blob/master/contrib/docs/tutorials/aws-fargate-deployment.md). Please consider contributing other deployment guides if you get Backstage set up diff --git a/docs/features/kubernetes/installation.md b/docs/features/kubernetes/installation.md index ec0355c7d8..3b0895dd23 100644 --- a/docs/features/kubernetes/installation.md +++ b/docs/features/kubernetes/installation.md @@ -33,10 +33,14 @@ const serviceEntityPage = ( {/* other tabs... */} - + ``` +**Notes:** + +- The optional `refreshIntervalMs` property on the `EntityKubernetesContent` defines the interval in which the content automatically refreshes, if not set this will default to 10 seconds. + That's it! But now, we need the Kubernetes Backend plugin for the frontend to work. diff --git a/docs/features/search/README.md b/docs/features/search/README.md index a151da1736..f1744d4d80 100644 --- a/docs/features/search/README.md +++ b/docs/features/search/README.md @@ -10,76 +10,23 @@ description: Backstage Search lets you find the right information you are lookin ## What is it? -Backstage Search lets you find the right information you are looking for in the -Backstage ecosystem. +Backstage Search lets you find the right information you are looking for in the Backstage ecosystem. ## Features -- A federated, faceted search, searching across all entities registered in your - Backstage instance. +- A federated, faceted search, searching across all entities registered in your Backstage instance. - A search that lets you plug in your own search engine of choice. -- A standardized search API where you can choose to index other plugins data. +- A standardized search API where you can choose to index data from other plugins. ## Project roadmap -| Version | Description | -| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| Backstage Search Pre-Alpha โœ… | Search Frontend letting you search through the entities of the software catalog. [See Pre-Alpha Use Cases.](#backstage-search-pre-alpha) | -| Backstage Search Alpha โœ… | Basic โ€œout-of-the-boxโ€ in-memory indexing process of entities, and their metadata, registered to the Software Catalog. [See Alpha Use Cases](#backstage-search-alpha). | -| Backstage Search Beta โœ… | At least one production-ready search engine that supports the same use-cases as in the alpha. [See Beta Use Cases](#backstage-search-beta). | -| [Backstage Search 1.0 โŒ›] | A stable Search API for plugin developers to add search to their plugins, and app integrators to expose that to their users. [See 1.0 Use Cases](#backstage-search-1.0). | +### Now -## Use Cases +**Backstage Search 1.0** -#### Backstage Search Pre-Alpha - -The pre-alpha is intended to solve for the following user stories, but will get -there by means of a front-end only, non-extensible MVP. - -- As a software engineer I should be able to navigate to a search page and - search for entities registered in the Software Catalog. -- As a software engineer I should be able to use the search input field in the - sidebar to search for entities registered in the Software Catalog. -- As a software engineer I should be able to see the number of results my search - returned. -- As a software engineer I should be able to filter on metadata (kind, - lifecycle) when Iโ€™ve performed a search. -- As a software engineer I should be able to hide the filters if I donโ€™t need to - use them. - -#### Backstage Search Alpha - -We will consider Backstage Search to be in alpha when the above use-cases are -met, but built on top of a flexible, extensible platform. - -- As an integrator, I should be able to provide all of the pre-alpha experiences - to my users if I choose, but also be able to customize the experience using a - composable set of components. -- As a plugin developer, I should have a standard way to expose my plugin's data - to Backstage Search. -- As an integrator, I should still be able to expose everything in the Software - Catalog in search, but it should be possible to customize what is searchable. -- As an integrator, although I should be able to customize all of the above, it - should be possible to have the pre-alpha user experiences covered without - having to set up and configure a search engine. - -#### Backstage Search Beta - -We will consider Backstage Search to be in a beta phase when the above use-cases -are met, and can be deployed using a production-ready search engine. - -- As an integrator, I should be able to power my Backstage Search experience - (including querying and indexing) using a production-ready search engine like - ElasticSearch. -- As an integrator, I should be able to configure the connection to my search - engine in `app_config.yaml`. -- As an integrator, I should be able to tune the queries sent to my chosen - search engine according to my organization's needs, but a sensible default - query should be in place so that I am not required to do so. - -#### Backstage Search 1.0 +A stable Search API for plugin developers to add search to their plugins, and app integrators to expose that to their users. We will consider Backstage Search to be 1.0 when the above use-cases are met, and an ecosystem of search-enabled plugins are available and @@ -92,9 +39,23 @@ stable. how to customize and extend search in my Backstage instance to meet my organization's needs. -more to come... +### Next -## Search Engines Supported +_Not specified_ + +### Someday/Maybe + +_Not specified_ + +### Done + +See [Done](#done) below for a list of completed roadmap items. + +## Supported + +The following sections show the search engines and plugins currently supported by Backstage Search. + +### Search engines See [Backstage Search Architecture](architecture.md) to get an overview of how the search engines are used. @@ -108,11 +69,11 @@ the search engines are used. [Reach out to us](#get-involved) if you want to chat about support for more search engines. -## Plugins Integrated with Search +### Plugins integrated with Backstage Search | Plugin | Support Status | | ------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------- | -| Catalog | โœ… | +| Software Catalog | โœ… | | [TechDocs](./how-to-guides.md#how-to-index-techdocs-documents) | โœ… | | [Stack Overflow](https://github.com/backstage/backstage/blob/master/plugins/stack-overflow-backend/README.md#index-stack-overflow-questions-to-search) | โœ… | @@ -131,8 +92,62 @@ plugins integrated to search. | Backend Plugin Module | @backstage/plugin-search-backend-module-elasticsearch | | Backend Plugin Module | @backstage/plugin-search-backend-module-pg | -## Get Involved +## Get involved For any questions, feedback, or to help move search forward, reach out to us in -the `#search` channel of our +the **#search** channel of our [Discord chatroom](https://github.com/backstage/backstage#community). + +## Done + +**Backstage Search Pre-Alpha** + +Search Frontend letting you search through the entities of the software catalog. + +The pre-alpha is intended to solve for the following user stories, but will get +there by means of a front-end only, non-extensible MVP. + +- As a software engineer I should be able to navigate to a search page and + search for entities registered in the Software Catalog. +- As a software engineer I should be able to use the search input field in the + sidebar to search for entities registered in the Software Catalog. +- As a software engineer I should be able to see the number of results my search + returned. +- As a software engineer I should be able to filter on metadata (kind, + lifecycle) when Iโ€™ve performed a search. +- As a software engineer I should be able to hide the filters if I donโ€™t need to + use them. + +**Backstage Search Alpha** + +Basic โ€œout-of-the-boxโ€ in-memory indexing process of entities, and their metadata, registered to the Software Catalog. + +We will consider Backstage Search to be in alpha when the above use-cases are +met, but built on top of a flexible, extensible platform. + +- As an integrator, I should be able to provide all of the pre-alpha experiences + to my users if I choose, but also be able to customize the experience using a + composable set of components. +- As a plugin developer, I should have a standard way to expose my plugin's data + to Backstage Search. +- As an integrator, I should still be able to expose everything in the Software + Catalog in search, but it should be possible to customize what is searchable. +- As an integrator, although I should be able to customize all of the above, it + should be possible to have the pre-alpha user experiences covered without + having to set up and configure a search engine. + +**Backstage Search Beta** + +At least one production-ready search engine that supports the same use-cases as in the alpha. + +We will consider Backstage Search to be in a beta phase when the above use-cases +are met, and can be deployed using a production-ready search engine. + +- As an integrator, I should be able to power my Backstage Search experience + (including querying and indexing) using a production-ready search engine like + ElasticSearch. +- As an integrator, I should be able to configure the connection to my search + engine in **app_config.yaml**. +- As an integrator, I should be able to tune the queries sent to my chosen + search engine according to my organization's needs, but a sensible default + query should be in place so that I am not required to do so. diff --git a/docs/features/search/getting-started.md b/docs/features/search/getting-started.md index 14ad523740..d0e13eda55 100644 --- a/docs/features/search/getting-started.md +++ b/docs/features/search/getting-started.md @@ -18,7 +18,7 @@ If you haven't setup Backstage already, start ```bash # From your Backstage root directory -yarn add --cwd packages/app @backstage/plugin-search +yarn add --cwd packages/app @backstage/plugin-search @backstage/plugin-search-react ``` Create a new `packages/app/src/components/search/SearchPage.tsx` file in your @@ -33,7 +33,7 @@ import { SearchResult, DefaultResultListItem, SearchFilter, -} from '@backstage/plugin-search'; +} from '@backstage/plugin-search-react'; import { CatalogResultListItem } from '@backstage/plugin-catalog'; export const searchPage = ( @@ -213,7 +213,7 @@ apiRouter.use('/search', await search(searchEnv)); ### Frontend -The Search Plugin exposes several default filter types as static properties, +The Search Plugin web library (`@backstage/plugin-search-react`) exposes several default filter types as static properties, including `` and ``. These allow you to provide values relevant to your Backstage instance that, when selected, get passed to the backend. @@ -237,7 +237,7 @@ If you have advanced filter needs, you can specify your own filter component like this (although new core filter contributions are welcome): ```tsx -import { useSearch, SearchFilter } from '@backstage/plugin-search'; +import { useSearch, SearchFilter } from '@backstage/plugin-search-react'; const MyCustomFilter = () => { // Note: filters contain filter data from other filter components. Be sure diff --git a/docs/features/search/search-engines.md b/docs/features/search/search-engines.md index f1fd4bd62b..9bb8b8c343 100644 --- a/docs/features/search/search-engines.md +++ b/docs/features/search/search-engines.md @@ -102,6 +102,31 @@ import { Client } from '@elastic/elastic-search'; const client = searchEngine.newClient(options => new Client(options)); ``` +#### Set custom index template + +The elasticsearch engine gives you the ability to set a custom index template if needed. + +> Index templates define settings, mappings, and aliases that can be applied automatically to new indices. + +```typescript +// app/backend/src/plugins/search.ts +const searchEngine = await ElasticSearchSearchEngine.initialize({ + logger: env.logger, + config: env.config, +}); + +searchEngine.setIndexTemplate({ + name: '', + body: { + index_patterns: [''], + template: { + mappings: {}, + settings: {}, + }, + }, +}); +``` + ## Example configurations ### AWS diff --git a/docs/features/software-catalog/descriptor-format.md b/docs/features/software-catalog/descriptor-format.md index 86c832e3d2..81e3604ffe 100644 --- a/docs/features/software-catalog/descriptor-format.md +++ b/docs/features/software-catalog/descriptor-format.md @@ -55,6 +55,7 @@ metadata: - url: https://admin.example-org.com title: Admin Dashboard icon: dashboard + type: admin-dashboard spec: type: website lifecycle: production @@ -83,7 +84,8 @@ This is the same entity as returned in JSON from the software catalog API: { "url": "https://admin.example-org.com", "title": "Admin Dashboard", - "icon": "dashboard" + "icon": "dashboard", + "type": "admin-dashboard" } ], "tags": ["java"], @@ -353,6 +355,7 @@ Fields of a link are: | `url` | String | [Required] A `url` in a standard `uri` format (e.g. `https://example.com/some/page`) | | `title` | String | [Optional] A user friendly display name for the link. | | `icon` | String | [Optional] A key representing a visual icon to be displayed in the UI. | +| `type` | String | [Optional] An optional value to categorize links into specific groups. | _NOTE_: The `icon` field value is meant to be a semantic key that will map to a specific icon that may be provided by an icon library (e.g. `material-ui` @@ -362,6 +365,8 @@ Backstage integrator will ultimately be left to provide the appropriate icon component mappings. A generic fallback icon would be provided if a mapping cannot be resolved. +The semantics of the `type` field are undefined. The adopter is free to define their own set of types and utilize them as they wish. Some potential use cases can be to utilize the type field to validate certain links exist on entities or to create customized UI components for specific link types. + ## Common to All Kinds: Relations The `relations` root field is a read-only list of relations, between the current diff --git a/docs/features/software-catalog/system-model.md b/docs/features/software-catalog/system-model.md index eedb94d02e..8a7e0cd30a 100644 --- a/docs/features/software-catalog/system-model.md +++ b/docs/features/software-catalog/system-model.md @@ -33,8 +33,8 @@ tracked in source control, or use some existing open source or commercial software. A component can implement APIs for other components to consume. In turn it might -depend on APIs implemented by other components, or resources that are attached -to it at runtime. +consume APIs implemented by other components, or directly depend on components or +resources that are attached to it at runtime. ### API diff --git a/docs/features/software-catalog/well-known-annotations.md b/docs/features/software-catalog/well-known-annotations.md index 4fa71329dd..24464dd700 100644 --- a/docs/features/software-catalog/well-known-annotations.md +++ b/docs/features/software-catalog/well-known-annotations.md @@ -353,6 +353,19 @@ to `scm-only`, the plugin will only take into account files stored in source control (e.g. ignoring generated code). If set to `enabled`, all files covered by a coverage report will be taken into account. +### vault.io/secrets-path + +```yaml +# Example: +metadata: + annotations: + vault.io/secrets-path: test/backstage +``` + +The value of this annotation contains the path to the secrets of the entity in +Vault. If not present when the Vault plugin is in use, a message will be shown +instead, letting the user know what is missing in the `catalog-info.yaml`. + ## Deprecated Annotations The following annotations are deprecated, and only listed here to aid in diff --git a/docs/features/software-templates/writing-templates.md b/docs/features/software-templates/writing-templates.md index 0163877a8b..ff9962d8d4 100644 --- a/docs/features/software-templates/writing-templates.md +++ b/docs/features/software-templates/writing-templates.md @@ -397,6 +397,29 @@ There's also the ability to pass additional scopes when requesting the `oauth` token from the user, which you can do on a per-provider basis, in case your template can be published to multiple providers. +Note, that you will need to configure an [authentication provider](../../auth/index.md#configuring-authentication-providers), alongside the +[`ScmAuthApi`](../../auth/index.md#scaffolder-configuration-software-templates) for your source code management (SCM) service to make this feature work. + +### Accessing the signed-in users details + +Sometimes when authoring templates, you'll want to access the user that is running the template, and get details from the profile or the users `Entity` in the Catalog. + +If you have enabled a sign in provider and have a [sign in resolver](../../auth/identity-resolver.md) that points to a user in the Catalog, then you can use the `${{ user.entity }}` templating expression to access the raw entity from the Catalog. + +This can be particularly useful if you have processors setup in the Catalog to write `spec.profile.email` of the `User Entities` to reference them and pass them into actions like below: + +```yaml + steps: + action: publish:github + ... + input: + ... + gitAuthorName: ${{ user.entity.metadata.name }} + gitAuthorEmail: ${{ user.entity.spec.profile.email }} +``` + +You also have access to `user.entity.metadata.annotations` too, so if you have some other additional information stored in there, you reference those too. + ### The Owner Picker When the scaffolder needs to add new components to the catalog, it needs to have diff --git a/docs/features/techdocs/README.md b/docs/features/techdocs/README.md index 5ac275b8b4..761a247819 100644 --- a/docs/features/techdocs/README.md +++ b/docs/features/techdocs/README.md @@ -21,14 +21,44 @@ Today, it is one of the core products in Spotifyโ€™s developer experience offeri - Deploy TechDocs no matter how your software environment is set up. - Discover your Service's technical documentation from the Service's page in Backstage Catalog. - Create documentation-only sites for any purpose by just writing Markdown. +- Take advantage of the [TechDocs Addon Framework](addons.md) to add features on top of the base docs-like-code experience. - Explore and take advantage of the large ecosystem of [MkDocs plugins](https://www.mkdocs.org/user-guide/plugins/) to create a rich reading experience. - Search for and find docs. -## Platforms supported +## Project roadmap -See [TechDocs Architecture](architecture.md) to get an overview of where these -providers are used. +### Now + +With the Backstage 1.2 release, we have introduced the [TechDocs Addon Framework](https://backstage.io/blog/2022/05/13/techdocs-addon-framework) for augmenting the TechDocs experience at read-time. + +In addition to the framework itself, we have open sourced a **ReportIssue** Addon, helping you to create a feedback loop that drives up documentation quality and foster a documentation culture at your organization. + +### Next + +What can we do in TechDocs to help drive up documentation quality? We have many ideas, for example, a Trust Card with associated Trust Score and automatic triggering of documentation maintenance notifications. + +### Someday/Maybe + +- Contribute to and deploy from a marketplace of TechDocs Addons +- Addon: MDX (allows you to use JSX in your Markdown content) +- Can we go static site generator agnostic? +- Better integration with + [Scaffolder V2](https://github.com/backstage/backstage/issues/2771) (e.g. easy to choose and apply documentation template with Software Templates) +- Possible to configure several aspects about TechDocs (e.g. URL, homepage, + theme) + +### Done + +See [Done](#done) below for a list of completed roadmap items. + +## Supported + +The following sections show the source code hosting providers and file storage providers that are currently supported by TechDocs. + +See [TechDocs Architecture](architecture.md) to get an overview of where the below providers are used. + +### Source code hosting providers | Source Code Hosting Provider | Support Status | | ---------------------------- | -------------- | @@ -39,6 +69,8 @@ providers are used. | GitLab | Yes โœ… | | GitLab Enterprise | Yes โœ… | +### File storage providers + | File Storage Provider | Support Status | | --------------------------------- | -------------- | | Local Filesystem of Backstage app | Yes โœ… | @@ -47,72 +79,52 @@ providers are used. | Azure Blob Storage | Yes โœ… | | OpenStack Swift | Community โœ… | -[Reach out to us](#feedback) if you want to request more platforms. +[Reach out to us](#get-involved) if you want to request more providers. -## Project roadmap +## Tech stack -### **Published versions** +| Stack | Location | +| ----------------------------------------------- | ------------------------------------------------------------- | +| Frontend Plugin | [@backstage/plugin-techdocs][techdocs/frontend] | +| Frontend Plugin Library | [@backstage/plugin-techdocs-react][techdocs/frontend-library] | +| Backend Plugin | [@backstage/plugin-techdocs-backend][techdocs/backend] | +| CLI (for local development and generating docs) | [@techdocs/cli][techdocs/cli] | +| Docker Container (for generating docs) | [techdocs-container][techdocs/container] | + +[techdocs/frontend]: https://github.com/backstage/backstage/blob/master/plugins/techdocs +[techdocs/frontend-library]: https://github.com/backstage/backstage/blob/master/plugins/techdocs-react +[techdocs/backend]: https://github.com/backstage/backstage/blob/master/plugins/techdocs-backend +[techdocs/container]: https://github.com/backstage/techdocs-container +[techdocs/cli]: https://github.com/backstage/backstage/blob/master/packages/techdocs-cli + +## Get involved + +Reach out to us in the **#docs-like-code** channel of our +[Discord chatroom](https://github.com/backstage/backstage#community). + +## Done + +**Alpha release** -**Alpha release** โœ… - [Milestone](https://github.com/backstage/backstage/milestone/16) - Alpha of TechDocs that you can use end to end - and contribute to. -**Beta release** โœ… - +**Beta release** + [Milestone](https://github.com/backstage/backstage/milestone/29) - TechDocs' recommended setup supports most environments (CI systems, cloud storage solutions, source control systems). - [Instructions for upgrading from Alpha to Beta](how-to-guides.md#how-to-migrate-from-techdocs-alpha-to-beta) -**v1** โœ… +**v1.0** + +TechDocs promoted to v1.0! To understand how this change affects the package, check out our [versioning policy](https://backstage.io/docs/overview/versioning-policy). TechDocs packages: -- '@backstage/plugin-techdocs' -- '@backstage/plugin-techdocs-backend' -- '@backstage/plugin-techdocs-node' -- '@techdocs/cli' - -TechDocs promoted to v1.0! To understand how this change affects the package, please check out our [versioning policy](https://backstage.io/docs/overview/versioning-policy). - -**v1.2** ๐Ÿšง - -With the Backstage 1.2 release, we plan to introduce the [TechDocs Addon Framework](https://github.com/backstage/backstage/issues/9636) for augmenting the TechDocs experience at read-time. - -In addition to the framework itself, we'll be open sourcing a `` addon, helping you to create a feedback loop that drives up documentation quality and fosters a documentation culture at your organization. - -### **Next** - -- What can we do in TechDocs to drive up documentation quality? - -### **Someday/Maybe** - -- Contribute to and deploy from a marketplace of TechDocs Addons -- Addon: MDX (allows you to use JSX in your Markdown content) -- Can we go static site generator agnostic? -- Better integration with - [Scaffolder V2](https://github.com/backstage/backstage/issues/2771) (e.g. easy to choose and apply documentation template with Software Templates) -- Possible to configure several aspects about TechDocs (e.g. URL, homepage, - theme) - -## Tech stack - -| Stack | Location | -| ----------------------------------------------- | --------------------------------------------------------------- | -| Frontend Plugin | [`@backstage/plugin-techdocs`][techdocs/frontend] | -| Frontend Plugin Library | [`@backstage/plugin-techdocs-react`][techdocs/frontend-library] | -| Backend Plugin | [`@backstage/plugin-techdocs-backend`][techdocs/backend] | -| CLI (for local development and generating docs) | [`@techdocs/cli`][techdocs/cli] | -| Docker Container (for generating docs) | [`techdocs-container`][techdocs/container] | - -[techdocs/frontend]: https://github.com/backstage/backstage/blob/master/plugins/techdocs -[techdocs/frontend-library]: https://github.com/backstage/backstage/blob/master/plugins/techdocs-react -[techdocs/backend]: https://github.com/backstage/backstage/blob/master/plugins/techdocs-backend -[techdocs/container]: https://github.com/backstage/techdocs-container -[techdocs/cli]: https://github.com/backstage/techdocs-cli - -## Contact us - -Reach out to us in the `#docs-like-code` channel of our -[Discord chatroom](https://github.com/backstage/backstage#community). +- @backstage/plugin-techdocs +- @backstage/plugin-techdocs-backend +- @backstage/plugin-techdocs-node +- @techdocs/cli diff --git a/docs/getting-started/configuration.md b/docs/getting-started/configuration.md index fc78217fbc..59780d2a01 100644 --- a/docs/getting-started/configuration.md +++ b/docs/getting-started/configuration.md @@ -164,29 +164,29 @@ Open `packages/app/src/App.tsx` and below the last `import` line, add: ```typescript import { githubAuthApiRef } from '@backstage/core-plugin-api'; import { SignInProviderConfig, SignInPage } from '@backstage/core-components'; - -const githubProvider: SignInProviderConfig = { - id: 'github-auth-provider', - title: 'GitHub', - message: 'Sign in using GitHub', - apiRef: githubAuthApiRef, -}; ``` Search for `const app = createApp({` in this file, and below `apis,` add: ```typescript components: { - SignInPage: props => ( - - ), - }, + SignInPage: props => ( + + ), +}, ``` +> Since [v1.1.0](https://github.com/backstage/backstage/releases/tag/v1.1.0-next.3), you must provide an [explicit sign-in resolver](../auth/identity-resolver.md). + That should be it. You can stop your Backstage App. When you start it again and go to your Backstage portal in your browser, you should have your login prompt! diff --git a/docs/integrations/azure/discovery.md b/docs/integrations/azure/discovery.md index 9293b6d9d7..c607a6550e 100644 --- a/docs/integrations/azure/discovery.md +++ b/docs/integrations/azure/discovery.md @@ -6,25 +6,95 @@ sidebar_label: Discovery description: Automatically discovering catalog entities from repositories in an Azure DevOps organization --- -The Azure DevOps integration has a special discovery processor for discovering -catalog entities within an Azure DevOps. The processor will crawl the Azure +The Azure DevOps integration has a special entity provider for discovering +catalog entities within an Azure DevOps. The provider will crawl your Azure DevOps organization and register entities matching the configured path. This can be useful as an alternative to static locations or manually adding things to the catalog. +This guide explains how to install and configure the Azure DevOps Entity Provider (recommended) or the Azure DevOps Processor. + +## Dependencies + +### Code Search Feature + +Azure discovery is driven by the Code Search feature in Azure DevOps, this may not be enabled by default. For Azure +DevOps Services you can confirm this by looking at the installed extensions in your Organization Settings. For Azure +DevOps Server you'll find this information in your Collection Settings. + +If the Code Search extension is not listed then you can install it from the [Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=ms.vss-code-search&targetId=f9352dac-ba6e-434e-9241-a848a510ce3f&utm_source=vstsproduct&utm_medium=SearchExtStatus). + +### Azure Integration + +Setup [Azure integration](locations.md) with `host` and `token`. Host must be `dev.azure.com` for Cloud users, otherwise set this to your on-premise hostname. + ## Installation -You will have to add the processors in the catalog initialization code of your -backend. They are not installed by default, therefore you have to add a -dependency to `@backstage/plugin-catalog-backend-module-azure` to your backend -package. +At your configuration, you add one or more provider configs: + +```yaml +# app-config.yaml +catalog: + providers: + azureDevOps: + yourProviderId: # identifies your dataset / provider independent of config changes + organization: myorg + project: myproject + repository: service-* # this will match all repos starting with service-* + path: /catalog-info.yaml + anotherProviderId: # another identifier + organization: myorg + project: myproject + repository: '*' # this will match all repos starting with service-* + path: /src/*/catalog-info.yaml # this will search for files deep inside the /src folder + yetAotherProviderId: # guess, what? Another one :) + host: selfhostedazure.yourcompany.com + organization: myorg + project: myproject +``` + +The parameters available are: + +- `host:` Leave empty for Cloud hosted, otherwise set to your self-hosted instance host. +- `organization:` Your Organization slug (or Collection for on-premise users). Required. +- `project:` Your project slug. Required. +- `repository:` The repository name. Wildcards are supported as show on the examples above. If not set, all repositories will be searched. +- `path:` Where to find catalog-info.yaml files. Defaults to /catalog-info.yaml. + +_Note:_ the path parameter follows the same rules as the search on Azure DevOps +web interface. For more details visit the +[official search documentation](https://docs.microsoft.com/en-us/azure/devops/project/search/get-started-search?view=azure-devops). + +As this provider is not one of the default providers, you will first need to install +the Azure catalog plugin: ```bash # From your Backstage root directory yarn add --cwd packages/backend @backstage/plugin-catalog-backend-module-azure ``` -And then add the processors to your catalog builder: +Once you've done that, you'll also need to add the segment below to `packages/backend/src/plugins/catalog.ts`: + +```diff +/* packages/backend/src/plugins/catalog.ts */ ++import { AzureDevOpsEntityProvider } from '@backstage/plugin-catalog-backend-module-azure'; + +const builder = await CatalogBuilder.create(env); +/** ... other processors and/or providers ... */ ++builder.addEntityProvider( ++ AzureDevOpsEntityProvider.fromConfig(env.config, { ++ logger: env.logger, ++ schedule: env.scheduler.createScheduledTaskRunner({ ++ frequency: Duration.fromObject({ minutes: 30 }), ++ timeout: Duration.fromObject({ minutes: 3 }), ++ }), ++ }), ++); +``` + +## Alternative Processor + +As an alternative to the entity provider `AzureDevOpsEntityProvider`, you can still use the `AzureDevopsDiscoveryProcessor`. ```diff // In packages/backend/src/plugins/catalog.ts @@ -37,12 +107,6 @@ And then add the processors to your catalog builder: + builder.addProcessor(AzureDevOpsDiscoveryProcessor.fromConfig(env.config, { logger: env.logger })); ``` -## Configuration - -To use the discovery processor, you'll need a Azure integration -[set up](locations.md) with a `AZURE_TOKEN`. Then you can add a location target -to the catalog configuration: - ```yaml catalog: locations: @@ -70,13 +134,3 @@ When using a custom pattern, the target is composed of five parts: - The path within each repository to find the catalog YAML file. This will usually be `/catalog-info.yaml`, `/src/*/catalog-info.yaml` or a similar variation for catalog files stored in the root directory of each repository. - -_Note:_ the path parameter follows the same rules as the search on Azure DevOps -web interface. For more details visit the -[official search documentation](https://docs.microsoft.com/en-us/azure/devops/project/search/get-started-search?view=azure-devops). - -Azure discovery is driven by the Code Search feature in Azure DevOps, this may not be enabled by default. For Azure -DevOps Services you can confirm this by looking at the installed extensions in your Organization Settings. For Azure -DevOps Server you'll find this information in your Collection Settings. - -If the Code Search extension is not listed then you can install it from the [Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=ms.vss-code-search&targetId=f9352dac-ba6e-434e-9241-a848a510ce3f&utm_source=vstsproduct&utm_medium=SearchExtStatus). diff --git a/docs/integrations/bitbucket/locations.md b/docs/integrations/bitbucket/locations.md index c0c6afc7a3..878030a29c 100644 --- a/docs/integrations/bitbucket/locations.md +++ b/docs/integrations/bitbucket/locations.md @@ -15,25 +15,7 @@ plugin. ## Bitbucket Cloud -```yaml -integrations: - bitbucketCloud: - - username: ${BITBUCKET_CLOUD_USERNAME} - appPassword: ${BITBUCKET_CLOUD_PASSWORD} -``` - -> Note: A public Bitbucket Cloud provider is added automatically at startup for -> convenience, so you only need to list it if you want to supply credentials. - -Directly under the `bitbucketCloud` key is a list of provider configurations, where -you can list the Bitbucket Cloud providers you want to fetch data from. -In the case of Bitbucket Cloud, you will have up to one entry. - -This one entry will have the following elements: - -- `username`: The Bitbucket Cloud username to use in API requests. If - neither a username nor token are supplied, anonymous access will be used. -- `appPassword`: The app password for the Bitbucket Cloud user. +Please see [the Bitbucket Cloud documentation](../bitbucketCloud/locations.md). ## Bitbucket Server diff --git a/docs/integrations/bitbucketCloud/discovery.md b/docs/integrations/bitbucketCloud/discovery.md new file mode 100644 index 0000000000..6609707868 --- /dev/null +++ b/docs/integrations/bitbucketCloud/discovery.md @@ -0,0 +1,95 @@ +--- +id: discovery +title: Bitbucket Cloud Discovery +sidebar_label: Discovery +# prettier-ignore +description: Automatically discovering catalog entities from repositories in Bitbucket Cloud +--- + +The Bitbucket Cloud integration has a special entity provider for discovering +catalog files located in [Bitbucket Cloud](https://bitbucket.org). +The provider will search your Bitbucket Cloud account and register catalog files matching the configured path +as Location entity and via following processing steps add all contained catalog entities. +This can be useful as an alternative to static locations or manually adding things to the catalog. + +## Installation + +You will have to add the entity provider in the catalog initialization code of your +backend. The provider is not installed by default, therefore you have to add a +dependency to `@backstage/plugin-catalog-backend-module-bitbucket-cloud` to your backend +package. + +```bash +# From your Backstage root directory +yarn add --cwd packages/backend @backstage/plugin-catalog-backend-module-bitbucket-cloud +``` + +And then add the entity provider to your catalog builder: + +```diff + // In packages/backend/src/plugins/catalog.ts ++ import { BitbucketCloudEntityProvider } from '@backstage/plugin-catalog-backend-module-bitbucket-cloud'; + + export default async function createPlugin( + env: PluginEnvironment, + ): Promise { + const builder = await CatalogBuilder.create(env); ++ builder.addEntityProvider( ++ BitbucketCloudEntityProvider.fromConfig(env.config, { ++ logger: env.logger, ++ schedule: env.scheduler.createScheduledTaskRunner({ ++ frequency: { minutes: 30 }, ++ timeout: { minutes: 3 }, ++ }), ++ }), ++ ); + + // [...] + } +``` + +## Configuration + +To use the entity provider, you'll need a [Bitbucket Cloud integration set up](locations.md). +Very likely a `username` and `appPassword` will be required +(you are restricted to public repositories and a very low rate limit otherwise). + +Additionally, you need to configure your entity provider instance(s): + +```yaml +# app-config.yaml + +catalog: + providers: + bitbucketCloud: + yourProviderId: # identifies your ingested dataset + catalogPath: /catalog-info.yaml # default value + filters: # optional + projectKey: '^apis-.*$' # optional; RegExp + repoSlug: '^service-.*$' # optional; RegExp + workspace: workspace-name +``` + +> **Note:** It is possible but certainly not recommended to skip the provider ID level. +> If you do so, `default` will be used as provider ID. + +- **catalogPath** _(optional)_: + Default: `/catalog-info.yaml`. + Path where to look for `catalog-info.yaml` files. + When started with `/`, it is an absolute path from the repo root. + It supports values as allowed by the `path` filter/modifier + [at Bitbucket Cloud's code search](https://confluence.atlassian.com/bitbucket/code-search-in-bitbucket-873876782.html#Search-Pathmodifier). +- **filters** _(optional)_: + - **projectKey** _(optional)_: + Regular expression used to filter results based on the project key. + - **repoSlug** _(optional)_: + Regular expression used to filter results based on the repo slug. +- **workspace**: + Name of your organization account/workspace. + If you want to add multiple workspaces, you need to add one provider config each. + +## Alternative + +_Deprecated!_ Please raise issues for use cases not covered by the entity provider. + +[You can use the `BitbucketDiscoveryProcessor`.](../bitbucket/discovery.md#bitbucket-cloud) diff --git a/docs/integrations/bitbucketCloud/locations.md b/docs/integrations/bitbucketCloud/locations.md new file mode 100644 index 0000000000..b886a49346 --- /dev/null +++ b/docs/integrations/bitbucketCloud/locations.md @@ -0,0 +1,36 @@ +--- +id: locations +title: Bitbucket Cloud Locations +sidebar_label: Locations +# prettier-ignore +description: Integrating source code stored in Bitbucket Cloud into the Backstage catalog +--- + +The Bitbucket Cloud integration supports loading catalog entities from [bitbucket.org](https://bitbucket.org). +Entities can be added to +[static catalog configuration](../../features/software-catalog/configuration.md), +or registered with the +[catalog-import](https://github.com/backstage/backstage/tree/master/plugins/catalog-import) +plugin. + +## Configuration + +```yaml +integrations: + bitbucketCloud: + - username: ${BITBUCKET_CLOUD_USERNAME} + appPassword: ${BITBUCKET_CLOUD_PASSWORD} +``` + +> Note: A public Bitbucket Cloud provider is added automatically at startup for +> convenience, so you only need to list it if you want to supply credentials. + +Directly under the `bitbucketCloud` key is a list of provider configurations, where +you can list the Bitbucket Cloud providers you want to fetch data from. +In the case of Bitbucket Cloud, you will have up to one entry. + +This one entry will have the following elements: + +- `username`: The Bitbucket Cloud username to use in API requests. If + neither a username nor token are supplied, anonymous access will be used. +- `appPassword`: The app password for the Bitbucket Cloud user. diff --git a/docs/integrations/gitlab/discovery.md b/docs/integrations/gitlab/discovery.md index 8d805fa362..ea151e9415 100644 --- a/docs/integrations/gitlab/discovery.md +++ b/docs/integrations/gitlab/discovery.md @@ -6,14 +6,55 @@ sidebar_label: Discovery description: Automatically discovering catalog entities from repositories in GitLab --- -The GitLab integration has a special discovery processor for discovering catalog -entities from GitLab. The processor will crawl the GitLab instance and register -entities matching the configured path. This can be useful as an alternative to +The GitLab integration has a special entity provider for discovering catalog +entities from GitLab. The entity provider will crawl the GitLab instance and register +entities matching the configured paths. This can be useful as an alternative to static locations or manually adding things to the catalog. -To use the discovery processor, you'll need a GitLab integration -[set up](locations.md) with a `token`. Then you can add a location target to the -catalog configuration: +To use the discovery provider, you'll need a GitLab integration +[set up](locations.md) with a `token`. Then you can add a provider config per group +to the catalog configuration: + +```yaml +catalog: + providers: + gitlab: + yourProviderId: + host: gitlab-host # Identifies one of the hosts set up in the integrations + branch: main # Optional. Uses `master` as default + group: example-group # Group and subgroup (if needed) to look for repositories + entityFilename: catalog-info.yaml # Optional. Defaults to `catalog-info.yaml` +``` + +As this provider is not one of the default providers, you will first need to install +the gitlab catalog plugin: + +```bash +# From the Backstage root directory +yarn add --cwd packages/backend @backstage/plugin-catalog-backend-module-gitlab +``` + +Once you've done that, you'll also need to add the segment below to `packages/backend/src/plugins/catalog.ts`: + +```ts +/* packages/backend/src/plugins/catalog.ts */ + +import { GitlabDiscoveryEntityProvider } from '@backstage/plugin-catalog-backend-module-gitlab'; + +const builder = await CatalogBuilder.create(env); +/** ... other processors and/or providers ... */ +builder.addEntityProvider( + ...GitlabDiscoveryEntityProvider.fromConfig(env.config, { + logger: env.logger, + schedule: env.scheduler.createScheduledTaskRunner({ + frequency: { minutes: 30 }, + timeout: { minutes: 3 }, + }), + }), +); +``` + +## Alternative processor ```yaml catalog: @@ -22,6 +63,9 @@ catalog: target: https://gitlab.com/group/subgroup/blob/main/catalog-info.yaml ``` +As alternative to the entity provider `GitlabDiscoveryEntityProvider` +you can still use the `GitLabDiscoveryProcessor`. + Note the `gitlab-discovery` type, as this is not a regular `url` processor. The target is composed of three parts: @@ -50,3 +94,5 @@ of your backend. + GitLabDiscoveryProcessor.fromConfig(env.config, { logger: env.logger }) + ); ``` + +If you don't want create location object if file with component definition do not exists in project, you can set the `skipReposWithoutExactFileMatch` option. That can reduce count of request to gitlab with 404 status code. diff --git a/docs/overview/roadmap.md b/docs/overview/roadmap.md index 106bf56f79..727000728f 100644 --- a/docs/overview/roadmap.md +++ b/docs/overview/roadmap.md @@ -12,12 +12,12 @@ and the broader Backstage community. The Backstage roadmap lays out both [โ€œwhat's nextโ€](#whats-next) and ["future work"](#future-work). With "next" we mean features planned for release within -the ongoing quarter from January through March 2022. With "future" we mean +the ongoing quarter from April through June 2022. With "future" we mean features on the radar, but not yet scheduled. -| [What's next](#whats-next) | [Future work](#future-work) | -| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- | -| [Ease of onboarding](#ease-of-onboarding)
[Search 1.0](#search-1.0)
[TechDocs Addon framework](#techdocs-addon-framework)
[Backend Services (initial)](#backend-services-initial)
[Backstage Security Audit](#backstage-security-audit)
[SIGs for contributors](#sigs-for-contributors) | Security Plan (and Strategy)
Composable Homepage 1.0
GraphQL
Telemetry
Improved UX design | +| [What's next](#whats-next) | [Future work](#future-work) | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- | +| [Ease of onboarding](#ease-of-onboarding)
[Backstage Search 1.0](#search-1.0)
[TechDocs Addon Framework](#techdocs-addon-framework)
[Backend Services (initial)](#backend-services-initial)
[Backstage Security Audit](#backstage-security-audit)
[SIGs for contributors](#sigs-for-contributors) | Security Plan (and Strategy)
Composable Homepage 1.0
GraphQL
Telemetry
Improved UX design | The long-term roadmap (12 - 36 months) is not detailed in the public roadmap. Third-party contributions are also not currently included in the roadmap. Let us @@ -46,18 +46,17 @@ More iterations will be required in the following quarters, but this will be a good improvement in the onboarding experience, especially for the benefit of new adopters. -### Search 1.0 +### Backstage Search 1.0 -Fix the few remaining issues to get Backstage Search platform up to 1.0 -([here](https://github.com/backstage/backstage/milestone/27) and -[here](https://github.com/backstage/backstage/milestone/28)). +Fix the few remaining issues to get Backstage Search platform up to 1.0. For more information, see the [Backstage Search documentation and roadmap page](https://backstage.io/docs/features/search/search-overview). -### TechDocs Addon framework +### TechDocs Addon Framework -Addons are TechDocs features that are added on top of the base docs-like-code -experience. An example would be a feature that showed comments on the page. We -plan to add an Addon framework and open source a selection of the Addons that we -use internally at Spotify. Further Addons can then be added by the Community. +Addons are TechDocs features that are added on top of the base docs-like-code experience. An example would be a feature that showed comments on the page. We plan to add an Addon framework and open source a selection of the Addons that we use internally at Spotify. We encourage the Backstage community to add further Addons. + +For more information about the TechDocs Addon Framework, see the documentation page [here](https://backstage.io/docs/features/techdocs/addons) + +For general information about TechDocs including roadmap, see [here](https://backstage.io/docs/features/techdocs/techdocs-overview). ### Backend Services (initial) diff --git a/docs/permission/concepts.md b/docs/permissions/concepts.md similarity index 100% rename from docs/permission/concepts.md rename to docs/permissions/concepts.md diff --git a/docs/permission/custom-rules.md b/docs/permissions/custom-rules.md similarity index 100% rename from docs/permission/custom-rules.md rename to docs/permissions/custom-rules.md diff --git a/docs/permission/getting-started.md b/docs/permissions/getting-started.md similarity index 99% rename from docs/permission/getting-started.md rename to docs/permissions/getting-started.md index b115b674d6..6daf448cdd 100644 --- a/docs/permission/getting-started.md +++ b/docs/permissions/getting-started.md @@ -152,6 +152,6 @@ permission: 3. Now that youโ€™ve made this change, you should find that the unregister entity menu option on the catalog entity page is disabled. -![Entity detail page showing disabled unregister entity context menu entry](../assets/permission/disabled-unregister-entity.png) +![Entity detail page showing disabled unregister entity context menu entry](../assets/permissions/disabled-unregister-entity.png) Now that the framework is fully configured, you can craft a permission policy that works best for your organization by utilizing a provided authorization method or by [writing your own policy](./writing-a-policy.md)! diff --git a/docs/permission/overview.md b/docs/permissions/overview.md similarity index 97% rename from docs/permission/overview.md rename to docs/permissions/overview.md index 8e87756faa..b972814aeb 100644 --- a/docs/permission/overview.md +++ b/docs/permissions/overview.md @@ -22,7 +22,7 @@ The permission framework was designed with a few key properties in mind: - **Integrators** can author or configure policies that define which users can take certain actions upon which resources. -![](../assets/permission/permission-framework-overview.drawio.svg) +![](../assets/permissions/permission-framework-overview.drawio.svg) 1. The user triggers a request to perform some action. The request specifies the authorization details using the permission specified by the plugin (in this case, a resource read action). diff --git a/docs/permission/plugin-authors/01-setup.md b/docs/permissions/plugin-authors/01-setup.md similarity index 98% rename from docs/permission/plugin-authors/01-setup.md rename to docs/permissions/plugin-authors/01-setup.md index 2f070f603b..d08d1ee79c 100644 --- a/docs/permission/plugin-authors/01-setup.md +++ b/docs/permissions/plugin-authors/01-setup.md @@ -105,7 +105,7 @@ The source code is available here: Now if you start your application you should be able to reach the `/todo-list` page: -![Todo List plugin page](../../assets/permission/permission-todo-list-page.png) +![Todo List plugin page](../../assets/permissions/permission-todo-list-page.png) --- diff --git a/docs/permission/plugin-authors/02-adding-a-basic-permission-check.md b/docs/permissions/plugin-authors/02-adding-a-basic-permission-check.md similarity index 100% rename from docs/permission/plugin-authors/02-adding-a-basic-permission-check.md rename to docs/permissions/plugin-authors/02-adding-a-basic-permission-check.md diff --git a/docs/permission/plugin-authors/03-adding-a-resource-permission-check.md b/docs/permissions/plugin-authors/03-adding-a-resource-permission-check.md similarity index 100% rename from docs/permission/plugin-authors/03-adding-a-resource-permission-check.md rename to docs/permissions/plugin-authors/03-adding-a-resource-permission-check.md diff --git a/docs/permission/plugin-authors/04-authorizing-access-to-paginated-data.md b/docs/permissions/plugin-authors/04-authorizing-access-to-paginated-data.md similarity index 100% rename from docs/permission/plugin-authors/04-authorizing-access-to-paginated-data.md rename to docs/permissions/plugin-authors/04-authorizing-access-to-paginated-data.md diff --git a/docs/permission/writing-a-policy.md b/docs/permissions/writing-a-policy.md similarity index 100% rename from docs/permission/writing-a-policy.md rename to docs/permissions/writing-a-policy.md diff --git a/docs/plugins/analytics.md b/docs/plugins/analytics.md index d5fd02a46d..ccb4e5c5a7 100644 --- a/docs/plugins/analytics.md +++ b/docs/plugins/analytics.md @@ -52,11 +52,12 @@ learn how to contribute the integration yourself! The following table summarizes events that, depending on the plugins you have installed, may be captured. -| Action | Provided By | Subject | -| ---------- | -------------- | --------------------------------------------------- | -| `navigate` | Backstage Core | The URL of the page that was navigated to | -| `click` | Backstage Core | The text of the link that was clicked on | -| `search` | Backstage Core | The search term entered in any search bar component | +| Action | Subject | Other Notes | +| ---------- | --------------------------------------------------- | ----------------------------------------------------------------- | +| `navigate` | The URL of the page that was navigated to | | +| `click` | The text of the link that was clicked on | The `to` attribute represents the URL clicked to | +| `search` | The search term entered in any search bar component | The `searchTypes` attribute holds `types` constraining the search | +| `discover` | The title of the search result that was clicked on | The `value` is the result rank. A `to` attribute is also provided | If there is an event you'd like to see captured, please [open an issue][add-event] describing the event you want to see and the questions it diff --git a/docs/plugins/integrating-search-into-plugins.md b/docs/plugins/integrating-search-into-plugins.md new file mode 100644 index 0000000000..77456dd5b4 --- /dev/null +++ b/docs/plugins/integrating-search-into-plugins.md @@ -0,0 +1,203 @@ +--- +id: integrating-search-into-plugins +title: Integrating Search into a plugin +description: How to integrate Search into a Backstage plugin +--- + +The Backstage Search Platform was designed to give plugin developers the APIs +and interfaces needed to offer search experiences within their plugins, while +abstracting away (and instead empowering application integrators to choose) the +specific underlying search technologies. + +On this page, you'll find concepts and tutorials for leveraging the Backstage +Search Platform in your plugin. + +## Providing data to the search platform + +> A guide on how to create collators is coming soon! + +## Building a search experience into your plugin + +While the core Search plugin offers components and extensions that empower app +integrators to compose a global search experience, you may find that you want a +narrower search experience just within your plugin. This could be as literal as +an autocomplete-style search bar focused on documents provided by your plugin +(for example, the [TechDocsSearch](https://github.com/backstage/backstage/blob/master/plugins/techdocs/src/search/components/TechDocsSearch.tsx) +component), or as abstract as a widget that presents a list of links that +are contextually related to something else on the page. + +### Search Experience Concepts + +Knowing these high-level concepts will help you as you craft your in-plugin +search experience. + +- All search experiences must be wrapped in a ``, which + is provided by `@backstage/plugin-search-react`. This context keeps track + of state necessary to perform search queries and display any results. As + inputs to the query are updated (e.g. a `term` or `filter` values), the + updated query is executed and `results` are refreshed. Check out the + [SearchContextValue](https://backstage.io/docs/reference/plugin-search-react.searchcontextvalue) + for details. +- The aforementioned state can be modified and/or consumed via the + `useSearch()` hook, also exported by `@backstage/plugin-search-react`. +- For more literal search experiences, reusable components are available + to import and compose into a cohesive experience in your plugin (e.g. + `` or ``). You can see all such + components in [Backstage's storybook](https://backstage.io/storybook/?path=/story/plugins-search-searchbar--default). + +### Search Experience Tutorials + +The following tutorials make use of packages and plugins that you may not yet +have as dependencies for your plugin; be sure to add them before you use them! + +- [`@backstage/plugin-search-react`](https://www.npmjs.com/package/@backstage/plugin-search-react) - A + package containing components, hooks, and types that are shared across all + frontend plugins, including plugins like yours! +- [`@backstage/plugin-search`](https://www.npmjs.com/package/@backstage/plugin-search) - The + main search plugin, used by app integrators to compose global search + experiences. +- [`@backstage/core-components`](https://www.npmjs.com/package/@backstage/core-components) - A + package containing generic components useful for a variety of experiences + built in Backstage. + +#### Improved "404" page experience + +Imagine you have a plugin that allows users to manage _widgets_. Perhaps they +can be viewed at a URL like `backstage.example.biz/widgets/{widgetName}`. +At some point, a widget is renamed, and links to that widget's page from +chat systems, wikis, or browser bookmarks become stale, resulting in errors or +404s. + +What if instead of showing a broken page or the generic "looks like someone +dropped the mic" 404 page, you showed a list of possibly related widgets? + +```javascript +import { Link } from '@backstage/core-components'; +import { SearchResult } from '@backstage/plugin-search'; +import { SearchContextProvider } from '@backstage/plugin-search-react'; + +export const Widget404Page = ({ widgetName }) => { + // Supplying this to runs a pre-filtered search with + // the given widgetName as the search term, focused on search result of type + // "widget" with no other filters. + const preFiltered = { + term: widgetName, + types: ['widget'], + filters: {}, + }; + + return ( + + {/* The component allows us to iterate through results and + display them in whatever way fits best! */} + + {({ results }) => ( + {results.map(({ document }) => ( + + {document.title} + + ))} + )} + + + ); +); +``` + +Not all search experiences require user input! As you can see, it's possible to +leverage the Backstage Search Platform's frontend framework without necessarily +giving users input controls. + +#### Simple search page + +Of course, it's also possible to provide a more fully featured search +experience in your plugin. The simplest way is to leverage reusable components +provided by the `@backstage/plugin-search` package, like this: + +```javascript +import { useProfile } from '@internal/api'; +import { + Content, + ContentHeader, + PageWithHeader, +} from '@backstage/core-components'; +import { SearchBar, SearchResult } from '@backstage/plugin-search'; +import { SearchContextProvider } from '@backstage/plugin-search-react'; + +export const ManageMyWidgets = () => { + const { primaryTeam } = useProfile(); + // In this example, note how we are pre-filtering results down to a specific + // owner field value (the currently logged-in user's team), but allowing the + // search term to be controlled by the user via the component. + const preFiltered = { + types: ['widget'], + term: '', + filters: { + owner: primaryTeam, + }, + }; + + return ( + + + + + + + {/* Render results here, just like above */} + + + + + ); +}; +``` + +#### Custom search control surfaces + +If the reusable search components provided by `@backstage/plugin-search` aren't +adequate, no problem! There's an API in place that you can use to author your +own components to control the various parts of the search context. + +```javascript +import { useSearch } from '@backstage/plugin-search-react'; +import ChipInput from 'material-ui-chip-input'; + +export const CustomChipFilter = ({ name }) => { + const { filters, setFilters } = useSearch(); + const chipValues = filters[name] || []; + + // When a chip value is changed, update the filters value by calling the + // setFilters function from the search context. + const handleChipChange = (chip, index) => { + // There may be filters set for other fields. Be sure to maintain them. + setFilters(prevState => { + const { [name]: filter = [], ...others } = prevState; + + if (index === undefined) { + filter.push(chip); + } else { + filter.splice(index, 1); + } + + return { ...others, [name]: filter }; + }); + }; + + return ( + + ); +}; +``` + +Check out the [SearchContextValue type](https://github.com/backstage/backstage/blob/master/plugins/search-react/src/context/SearchContext.tsx) +for more details on what methods and values are available for manipulating and +reading the search context. + +If you produce something generic and reusable, consider contributing your +component upstream so that all users of the Backstage Search Platform can +benefit. Issues and pull requests welcome. diff --git a/docs/plugins/url-reader.md b/docs/plugins/url-reader.md index 8a84f3e1ff..4c6938ade2 100644 --- a/docs/plugins/url-reader.md +++ b/docs/plugins/url-reader.md @@ -104,10 +104,10 @@ directly be used with a URL. Some example usages - - [`read`](https://github.com/backstage/backstage/blob/d5c83bb889b8142e343ebc4e4c0b90a02d1c1a3d/plugins/catalog-backend/src/ingestion/processors/codeowners/read.ts#L24-L33) - Catalog using the `read` method to read the CODEOWNERS file in a repository. -- [`readTree`](https://github.com/backstage/backstage/blob/d5c83bb889b8142e343ebc4e4c0b90a02d1c1a3d/packages/techdocs-common/src/helpers.ts#L198-L220) - +- [`readTree`](https://github.com/backstage/backstage/blob/84a8788/plugins/techdocs-node/src/helpers.ts#L146-L167) - TechDocs using the `readTree` method to download markdown files in order to generate the documentation site. -- [`readTree`](https://github.com/backstage/backstage/blob/d5c83bb889b8142e343ebc4e4c0b90a02d1c1a3d/packages/techdocs-common/src/stages/prepare/url.ts#L33-L54) - +- [`readTree`](https://github.com/backstage/backstage/blob/cb4f0e4/plugins/techdocs-node/src/stages/prepare/url.ts#L51-L73) - TechDocs using `NotModifiedError` to maintain cache and speed up and limit the number of requests. - [`search`](https://github.com/backstage/backstage/blob/d5c83bb889b8142e343ebc4e4c0b90a02d1c1a3d/plugins/catalog-backend/src/ingestion/processors/UrlReaderProcessor.ts#L88-L108) - diff --git a/docs/releases/v1.3.0-changelog.md b/docs/releases/v1.3.0-changelog.md new file mode 100644 index 0000000000..641fd8422a --- /dev/null +++ b/docs/releases/v1.3.0-changelog.md @@ -0,0 +1,2311 @@ +# Release v1.3.0 + +## @backstage/backend-common@0.14.0 + +### Minor Changes + +- 55647ec7df: **BREAKING**: Server-to-server tokens that are authenticated by the `ServerTokenManager` now must have an `exp` claim that has not expired. Tokens where the `exp` claim is in the past or missing are considered invalid and will throw an error. This is a followup to the deprecation from the `1.2` release of Backstage where perpetual tokens were deprecated. Be sure to update any usage of the `getToken()` method to have it be called every time a token is needed. Do not store tokens for later use. + +### Patch Changes + +- f72a6b8c62: Applied the `luxon` dependency fix from the `0.13.4` patch release. +- 5b22a8c97f: Applied the AWS S3 reading patch from the `0.13.5` patch release. +- f5283a42e2: Updated dependency `@google-cloud/storage` to `^6.0.0`. +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/integration@1.2.1 + - @backstage/config-loader@1.1.2 + +## @backstage/plugin-bitbucket-cloud-common@0.1.0 + +### Minor Changes + +- 1dffa7dd4d: Add new common library `bitbucket-cloud-common` with a client for Bitbucket Cloud. + + This client can be reused across all packages and might be the future place for additional + features like managing the rate limits, etc. + + The client itself was generated in parts using the `@openapitools/openapi-generator-cli`. + +### Patch Changes + +- 9122060776: Updated dependency `msw` to `^0.42.0`. +- Updated dependencies + - @backstage/integration@1.2.1 + +## @backstage/plugin-catalog@1.3.0 + +### Minor Changes + +- fe7614ea54: Add an optional icon to the Catalog and TechDocs search results + +### Patch Changes + +- 449dcef98e: Updates the `isKind`, `รฌsComponentType`, and `isNamespace` to allow an array of possible values + +- 1f70704580: Accessibility updates: + + - Added screen reader elements to describe default table `Action` buttons + +- 915700f64f: In order to simplify analytics on top of the search experience in Backstage, the provided `<*ResultListItem />` component now captures a `discover` analytics event instead of a `click` event. This event includes the result rank as its `value` and, like a click, the URL/path clicked to as its `to` attribute. + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/plugin-search-common@0.3.5 + - @backstage/plugin-search-react@0.2.1 + - @backstage/plugin-catalog-common@1.0.3 + - @backstage/core-components@0.9.5 + - @backstage/catalog-client@1.0.3 + - @backstage/core-plugin-api@1.0.3 + - @backstage/integration-react@1.1.1 + - @backstage/catalog-model@1.0.3 + +## @backstage/plugin-catalog-backend@1.2.0 + +### Minor Changes + +- b594679ae3: Allow array as non-spread arguments at the `CatalogBuilder`. + + ```typescript + builder.addEntityProvider(...getArrayOfProviders()); + ``` + + can be simplified to + + ```typescript + builder.addEntityProvider(getArrayOfProviders()); + ``` + +### Patch Changes + +- 8838b13038: Disallow anything but `'url'` locations from being registered via the location service. +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-search-common@0.3.5 + - @backstage/plugin-permission-node@0.6.2 + - @backstage/plugin-catalog-common@1.0.3 + - @backstage/backend-common@0.14.0 + - @backstage/integration@1.2.1 + - @backstage/catalog-client@1.0.3 + - @backstage/plugin-permission-common@0.6.2 + - @backstage/catalog-model@1.0.3 + - @backstage/plugin-scaffolder-common@1.1.1 + +## @backstage/plugin-catalog-backend-module-bitbucket@0.2.0 + +### Minor Changes + +- 1c01c0fd14: Integrate `@backstage/plugin-bitbucket-cloud-common` as replacement for the `BitbucketCloudClient`. + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-backend@1.2.0 + - @backstage/backend-common@0.14.0 + - @backstage/integration@1.2.1 + - @backstage/plugin-bitbucket-cloud-common@0.1.0 + - @backstage/catalog-model@1.0.3 + +## @backstage/plugin-catalog-backend-module-bitbucket-cloud@0.1.0 + +### Minor Changes + +- dfc4efcbf0: Add new plugin `catalog-backend-module-bitbucket-cloud` with `BitbucketCloudEntityProvider`. + + This entity provider is an alternative/replacement to the `BitbucketDiscoveryProcessor` **_(for Bitbucket Cloud only!)_**. + It replaces use cases using `search=true` and should be powerful enough as a complete replacement. + + If any feature for Bitbucket Cloud is missing and preventing you from switching, please raise an issue. + + **Before:** + + ```typescript + // packages/backend/src/plugins/catalog.ts + + builder.addProcessor( + BitbucketDiscoveryProcessor.fromConfig(env.config, { logger: env.logger }), + ); + ``` + + ```yaml + # app-config.yaml + + catalog: + locations: + - type: bitbucket-discovery + target: 'https://bitbucket.org/workspaces/workspace-name/projects/apis-*/repos/service-*?search=true&catalogPath=/catalog-info.yaml' + ``` + + **After:** + + ```typescript + // packages/backend/src/plugins/catalog.ts + builder.addEntityProvider( + BitbucketCloudEntityProvider.fromConfig(env.config, { + logger: env.logger, + schedule: env.scheduler.createScheduledTaskRunner({ + frequency: { minutes: 30 }, + timeout: { minutes: 3 }, + }), + }), + ); + ``` + + ```yaml + # app-config.yaml + + catalog: + providers: + bitbucketCloud: + yourProviderId: # identifies your ingested dataset + catalogPath: /catalog-info.yaml # default value + filters: # optional + projectKey: '^apis-.* + ``` + +## @backstage/plugin-catalog-backend-module-ldap@0.5.0 + +### Minor Changes + +- 1f83f0bc84: Added the possibility to pass TLS configuration to ldap connection + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.2.0 + - @backstage/backend-tasks@0.3.2 + - @backstage/catalog-model@1.0.3 + +## @backstage/plugin-dynatrace@0.1.0 + +### Minor Changes + +- 70027d09aa: Adds Dynatrace plugin + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## @backstage/plugin-github-pull-requests-board@0.1.0 + +### Minor Changes + +- fc9927c81d: Add Github Pull Requests board plugin + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/integration@1.2.1 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## @backstage/plugin-kubernetes-backend@0.6.0 + +### Minor Changes + +- 4328737af6: Add support to fetch data for Stateful Sets from Kubernetes + +### Patch Changes + +- 0c70cd8e1d: cache and refresh Azure tokens to avoid excessive calls to Azure Identity +- 2aedf64ad3: Updated dependency `@google-cloud/container` to `^4.0.0`. +- Updated dependencies + - @backstage/backend-common@0.14.0 + - @backstage/plugin-kubernetes-common@0.3.0 + - @backstage/catalog-model@1.0.3 + +## @backstage/plugin-kubernetes-common@0.3.0 + +### Minor Changes + +- 4328737af6: Add support to fetch data for Stateful Sets + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.0.3 + +## @backstage/plugin-pagerduty@0.4.0 + +### Minor Changes + +- b157c2eb1c: **Breaking**: Use identityApi to provide auth token for pagerduty API calls. + +### Patch Changes + +- 76bf6400fe: Fix alert that was not showing after creating an incident. +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## @backstage/plugin-scaffolder@1.3.0 + +### Minor Changes + +- dc39366bdb: - Added a new page under `/create/tasks` to show tasks that have been run by the Scaffolder. + + - Ability to filter these tasks by the signed in user, and all tasks. + - Added optional method to the `ScaffolderApi` interface called `listTasks` to get tasks with an required `filterByOwnership` parameter. + +- 86a4a0f72d: Get data of other fields in Form from a custom field in template Scaffolder. + following: + + ```tsx + const CustomFieldExtensionComponent = (props: FieldExtensionComponentProps) => { + const { formData } = props.formContext; + ... + }; + + const CustomFieldExtension = scaffolderPlugin.provide( + createScaffolderFieldExtension({ + name: ..., + component: CustomFieldExtensionComponent, + validation: ... + }) + ); + ``` + +- 72dfcbc8bf: Gerrit Integration: Implemented a `RepoUrlPicker` for Gerrit. + +- f93af969cd: Added the ability to support running of templates that are not in the `default` namespace + +### Patch Changes + +- ac0c7e45ee: Fixes review mask in `MultistepJsonForm` to work as documented. `show: true` no longer needed when mask is set. +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- fd505f40c0: Handle binary files and files that are too large during dry-run content upload. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/plugin-catalog-common@1.0.3 + - @backstage/core-components@0.9.5 + - @backstage/integration@1.2.1 + - @backstage/catalog-client@1.0.3 + - @backstage/core-plugin-api@1.0.3 + - @backstage/integration-react@1.1.1 + - @backstage/catalog-model@1.0.3 + - @backstage/plugin-permission-react@0.4.2 + - @backstage/plugin-scaffolder-common@1.1.1 + +## @backstage/plugin-scaffolder-backend@1.3.0 + +### Minor Changes + +- 35a26131b3: **DEPRECATION**: The `projectid` input parameters to the `publish:gitlab:merge-request`, it's no longer required as it can be decoded from the `repoUrl` input parameter. + **DEPRECATION**: The `projectid` output of the action in favour of `projectPath` + +- 72dfcbc8bf: A new scaffolder action has been added: `gerrit:publish` + +- ce0d8d7eb1: Fixed a bug in `publish:github` action that didn't permit to add users as collaborators. + This fix required changing the way parameters are passed to the action. + In order to add a team as collaborator, now you must use the `team` field instead of `username`. + In order to add a user as collaborator, you must use the `user` field. + + It's still possible to use the field `username` but is deprecated in favor of `team`. + + ```yaml + - id: publish + name: Publish + action: publish:github + input: + repoUrl: ... + collaborators: + - access: ... + team: my_team + - access: ... + user: my_username + ``` + +- 582003a059: - Added an optional `list` method on the `TaskBroker` and `TaskStore` interface to list tasks by an optional `userEntityRef` + + - Implemented a `list` method on the `DatabaseTaskStore` class to list tasks by an optional `userEntityRef` + - Added a route under `/v2/tasks` to list tasks by a `userEntityRef` using the `createdBy` query parameter + +- c042c5eaff: Add an option to not protect the default branch. + +- f93af969cd: Added the ability to support running of templates that are not in the `default` namespace + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- 6901f6be4a: Adds more of an explanation when the `publish:github` scaffolder action fails to create a repository. +- Updated dependencies + - @backstage/plugin-catalog-backend@1.2.0 + - @backstage/backend-common@0.14.0 + - @backstage/integration@1.2.1 + - @backstage/catalog-client@1.0.3 + - @backstage/catalog-model@1.0.3 + - @backstage/plugin-scaffolder-common@1.1.1 + +## @backstage/plugin-search@0.9.0 + +### Minor Changes + +- 2dc4818541: The pre-alpha ``, ``, `etc...` components have been removed. In the unlikely event you were still using/referencing them, please update to using their non-`*Next` equivalents from either `@backstage/plugin-search-react` or `@backstage/plugin-search`. + +### Patch Changes + +- 8809159148: Components ``, `` (including ``), `` (including `.Checkbox`, `.Select`, and `.Autocomplete` static prop components), ``, and `` are now exported from `@backstage/plugin-search-react`. They are now deprecated in `@backstage/plugin-search` and will be removed in a future release. +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- 5388e6bdc5: Fixed a bug that could cause analytics events in other parts of Backstage to capture nonsensical values resembling search modal state under some circumstances. +- 915700f64f: In order to simplify analytics on top of the search experience in Backstage, the provided `<*ResultListItem />` component now captures a `discover` analytics event instead of a `click` event. This event includes the result rank as its `value` and, like a click, the URL/path clicked to as its `to` attribute. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/plugin-search-common@0.3.5 + - @backstage/plugin-search-react@0.2.1 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## @backstage/plugin-techdocs@1.2.0 + +### Minor Changes + +- fe7614ea54: Add an optional icon to the Catalog and TechDocs search results + +### Patch Changes + +- d047d81295: Use entity title as label in `TechDocsReaderPageHeader` if available + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. + +- bff65e6958: Updated sidebar-related logic to use `` + `useSidebarPinState()` and/or `` + `useSidebarOpenState()` from `@backstage/core-components`. + +- 915700f64f: In order to simplify analytics on top of the search experience in Backstage, the provided `<*ResultListItem />` component now captures a `discover` analytics event instead of a `click` event. This event includes the result rank as its `value` and, like a click, the URL/path clicked to as its `to` attribute. + +- 881fbd7e8d: Fix `EntityTechdocsContent` component to use objects instead of `` elements, otherwise "outlet" will be null on sub-pages and add-ons won't render. + +- 17c059dfd0: Restructures reader style transformations to improve code readability: + + - Extracts the style rules to separate files; + - Creates a hook that processes each rule; + - And creates another hook that returns a transformer responsible for injecting them into the head tag of a given element. + +- 3b45ad701f: Packages a set of tweaks to the TechDocs addons rendering process: + + - Prevents displaying sidebars until page styles are loaded and the sidebar position is updated; + - Prevents new sidebar locations from being created every time the reader page is rendered if these locations already exist; + - Centers the styles loaded event to avoid having multiple locations setting the opacity style in Shadow Dom causing the screen to flash multiple times. + +- 9b94ade898: Use entity title in `TechDocsSearch` placeholder if available. + +- 816f7475ec: Convert `sanitizeDOM` transformer to hook as part of code readability improvements in dom file. + +- 50ff56a80f: Change the `EntityDocsPage` path to be more specific and also add integration tests for `sub-routes` on this page. + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/plugin-search-common@0.3.5 + - @backstage/plugin-search-react@0.2.1 + - @backstage/core-components@0.9.5 + - @backstage/integration@1.2.1 + - @backstage/core-plugin-api@1.0.3 + - @backstage/integration-react@1.1.1 + - @backstage/catalog-model@1.0.3 + - @backstage/plugin-techdocs-react@1.0.1 + +## @backstage/plugin-vault@0.1.0 + +### Minor Changes + +- 7c310a5bc2: First implementation of the frontend vault plugin. For more information refer to its `README.md`. + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## @backstage/plugin-vault-backend@0.1.0 + +### Minor Changes + +- 7c310a5bc2: First implementation for the backend vault plugin. For more information refer to its `README.md`. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-tasks@0.3.2 + - @backstage/backend-common@0.14.0 + - @backstage/backend-test-utils@0.1.25 + +## @backstage/app-defaults@1.0.3 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.9.5 + - @backstage/core-app-api@1.0.3 + - @backstage/core-plugin-api@1.0.3 + - @backstage/plugin-permission-react@0.4.2 + +## @backstage/backend-tasks@0.3.2 + +### Patch Changes + +- fde10d24f6: Allow tasks that fail to retry on a loop emitting a warning log every time it fails with the amount of attempts it has +- f7146b516f: Updated dependency `cron` to `^2.0.0`. + Updated dependency `@types/cron` to `^2.0.0`. +- 7f108513b8: Add error logging when a background task throws an error rather than silently swallowing it. +- Updated dependencies + - @backstage/backend-common@0.14.0 + +## @backstage/backend-test-utils@0.1.25 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/cli@0.17.2 + - @backstage/backend-common@0.14.0 + +## @backstage/catalog-client@1.0.3 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- 35bc0a7c27: Update README to point to catalog-react for frontend usage +- Updated dependencies + - @backstage/catalog-model@1.0.3 + +## @backstage/catalog-model@1.0.3 + +### Patch Changes + +- 131a99e909: Added targetRef to common.schema.json to match the Typescript type + +## @backstage/cli@0.17.2 + +### Patch Changes + +- 026cfe525a: Fix the public path configuration of the frontend app build so that a trailing `/` is always appended when needed. +- 4f73352608: Updated Lockfile to support new versions of yarn as well as the legacy 1 version +- b8970b8941: Improved the `create-github-app` permissions selection prompt by converting it into a multi-select with clearer descriptions. The `members` permission is now also included in the list which is required for ingesting user data into the catalog. +- bd58365d09: Updated dependency `run-script-webpack-plugin` to `^0.0.14`. +- 9002ebd76b: Updated dependency `@rollup/plugin-commonjs` to `^22.0.0`. +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- 1a33e8b287: Updated dependency `minimatch` to `5.1.0`. +- 6de866ea74: Added console warning to frontend start when the `app.baseUrl` and `backend.baseUrl` are identical +- Updated dependencies + - @backstage/config-loader@1.1.2 + - @backstage/release-manifests@0.0.4 + +## @backstage/config-loader@1.1.2 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. + +## @backstage/core-app-api@1.0.3 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- 19781483a2: Handle URLs as the first argument to `fetchApi`, when using the `plugin:` protocol +- Updated dependencies + - @backstage/core-plugin-api@1.0.3 + +## @backstage/core-components@0.9.5 + +### Patch Changes + +- feb4e8de07: Fix EntityPage tab scrolling overflow bug on Firefox + +- 65840b17be: Fix issue where right arrow icon was incorrectly added to side bar items without a sub-menu + +- de593ec78f: Updated dependency `react-text-truncate` to `^0.19.0`. + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. + +- 6968b65ba1: Updated dependency `@react-hookz/web` to `^14.0.0`. + +- ee2cd642c5: Updated dependency `rc-progress` to `3.3.3`. + +- 96d1e01641: Accessibility updates: + + - Added `aria-label` to the `Select` component + - Changed heading level used in the header of `Table` component + +- 7d355c4b3f: Fix the missing filter in the toolbar when passing a custom component in the core-components Table + +- 1cf9caecd6: fix Sidebar Contexts deprecation message + +- bff65e6958: The `SidebarPinStateContext` and `SidebarContext` have been deprecated and will be removed in a future release. Instead, use `` + `useSidebarPinState()` and/or `` + `useSidebarOpenState()`. + + This was done to ensure that sidebar state can be shared successfully across components exported by different packages, regardless of what version of this package is resolved and installed for each individual package. + +- Updated dependencies + - @backstage/core-plugin-api@1.0.3 + +## @backstage/core-plugin-api@1.0.3 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. + +## @backstage/create-app@0.4.28 + +### Patch Changes + +- 881fbd7e8d: Register `TechDocs` addons on catalog entity pages, follow the steps below to add them manually: + + ```diff + // packages/app/src/components/catalog/EntityPage.tsx + + + import { TechDocsAddons } from '@backstage/plugin-techdocs-react'; + + import { + + ReportIssue, + + } from '@backstage/plugin-techdocs-module-addons-contrib'; + + + const techdocsContent = ( + + + + + + + + + + + + ); + + const defaultEntityPage = ( + ... + + + {techdocsContent} + + ... + ); + + const serviceEntityPage = ( + ... + + + {techdocsContent} + + ... + ); + + const websiteEntityPage = ( + ... + + + {techdocsContent} + + ... + ); + ``` + +- 0e870fe0ac: Removed peer dependencies, as they are no longer needed. + +- bff65e6958: Use of `SidebarContext` has been deprecated and will be removed in a future release. Instead, `useSidebarOpenState()` should be used to consume the context and `` should be used to provide it. + + To prepare your app, update `packages/app/src/components/Root/Root.tsx` as follows: + + ```diff + import { + Sidebar, + sidebarConfig, + - SidebarContext + SidebarDivider, + // ... + SidebarSpace, + + useSidebarOpenState, + } from '@backstage/core-components'; + + // ... + + const SidebarLogo = () => { + const classes = useSidebarLogoStyles(); + - const { isOpen } = useContext(SidebarContext); + + const { isOpen } = useSidebarOpenState(); + + // ... + }; + ``` + +- 935d8515da: Updated the `--version` flag to output the version of the current backstage release instead of the version of create-app. + +- 18d4c3e50a: Updated `app-config.production.yaml` to specify an empty list of catalog locations. This is done to prevent example locations stored in `app-config.yaml` from being loaded as these are examples. + +- 1f70704580: Accessibility updates: + + - Added `aria-label` to the sidebar Logo link. To enable this for an existing app, please make the following changes: + + `packages/app/src/components/Root/Root.tsx` + + ```diff + const SidebarLogo = () => { + const classes = useSidebarLogoStyles(); + const { isOpen } = useContext(SidebarContext); + + return ( +
+ + {isOpen ? : } + +
+ ); + }; + ``` + +- 30f04d1497: Components ``, ``, ``, and `` are now deprecated in `@backstage/plugin-search` and should be imported from `@backstage/plugin-search-react` instead. + + To upgrade your App, update the following in `packages/app/src/components/search/SearchPage.tsx`: + + ```diff + import { + DefaultResultListItem + SearchBar + SearchFilter + SearchResult + - } from `@backstage/plugin-search`; + + } from `@backstage/plugin-search-react`; + ``` + +- f7f5a6c6a3: It's now possible to pass result item components a `rank`, which is captured by the analytics API when a user clicks on a search result. To apply this change, update your `/packages/app/src/components/search/SearchPage.tsx` in the following way: + + ```diff + // ... + + {({ results }) => ( + + - {results.map(({ type, document, highlight }) => { + + {results.map(({ type, document, highlight, rank }) => { + switch (type) { + case 'software-catalog': + return ( + + ); + case 'techdocs': + return ( + + ); + default: + return ( + + ); + } + })} + + )} + + // ... + ``` + + If you have implemented a custom Search Modal or other custom search experience, you will want to make similar changes in those components. + +- aaf7652084: Bump version of `cypress` in newly scaffolded Backstage Applications. To apply this change to your own instance, please make the following change to `packages/app/package.json` under `devDependencies`. + + ```diff + - "cypress": "^7.3.0", + + "cypress": "^9.7.0", + ``` + +- 141a1caebe: Updated the auth backend setup in the template to include a guest sign-in resolver in order to make it quicker to get up and running with a basic sign-in setup. There is no need to update existing apps to match this change, but in case you want to use the guest sign-in resolver you can find it at + +## @backstage/dev-utils@1.0.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/core-app-api@1.0.3 + - @backstage/core-plugin-api@1.0.3 + - @backstage/integration-react@1.1.1 + - @backstage/test-utils@1.1.1 + - @backstage/catalog-model@1.0.3 + - @backstage/app-defaults@1.0.3 + +## @backstage/integration@1.2.1 + +### Patch Changes + +- 72dfcbc8bf: Gerrit Integration: Handle absolute paths in `resolveUrl` properly. +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- e37c71b5a4: Updated to support deployments of Azure DevOps Server under TFS or similar sub path + +## @backstage/integration-react@1.1.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5 + - @backstage/integration@1.2.1 + - @backstage/core-plugin-api@1.0.3 + +## @backstage/release-manifests@0.0.4 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. + +## @techdocs/cli@1.1.2 + +### Patch Changes + +- f96e98f4cd: Updated dependency `cypress` to `^10.0.0`. +- bff65e6958: Updated sidebar-related logic to use `` + `useSidebarPinState()` and/or `` + `useSidebarOpenState()` from `@backstage/core-components`. +- Updated dependencies + - @backstage/backend-common@0.14.0 + - @backstage/plugin-techdocs-node@1.1.2 + - @backstage/catalog-model@1.0.3 + +## @backstage/test-utils@1.1.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-app-api@1.0.3 + - @backstage/core-plugin-api@1.0.3 + - @backstage/plugin-permission-common@0.6.2 + - @backstage/plugin-permission-react@0.4.2 + +## @backstage/plugin-adr@0.1.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- a6458a120b: Adding term highlighting support to `AdrSearchResultListItem` +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/plugin-search-common@0.3.5 + - @backstage/plugin-search-react@0.2.1 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/integration-react@1.1.1 + - @backstage/plugin-adr-common@0.1.1 + +## @backstage/plugin-adr-backend@0.1.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-search-common@0.3.5 + - @backstage/backend-common@0.14.0 + - @backstage/integration@1.2.1 + - @backstage/catalog-client@1.0.3 + - @backstage/catalog-model@1.0.3 + - @backstage/plugin-adr-common@0.1.1 + +## @backstage/plugin-adr-common@0.1.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-common@0.3.5 + - @backstage/integration@1.2.1 + - @backstage/catalog-model@1.0.3 + +## @backstage/plugin-airbrake@0.3.6 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/test-utils@1.1.1 + - @backstage/catalog-model@1.0.3 + - @backstage/dev-utils@1.0.3 + +## @backstage/plugin-airbrake-backend@0.2.6 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/backend-common@0.14.0 + +## @backstage/plugin-allure@0.1.22 + +### Patch Changes + +- 6387b7a98a: Add export for `isAllureReportAvailable` and `ALLURE_PROJECT_ID_ANNOTATION` so it can be used outside of plugin +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## @backstage/plugin-analytics-module-ga@0.1.17 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + +## @backstage/plugin-apache-airflow@0.1.14 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + +## @backstage/plugin-api-docs@0.8.6 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- 811ff4bcf4: Updated `swagger-ui-react` to 4.11.1 in order to address a [XSS + vulnerability](https://github.com/advisories/GHSA-hqq7-2q2v-82xq) in `@braintree/sanitize-url` +- Updated dependencies + - @backstage/plugin-catalog@1.3.0 + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## @backstage/plugin-app-backend@0.3.33 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/backend-common@0.14.0 + - @backstage/config-loader@1.1.2 + +## @backstage/plugin-auth-backend@0.14.1 + +### Patch Changes + +- 5e055079f0: Increased key field size for signing_keys table to account for larger signature keys +- f6aae90e4e: Added configurable algorithm field for TokenFactory +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- bc6fb57094: Updated dependency `passport` to `^0.6.0`. +- 467facc6ea: Fix improper binding of 'this' in ALB Auth provider +- Updated dependencies + - @backstage/backend-common@0.14.0 + - @backstage/plugin-auth-node@0.2.2 + - @backstage/catalog-client@1.0.3 + - @backstage/catalog-model@1.0.3 + +## @backstage/plugin-auth-node@0.2.2 + +### Patch Changes + +- 5ca0b86b88: Address corner cases where the key store was not being created at startup +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- 9079a78078: Added configurable algorithms array for IdentityClient +- Updated dependencies + - @backstage/backend-common@0.14.0 + +## @backstage/plugin-azure-devops@0.1.22 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## @backstage/plugin-azure-devops-backend@0.3.12 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/backend-common@0.14.0 + +## @backstage/plugin-badges@0.2.30 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## @backstage/plugin-badges-backend@0.1.27 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0 + - @backstage/catalog-client@1.0.3 + - @backstage/catalog-model@1.0.3 + +## @backstage/plugin-bazaar@0.1.21 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@1.3.0 + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/cli@0.17.2 + - @backstage/core-components@0.9.5 + - @backstage/catalog-client@1.0.3 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## @backstage/plugin-bazaar-backend@0.1.17 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0 + - @backstage/backend-test-utils@0.1.25 + +## @backstage/plugin-bitrise@0.1.33 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## @backstage/plugin-catalog-backend-module-aws@0.1.6 + +### Patch Changes + +- eb2544b21b: Inline config interfaces +- Updated dependencies + - @backstage/plugin-catalog-backend@1.2.0 + - @backstage/backend-tasks@0.3.2 + - @backstage/backend-common@0.14.0 + - @backstage/integration@1.2.1 + - @backstage/catalog-model@1.0.3 + +## @backstage/plugin-catalog-backend-module-azure@0.1.4 + +### Patch Changes + +- b8884fd579: Add a new provider `AzureDevOpsEntityProvider` as replacement for `AzureDevOpsDiscoveryProcessor`. + + In order to migrate from the `AzureDevOpsDiscoveryProcessor` you need to apply + the following changes: + + **Before:** + + ```yaml + # app-config.yaml + + catalog: + locations: + - type: azure-discovery + target: https://dev.azure.com/myorg/myproject/_git/service-*?path=/catalog-info.yaml + ``` + + ```ts + /* packages/backend/src/plugins/catalog.ts */ + + import { AzureDevOpsDiscoveryProcessor } from '@backstage/plugin-catalog-backend-module-azure'; + + const builder = await CatalogBuilder.create(env); + /** ... other processors ... */ + builder.addProcessor(new AzureDevOpsDiscoveryProcessor(env.reader)); + ``` + + **After:** + + ```yaml + # app-config.yaml + + catalog: + providers: + azureDevOps: + anyProviderId: + host: selfhostedazure.yourcompany.com # This is only really needed for on-premise user, defaults to dev.azure.com + organization: myorg # For on-premise this would be your Collection + project: myproject + repository: service-* + path: /catalog-info.yaml + ``` + + ```ts + /* packages/backend/src/plugins/catalog.ts */ + + import { AzureDevOpsEntityProvider } from '@backstage/plugin-catalog-backend-module-azure'; + + const builder = await CatalogBuilder.create(env); + /** ... other processors and/or providers ... */ + builder.addEntityProvider( + AzureDevOpsEntityProvider.fromConfig(env.config, { + logger: env.logger, + schedule: env.scheduler.createScheduledTaskRunner({ + frequency: { minutes: 30 }, + timeout: { minutes: 3 }, + }), + }), + ); + ``` + + Visit for more details and options on configuration. + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.2.0 + - @backstage/backend-tasks@0.3.2 + - @backstage/backend-common@0.14.0 + - @backstage/integration@1.2.1 + - @backstage/catalog-model@1.0.3 + +## @backstage/plugin-catalog-backend-module-gerrit@0.1.1 + +### Patch Changes + +- eb2544b21b: Inline config interfaces +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-backend@1.2.0 + - @backstage/backend-tasks@0.3.2 + - @backstage/backend-common@0.14.0 + - @backstage/integration@1.2.1 + - @backstage/catalog-model@1.0.3 + +## @backstage/plugin-catalog-backend-module-github@0.1.4 + +### Patch Changes + +- 8335a6f6f3: Adds an edit URL to the GitHub Teams Group entities. +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-backend@1.2.0 + - @backstage/backend-tasks@0.3.2 + - @backstage/backend-common@0.14.0 + - @backstage/integration@1.2.1 + - @backstage/catalog-model@1.0.3 + +## @backstage/plugin-catalog-backend-module-gitlab@0.1.4 + +### Patch Changes + +- eea8126171: Add a new provider `GitlabDiscoveryEntityProvider` as replacement for `GitlabDiscoveryProcessor` + + In order to migrate from the `GitlabDiscoveryProcessor` you need to apply + the following changes: + + **Before:** + + ```yaml + # app-config.yaml + + catalog: + locations: + - type: gitlab-discovery + target: https://company.gitlab.com/prefix/*/catalog-info.yaml + ``` + + ```ts + /* packages/backend/src/plugins/catalog.ts */ + + import { GitlabDiscoveryProcessor } from '@backstage/plugin-catalog-backend-module-gitlab'; + + const builder = await CatalogBuilder.create(env); + /** ... other processors ... */ + builder.addProcessor( + GitLabDiscoveryProcessor.fromConfig(env.config, { logger: env.logger }), + ); + ``` + + **After:** + + ```yaml + # app-config.yaml + + catalog: + providers: + gitlab: + yourProviderId: # identifies your dataset / provider independent of config changes + host: gitlab-host # Identifies one of the hosts set up in the integrations + branch: main # Optional. Uses `master` as default + group: example-group # Group and subgroup (if needed) to look for repositories + entityFilename: catalog-info.yaml # Optional. Defaults to `catalog-info.yaml` + ``` + + ```ts + /* packages/backend/src/plugins/catalog.ts */ + + import { GitlabDiscoveryEntityProvider } from '@backstage/plugin-catalog-backend-module-gitlab'; + + const builder = await CatalogBuilder.create(env); + /** ... other processors and/or providers ... */ + builder.addEntityProvider( + ...GitlabDiscoveryEntityProvider.fromConfig(env.config, { + logger: env.logger, + schedule: env.scheduler.createScheduledTaskRunner({ + frequency: { minutes: 30 }, + timeout: { minutes: 3 }, + }), + }), + ); + ``` + +- bad907d794: The `last_activity_after` timestamp is now being omitted when querying the GitLab API for the first time. + +- 3ac4522537: do not create location object if file with component definition do not exists in project, that decrease count of request to gitlab with 404 status code. Now we can create processor with new flag to enable this logic: + + ```ts + const processor = GitLabDiscoveryProcessor.fromConfig(config, { + logger, + skipReposWithoutExactFileMatch: true, + }); + ``` + + **WARNING:** This new functionality does not support globs in the repo file path + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.2.0 + - @backstage/backend-tasks@0.3.2 + - @backstage/backend-common@0.14.0 + - @backstage/integration@1.2.1 + - @backstage/catalog-model@1.0.3 + +## @backstage/plugin-catalog-backend-module-msgraph@0.3.3 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-backend@1.2.0 + - @backstage/backend-tasks@0.3.2 + - @backstage/catalog-model@1.0.3 + +## @backstage/plugin-catalog-common@1.0.3 + +### Patch Changes + +- 7d8acfc32e: Replaced all usages of `@backstage/search-common` with `@backstage/plugin-search-common` +- Updated dependencies + - @backstage/plugin-search-common@0.3.5 + - @backstage/plugin-permission-common@0.6.2 + +## @backstage/plugin-catalog-graph@0.2.18 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/catalog-client@1.0.3 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## @backstage/plugin-catalog-graphql@0.3.10 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/catalog-model@1.0.3 + +## @backstage/plugin-catalog-import@0.8.9 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- 05be420971: Updated catalog import page text so they go in the correct hierarchy order +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/integration@1.2.1 + - @backstage/catalog-client@1.0.3 + - @backstage/core-plugin-api@1.0.3 + - @backstage/integration-react@1.1.1 + - @backstage/catalog-model@1.0.3 + +## @backstage/plugin-catalog-react@1.1.1 + +### Patch Changes + +- 1f70704580: Accessibility updates: + + - Wrapped the `EntityLifecyclePicker`, `EntityOwnerPicker`, `EntityTagPicker`, in `label` elements + - Changed group name `Typography` component to `span` (from default `h6`), added `aria-label` to the `List` component, and `role` of `menuitem` to the container of the `MenuItem` component + +- 568f2d1e75: Table component no longer has drag and drop columns by default + +- Updated dependencies + - @backstage/plugin-catalog-common@1.0.3 + - @backstage/core-components@0.9.5 + - @backstage/integration@1.2.1 + - @backstage/catalog-client@1.0.3 + - @backstage/core-plugin-api@1.0.3 + - @backstage/plugin-permission-common@0.6.2 + - @backstage/catalog-model@1.0.3 + - @backstage/plugin-permission-react@0.4.2 + +## @backstage/plugin-cicd-statistics@0.1.8 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## @backstage/plugin-cicd-statistics-module-gitlab@0.1.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + - @backstage/plugin-cicd-statistics@0.1.8 + +## @backstage/plugin-circleci@0.3.6 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## @backstage/plugin-cloudbuild@0.3.6 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## @backstage/plugin-code-climate@0.1.6 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## @backstage/plugin-code-coverage@0.1.33 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## @backstage/plugin-code-coverage-backend@0.1.31 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/backend-common@0.14.0 + - @backstage/integration@1.2.1 + - @backstage/catalog-client@1.0.3 + - @backstage/catalog-model@1.0.3 + +## @backstage/plugin-codescene@0.1.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- ee2cd642c5: Updated dependency `rc-progress` to `3.3.3`. +- Updated dependencies + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + +## @backstage/plugin-config-schema@0.1.29 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + +## @backstage/plugin-cost-insights@0.11.28 + +### Patch Changes + +- dea1f32f44: In the README, a old path of the `sidebar` was updated to the current path. +- eb2544b21b: Add missing `export` in configuration schema. +- 2fc98ac50c: Fix broken app-config in the example in the README +- 2297510941: Fixed css to show large tooltips on cost overview graph +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## @backstage/plugin-explore@0.3.37 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/plugin-explore-react@0.0.18 + - @backstage/catalog-model@1.0.3 + +## @backstage/plugin-explore-react@0.0.18 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-plugin-api@1.0.3 + +## @backstage/plugin-firehydrant@0.1.23 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + +## @backstage/plugin-fossa@0.2.38 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## @backstage/plugin-gcalendar@0.3.2 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + +## @backstage/plugin-gcp-projects@0.3.25 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- 6968b65ba1: Updated dependency `@react-hookz/web` to `^14.0.0`. +- Updated dependencies + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + +## @backstage/plugin-git-release-manager@0.3.19 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5 + - @backstage/integration@1.2.1 + - @backstage/core-plugin-api@1.0.3 + +## @backstage/plugin-github-actions@0.5.6 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/integration@1.2.1 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## @backstage/plugin-github-deployments@0.1.37 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/integration@1.2.1 + - @backstage/core-plugin-api@1.0.3 + - @backstage/integration-react@1.1.1 + - @backstage/catalog-model@1.0.3 + +## @backstage/plugin-gitops-profiles@0.3.24 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + +## @backstage/plugin-gocd@0.1.12 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## @backstage/plugin-graphiql@0.2.38 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- 464c33f932: Fix for custom headers not being included in requests. +- Updated dependencies + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + +## @backstage/plugin-graphql-backend@0.1.23 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/backend-common@0.14.0 + - @backstage/plugin-catalog-graphql@0.3.10 + +## @backstage/plugin-home@0.4.22 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/plugin-stack-overflow@0.1.2 + - @backstage/catalog-model@1.0.3 + +## @backstage/plugin-ilert@0.1.32 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## @backstage/plugin-jenkins@0.7.5 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + - @backstage/plugin-jenkins-common@0.1.5 + +## @backstage/plugin-jenkins-backend@0.1.23 + +### Patch Changes + +- 83f6a64d2c: bug fix: provide backstage token for rebuild api call +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/backend-common@0.14.0 + - @backstage/plugin-auth-node@0.2.2 + - @backstage/catalog-client@1.0.3 + - @backstage/plugin-permission-common@0.6.2 + - @backstage/catalog-model@1.0.3 + - @backstage/plugin-jenkins-common@0.1.5 + +## @backstage/plugin-jenkins-common@0.1.5 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-common@1.0.3 + - @backstage/plugin-permission-common@0.6.2 + +## @backstage/plugin-kafka@0.3.6 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## @backstage/plugin-kafka-backend@0.2.26 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0 + - @backstage/catalog-model@1.0.3 + +## @backstage/plugin-kubernetes@0.6.6 + +### Patch Changes + +- 4328737af6: Add support to fetch data for Stateful Sets and display an accordion in the same way as with Deployments +- b9b8bbc7d9: show request/limit CPU and Memory on the UI +- 5553f09e80: ability to configure refresh interval on Kubernetes tab +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- 81304e3e91: Fix for HPA matching when deploying same HPA in multiple namespaces +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/plugin-kubernetes-common@0.3.0 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## @backstage/plugin-lighthouse@0.3.6 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## @backstage/plugin-newrelic@0.3.24 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + +## @backstage/plugin-newrelic-dashboard@0.1.14 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## @backstage/plugin-org@0.5.6 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. + +- 306d0b4fdd: Added the ability to use an additional `filter` when fetching groups in `MyGroupsSidebarItem` component. Example: + + ```diff + // app/src/components/Root/Root.tsx + + + //... + }> + {/* Global nav, not org-specific */} + //... + + + //... + + + + ``` + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## @backstage/plugin-periskop@0.1.4 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## @backstage/plugin-periskop-backend@0.1.4 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/backend-common@0.14.0 + +## @backstage/plugin-permission-backend@0.5.8 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-permission-node@0.6.2 + - @backstage/backend-common@0.14.0 + - @backstage/plugin-auth-node@0.2.2 + - @backstage/plugin-permission-common@0.6.2 + +## @backstage/plugin-permission-common@0.6.2 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. + +## @backstage/plugin-permission-node@0.6.2 + +### Patch Changes + +- 58426f9c0f: Added a new endpoint for aggregating permission metadata from a plugin backend: `/.well-known/backstage/permissions/metadata` + + By default, the metadata endpoint will return information about the permission rules supported by the plugin. Plugin authors can also provide an optional `permissions` parameter to `createPermissionIntegrationRouter`. If provided, these `Permission` objects will be included in the metadata returned by this endpoint. The `permissions` parameter will eventually be required in a future breaking change. + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. + +- Updated dependencies + - @backstage/backend-common@0.14.0 + - @backstage/plugin-auth-node@0.2.2 + - @backstage/plugin-permission-common@0.6.2 + +## @backstage/plugin-permission-react@0.4.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.0.3 + - @backstage/plugin-permission-common@0.6.2 + +## @backstage/plugin-proxy-backend@0.2.27 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0 + +## @backstage/plugin-rollbar@0.4.6 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## @backstage/plugin-rollbar-backend@0.1.30 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/backend-common@0.14.0 + +## @backstage/plugin-scaffolder-backend-module-cookiecutter@0.2.8 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-scaffolder-backend@1.3.0 + - @backstage/backend-common@0.14.0 + - @backstage/integration@1.2.1 + +## @backstage/plugin-scaffolder-backend-module-rails@0.4.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-backend@1.3.0 + - @backstage/backend-common@0.14.0 + - @backstage/integration@1.2.1 + +## @backstage/plugin-scaffolder-backend-module-yeoman@0.2.6 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-backend@1.3.0 + +## @backstage/plugin-scaffolder-common@1.1.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.0.3 + +## @backstage/plugin-search-backend@0.5.3 + +### Patch Changes + +- 7d8acfc32e: `RouterOptions` and `createRouter` now marked as public exports +- 915700f64f: The provided search engine now adds a pagination-aware `rank` value to all results. +- Updated dependencies + - @backstage/plugin-search-common@0.3.5 + - @backstage/plugin-permission-node@0.6.2 + - @backstage/backend-common@0.14.0 + - @backstage/plugin-auth-node@0.2.2 + - @backstage/plugin-search-backend-node@0.6.2 + - @backstage/plugin-permission-common@0.6.2 + +## @backstage/plugin-search-backend-module-elasticsearch@0.1.5 + +### Patch Changes + +- 281cec1b61: Use more precise matching for query filters + +- 915700f64f: The provided search engine now adds a pagination-aware `rank` value to all results. + +- ddce23d080: Now possible to set a custom index template on the elasticsearch search engine. + +- 7d8acfc32e: Additional types now exported publicly: + + - ElasticSearchAgentOptions + - ElasticSearchConcreteQuery + - ElasticSearchQueryTranslator + - ElasticSearchConnectionConstructor, + - ElasticSearchTransportConstructor, + - ElasticSearchNodeOptions, + - ElasticSearchOptions, + - ElasticSearchAuth, + +- Updated dependencies + - @backstage/plugin-search-common@0.3.5 + - @backstage/plugin-search-backend-node@0.6.2 + +## @backstage/plugin-search-backend-module-pg@0.3.4 + +### Patch Changes + +- 915700f64f: The provided search engine now adds a pagination-aware `rank` value to all results. +- Updated dependencies + - @backstage/plugin-search-common@0.3.5 + - @backstage/backend-common@0.14.0 + - @backstage/plugin-search-backend-node@0.6.2 + +## @backstage/plugin-search-backend-node@0.6.2 + +### Patch Changes + +- e7794a0aaa: propagate indexing errors so they don't appear successful to the task scheduler + +- 3bb25a9acc: Introducing a `NewlineDelimitedJsonCollatorFactory`, which can be used to create search indices from newline delimited JSON files stored in external storage readable via a configured `UrlReader` instance. + + This is useful if you have an independent process periodically generating `*.ndjson` files consisting of `IndexableDocument` objects and want to be able to generate a fresh index based on the latest version of such a file. + +- 3bb25a9acc: Fixed a bug that prevented `TestPipeline.withSubject` from identifying valid `Readable` subjects that were technically transform streams. + +- 915700f64f: The provided search engine now adds a pagination-aware `rank` value to all results. + +- 7d8acfc32e: Replaced all `@beta` exports with `@public` exports + +- Updated dependencies + - @backstage/plugin-search-common@0.3.5 + - @backstage/backend-tasks@0.3.2 + - @backstage/backend-common@0.14.0 + - @backstage/plugin-permission-common@0.6.2 + +## @backstage/plugin-search-common@0.3.5 + +### Patch Changes + +- 7d8acfc32e: `@beta` exports now replaced with `@public` exports +- 484afdf1dc: Added an optional `rank` attribute to the `Result` type. This represents the result rank (starting at 1) for a given result in a result set for a given search. +- Updated dependencies + - @backstage/plugin-permission-common@0.6.2 + +## @backstage/plugin-search-react@0.2.1 + +### Patch Changes + +- 8809159148: Components ``, `` (including ``), `` (including `.Checkbox`, `.Select`, and `.Autocomplete` static prop components), ``, and `` are now exported from `@backstage/plugin-search-react`. They are now deprecated in `@backstage/plugin-search` and will be removed in a future release. +- Updated dependencies + - @backstage/plugin-search-common@0.3.5 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + +## @backstage/plugin-sentry@0.3.44 + +### Patch Changes + +- c55f6cb22d: Exported `isSentryAvailable` which can be used to determine if sentry is available. +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## @backstage/plugin-shortcuts@0.2.7 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + +## @backstage/plugin-sonarqube@0.3.6 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- ee2cd642c5: Updated dependency `rc-progress` to `3.3.3`. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## @backstage/plugin-splunk-on-call@0.3.30 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## @backstage/plugin-stack-overflow@0.1.2 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-search-common@0.3.5 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/plugin-home@0.4.22 + +## @backstage/plugin-stack-overflow-backend@0.1.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-common@0.3.5 + +## @backstage/plugin-tech-insights@0.2.2 + +### Patch Changes + +- 09d2f4d179: Export TechInsightsClient so it may be extended by custom implementations +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## @backstage/plugin-tech-insights-backend@0.4.1 + +### Patch Changes + +- 4fee8f59e3: Updated tech-insights fetch/latest endpoint to return the actual latest row based on the timestamp + +- aa15229ec3: Introduce additional JsonValue types to be storable as facts. This enables the possibility to store more complex objects for fact checking purposes. The rules engine supports walking keyed object values directly to create rules and checks + + Modify facts database table to have a more restricted timestamp precision for cases where the postgres server isn't configured to contain such value. This fixes the issue where in some cases `maxItems` lifecycle condition didn't work as expected. + +- Updated dependencies + - @backstage/backend-tasks@0.3.2 + - @backstage/backend-common@0.14.0 + - @backstage/catalog-client@1.0.3 + - @backstage/catalog-model@1.0.3 + - @backstage/plugin-tech-insights-node@0.3.1 + +## @backstage/plugin-tech-insights-backend-module-jsonfc@0.1.17 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0 + - @backstage/plugin-tech-insights-node@0.3.1 + +## @backstage/plugin-tech-insights-node@0.3.1 + +### Patch Changes + +- aa15229ec3: Introduce additional JsonValue types to be storable as facts. This enables the possibility to store more complex objects for fact checking purposes. The rules engine supports walking keyed object values directly to create rules and checks + + Modify facts database table to have a more restricted timestamp precision for cases where the postgres server isn't configured to contain such value. This fixes the issue where in some cases `maxItems` lifecycle condition didn't work as expected. + +- Updated dependencies + - @backstage/backend-common@0.14.0 + +## @backstage/plugin-tech-radar@0.5.13 + +### Patch Changes + +- bb31e5489c: Updated dependency `d3-force` to `^3.0.0`. + Updated dependency `@types/d3-force` to `^3.0.0`. +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + +## @backstage/plugin-techdocs-addons-test-utils@1.0.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- d047d81295: Update default mock +- Updated dependencies + - @backstage/plugin-catalog@1.3.0 + - @backstage/plugin-techdocs@1.2.0 + - @backstage/plugin-search-react@0.2.1 + - @backstage/core-components@0.9.5 + - @backstage/core-app-api@1.0.3 + - @backstage/core-plugin-api@1.0.3 + - @backstage/integration-react@1.1.1 + - @backstage/test-utils@1.1.1 + - @backstage/plugin-techdocs-react@1.0.1 + +## @backstage/plugin-techdocs-backend@1.1.2 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. + +- 61fba6e50b: In order to ensure a good, stable TechDocs user experience when running TechDocs with `techdocs.builder` set to `local`, the number of concurrent builds has been limited to 10. Any additional builds requested concurrently will be queued and handled as prior builds complete. In the unlikely event that you need to handle more concurrent builds, consider scaling out your TechDocs backend deployment or using the `external` option for `techdocs.builder`. + +- 5d66d4ff67: Output logs from a TechDocs build to a logging transport in addition to existing + frontend event stream, for capturing these logs to other sources. + + This allows users to capture debugging information around why tech docs fail to build + without needing to rely on end users capturing information from their web browser. + + The most common use case is to log to the same place as the rest of the backend + application logs. + + Sample usage: + + import { DockerContainerRunner } from '@backstage/backend-common'; + import { + createRouter, + Generators, + Preparers, + Publisher, + } from '@backstage/plugin-techdocs-backend'; + import Docker from 'dockerode'; + import { Router } from 'express'; + import { PluginEnvironment } from '../types'; + + export default async function createPlugin( + env: PluginEnvironment, + ): Promise { + const preparers = await Preparers.fromConfig(env.config, { + logger: env.logger, + reader: env.reader, + }); + + const dockerClient = new Docker(); + const containerRunner = new DockerContainerRunner({ dockerClient }); + + const generators = await Generators.fromConfig(env.config, { + logger: env.logger, + containerRunner, + }); + + const publisher = await Publisher.fromConfig(env.config, { + logger: env.logger, + discovery: env.discovery, + }); + + await publisher.getReadiness(); + + return await createRouter({ + preparers, + generators, + publisher, + logger: env.logger, + // Passing a buildLogTransport as a parameter in createRouter will enable + // capturing build logs to a backend log stream + buildLogTransport: env.logger, + config: env.config, + discovery: env.discovery, + cache: env.cache, + }); + } + +- Updated dependencies + - @backstage/plugin-search-common@0.3.5 + - @backstage/plugin-catalog-common@1.0.3 + - @backstage/backend-common@0.14.0 + - @backstage/integration@1.2.1 + - @backstage/plugin-techdocs-node@1.1.2 + - @backstage/catalog-client@1.0.3 + - @backstage/plugin-permission-common@0.6.2 + - @backstage/catalog-model@1.0.3 + +## @backstage/plugin-techdocs-module-addons-contrib@1.0.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- 6968b65ba1: Updated dependency `@react-hookz/web` to `^14.0.0`. +- Updated dependencies + - @backstage/core-components@0.9.5 + - @backstage/integration@1.2.1 + - @backstage/core-plugin-api@1.0.3 + - @backstage/integration-react@1.1.1 + - @backstage/plugin-techdocs-react@1.0.1 + +## @backstage/plugin-techdocs-node@1.1.2 + +### Patch Changes + +- f5283a42e2: Updated dependency `@google-cloud/storage` to `^6.0.0`. +- 2c048f8b90: Updated deprecated use of `express`' `res.redirect()` method when handling legacy path casing. +- Updated dependencies + - @backstage/plugin-search-common@0.3.5 + - @backstage/backend-common@0.14.0 + - @backstage/integration@1.2.1 + - @backstage/catalog-model@1.0.3 + +## @backstage/plugin-techdocs-react@1.0.1 + +### Patch Changes + +- 3b45ad701f: Creates a `TechDocsShadowDom` component that takes a tree of elements and an `onAppend` handler: + + - Calls the `onAppend` handler when appending the element tree to the shadow root; + - Also dispatches an event when styles are loaded to let transformers know that the computed styles are ready to be consumed. + +- Updated dependencies + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## @backstage/plugin-todo@0.2.8 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## @backstage/plugin-todo-backend@0.1.30 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/backend-common@0.14.0 + - @backstage/integration@1.2.1 + - @backstage/catalog-client@1.0.3 + - @backstage/catalog-model@1.0.3 + +## @backstage/plugin-user-settings@0.4.5 + +### Patch Changes + +- 9d2d6a0cea: Added new `` to show the result of the `identityApi.getBackstageIdentity()` call to help debug ownership issues. The new card has been added to the user settings page. +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- bff65e6958: Updated sidebar-related logic to use `` + `useSidebarPinState()` and/or `` + `useSidebarOpenState()` from `@backstage/core-components`. +- Updated dependencies + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + +## @backstage/plugin-xcmetrics@0.2.26 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + +## example-app@0.2.72 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-kubernetes@0.6.6 + - @backstage/plugin-cost-insights@0.11.28 + - @backstage/plugin-scaffolder@1.3.0 + - @backstage/plugin-catalog@1.3.0 + - @backstage/plugin-techdocs@1.2.0 + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/cli@0.17.2 + - @backstage/plugin-user-settings@0.4.5 + - @backstage/plugin-pagerduty@0.4.0 + - @backstage/plugin-search-common@0.3.5 + - @backstage/plugin-search@0.9.0 + - @backstage/plugin-search-react@0.2.1 + - @backstage/plugin-sentry@0.3.44 + - @backstage/plugin-tech-insights@0.2.2 + - @backstage/plugin-catalog-common@1.0.3 + - @backstage/core-components@0.9.5 + - @backstage/plugin-dynatrace@0.1.0 + - @backstage/plugin-tech-radar@0.5.13 + - @backstage/core-app-api@1.0.3 + - @backstage/core-plugin-api@1.0.3 + - @backstage/integration-react@1.1.1 + - @backstage/plugin-airbrake@0.3.6 + - @backstage/plugin-apache-airflow@0.1.14 + - @backstage/plugin-api-docs@0.8.6 + - @backstage/plugin-azure-devops@0.1.22 + - @backstage/plugin-badges@0.2.30 + - @backstage/plugin-catalog-import@0.8.9 + - @backstage/plugin-circleci@0.3.6 + - @backstage/plugin-cloudbuild@0.3.6 + - @backstage/plugin-code-coverage@0.1.33 + - @backstage/plugin-explore@0.3.37 + - @backstage/plugin-gcalendar@0.3.2 + - @backstage/plugin-gcp-projects@0.3.25 + - @backstage/plugin-github-actions@0.5.6 + - @backstage/plugin-gocd@0.1.12 + - @backstage/plugin-graphiql@0.2.38 + - @backstage/plugin-home@0.4.22 + - @backstage/plugin-jenkins@0.7.5 + - @backstage/plugin-kafka@0.3.6 + - @backstage/plugin-lighthouse@0.3.6 + - @backstage/plugin-newrelic@0.3.24 + - @backstage/plugin-org@0.5.6 + - @backstage/plugin-rollbar@0.4.6 + - @backstage/plugin-shortcuts@0.2.7 + - @backstage/plugin-stack-overflow@0.1.2 + - @backstage/plugin-techdocs-module-addons-contrib@1.0.1 + - @backstage/plugin-todo@0.2.8 + - @backstage/catalog-model@1.0.3 + - @backstage/plugin-techdocs-react@1.0.1 + - @backstage/app-defaults@1.0.3 + - @backstage/plugin-catalog-graph@0.2.18 + - @backstage/plugin-newrelic-dashboard@0.1.14 + - @backstage/plugin-permission-react@0.4.2 + +## example-backend@0.2.72 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-tech-insights-backend@0.4.1 + - @backstage/plugin-catalog-backend@1.2.0 + - @backstage/plugin-auth-backend@0.14.1 + - @backstage/plugin-scaffolder-backend@1.3.0 + - @backstage/backend-tasks@0.3.2 + - @backstage/plugin-permission-node@0.6.2 + - @backstage/plugin-kubernetes-backend@0.6.0 + - @backstage/backend-common@0.14.0 + - @backstage/plugin-search-backend@0.5.3 + - @backstage/plugin-auth-node@0.2.2 + - @backstage/integration@1.2.1 + - @backstage/plugin-jenkins-backend@0.1.23 + - @backstage/plugin-search-backend-node@0.6.2 + - @backstage/catalog-client@1.0.3 + - @backstage/plugin-app-backend@0.3.33 + - @backstage/plugin-azure-devops-backend@0.3.12 + - @backstage/plugin-code-coverage-backend@0.1.31 + - @backstage/plugin-graphql-backend@0.1.23 + - @backstage/plugin-permission-backend@0.5.8 + - @backstage/plugin-permission-common@0.6.2 + - @backstage/plugin-rollbar-backend@0.1.30 + - @backstage/plugin-techdocs-backend@1.1.2 + - @backstage/plugin-todo-backend@0.1.30 + - @backstage/plugin-search-backend-module-elasticsearch@0.1.5 + - @backstage/plugin-search-backend-module-pg@0.3.4 + - @backstage/catalog-model@1.0.3 + - @backstage/plugin-tech-insights-node@0.3.1 + - example-app@0.2.72 + - @backstage/plugin-badges-backend@0.1.27 + - @backstage/plugin-kafka-backend@0.2.26 + - @backstage/plugin-proxy-backend@0.2.27 + - @backstage/plugin-scaffolder-backend-module-rails@0.4.1 + - @backstage/plugin-tech-insights-backend-module-jsonfc@0.1.17 + +## techdocs-cli-embedded-app@0.2.71 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@1.3.0 + - @backstage/plugin-techdocs@1.2.0 + - @backstage/cli@0.17.2 + - @backstage/core-components@0.9.5 + - @backstage/core-app-api@1.0.3 + - @backstage/core-plugin-api@1.0.3 + - @backstage/integration-react@1.1.1 + - @backstage/test-utils@1.1.1 + - @backstage/catalog-model@1.0.3 + - @backstage/plugin-techdocs-react@1.0.1 + - @backstage/app-defaults@1.0.3 + +## @internal/plugin-todo-list@1.0.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + +## @internal/plugin-todo-list-backend@1.0.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0 + - @backstage/plugin-auth-node@0.2.2 + +## @internal/plugin-todo-list-common@1.0.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-common@0.6.2 diff --git a/docs/releases/v1.3.0-next.0-changelog.md b/docs/releases/v1.3.0-next.0-changelog.md new file mode 100644 index 0000000000..412af14841 --- /dev/null +++ b/docs/releases/v1.3.0-next.0-changelog.md @@ -0,0 +1,1297 @@ +# Release v1.3.0-next.0 + +## @backstage/plugin-catalog-backend@1.2.0-next.0 + +### Minor Changes + +- b594679ae3: Allow array as non-spread arguments at the `CatalogBuilder`. + + ```typescript + builder.addEntityProvider(...getArrayOfProviders()); + ``` + + can be simplified to + + ```typescript + builder.addEntityProvider(getArrayOfProviders()); + ``` + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + - @backstage/integration@1.2.1-next.0 + - @backstage/plugin-permission-node@0.6.2-next.0 + +## @backstage/plugin-catalog-backend-module-ldap@0.5.0-next.0 + +### Minor Changes + +- 1f83f0bc84: Added the possibility to pass TLS configuration to ldap connection + +### Patch Changes + +- Updated dependencies + - @backstage/backend-tasks@0.3.2-next.0 + - @backstage/plugin-catalog-backend@1.2.0-next.0 + +## @backstage/plugin-kubernetes-backend@0.6.0-next.0 + +### Minor Changes + +- 4328737af6: Add support to fetch data for Stateful Sets from Kubernetes + +### Patch Changes + +- 0c70cd8e1d: cache and refresh Azure tokens to avoid excessive calls to Azure Identity +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + - @backstage/plugin-kubernetes-common@0.3.0-next.0 + +## @backstage/plugin-kubernetes-common@0.3.0-next.0 + +### Minor Changes + +- 4328737af6: Add support to fetch data for Stateful Sets + +## @backstage/plugin-scaffolder@1.3.0-next.0 + +### Minor Changes + +- 86a4a0f72d: Get data of other fields in Form from a custom field in template Scaffolder. + following: + + ```tsx + const CustomFieldExtensionComponent = (props: FieldExtensionComponentProps) => { + const { formData } = props.formContext; + ... + }; + + const CustomFieldExtension = scaffolderPlugin.provide( + createScaffolderFieldExtension({ + name: ..., + component: CustomFieldExtensionComponent, + validation: ... + }) + ); + ``` + +- 72dfcbc8bf: Gerrit Integration: Implemented a `RepoUrlPicker` for Gerrit. + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + - @backstage/integration@1.2.1-next.0 + - @backstage/integration-react@1.1.1-next.0 + +## @backstage/plugin-scaffolder-backend@1.3.0-next.0 + +### Minor Changes + +- 72dfcbc8bf: A new scaffolder action has been added: `gerrit:publish` + +### Patch Changes + +- 6901f6be4a: Adds more of an explanation when the `publish:github` scaffolder action fails to create a repository. +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + - @backstage/integration@1.2.1-next.0 + - @backstage/plugin-catalog-backend@1.2.0-next.0 + +## @backstage/app-defaults@1.0.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.9.5-next.0 + +## @backstage/backend-common@0.13.6-next.0 + +### Patch Changes + +- f72a6b8c62: Applied the `luxon` dependency fix from the `0.13.4` patch release. +- 5b22a8c97f: Applied the AWS S3 reading patch from the `0.13.5` patch release. +- Updated dependencies + - @backstage/integration@1.2.1-next.0 + +## @backstage/backend-tasks@0.3.2-next.0 + +### Patch Changes + +- fde10d24f6: Allow tasks that fail to retry on a loop emitting a warning log every time it fails with the amount of attempts it has +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + +## @backstage/backend-test-utils@0.1.25-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/cli@0.17.2-next.0 + - @backstage/backend-common@0.13.6-next.0 + +## @backstage/cli@0.17.2-next.0 + +### Patch Changes + +- 4f73352608: Updated Lockfile to support new versions of yarn as well as the legacy 1 version +- 6de866ea74: Added console warning to frontend start when the `app.baseUrl` and `backend.baseUrl` are identical + +## @backstage/core-components@0.9.5-next.0 + +### Patch Changes + +- 65840b17be: Fix issue where right arrow icon was incorrectly added to side bar items without a sub-menu +- 6968b65ba1: Updated dependency `@react-hookz/web` to `^14.0.0`. +- 96d1e01641: Accessibility updates: + + - Added `aria-label` to the `Select` component + - Changed heading level used in the header of `Table` component + +## @backstage/create-app@0.4.28-next.0 + +### Patch Changes + +- 881fbd7e8d: Register `TechDocs` addons on catalog entity pages, follow the steps below to add them manually: + + ```diff + // packages/app/src/components/catalog/EntityPage.tsx + + + import { TechDocsAddons } from '@backstage/plugin-techdocs-react'; + + import { + + ReportIssue, + + } from '@backstage/plugin-techdocs-module-addons-contrib'; + + + const techdocsContent = ( + + + + + + + + + + + + ); + + const defaultEntityPage = ( + ... + + + {techdocsContent} + + ... + ); + + const serviceEntityPage = ( + ... + + + {techdocsContent} + + ... + ); + + const websiteEntityPage = ( + ... + + + {techdocsContent} + + ... + ); + ``` + +- 935d8515da: Updated the `--version` flag to output the version of the current backstage release instead of the version of create-app. + +- 1f70704580: Accessibility updates: + + - Added `aria-label` to the sidebar Logo link. To enable this for an existing app, please make the following changes: + + `packages/app/src/components/Root/Root.tsx` + + ```diff + const SidebarLogo = () => { + const classes = useSidebarLogoStyles(); + const { isOpen } = useContext(SidebarContext); + + return ( +
+ + {isOpen ? : } + +
+ ); + }; + ``` + +## @backstage/dev-utils@1.0.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + - @backstage/app-defaults@1.0.3-next.0 + - @backstage/integration-react@1.1.1-next.0 + +## @backstage/integration@1.2.1-next.0 + +### Patch Changes + +- 72dfcbc8bf: Gerrit Integration: Handle absolute paths in `resolveUrl` properly. + +## @backstage/integration-react@1.1.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.9.5-next.0 + - @backstage/integration@1.2.1-next.0 + +## @techdocs/cli@1.1.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + - @backstage/plugin-techdocs-node@1.1.2-next.0 + +## @backstage/techdocs-common@0.11.16-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-techdocs-node@1.1.2-next.0 + +## @backstage/plugin-adr@0.1.1-next.0 + +### Patch Changes + +- a6458a120b: Adding term highlighting support to `AdrSearchResultListItem` +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + - @backstage/integration-react@1.1.1-next.0 + - @backstage/plugin-adr-common@0.1.1-next.0 + +## @backstage/plugin-adr-backend@0.1.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + - @backstage/integration@1.2.1-next.0 + - @backstage/plugin-adr-common@0.1.1-next.0 + +## @backstage/plugin-adr-common@0.1.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.2.1-next.0 + +## @backstage/plugin-airbrake@0.3.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + - @backstage/dev-utils@1.0.3-next.0 + +## @backstage/plugin-airbrake-backend@0.2.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + +## @backstage/plugin-allure@0.1.22-next.0 + +### Patch Changes + +- 6387b7a98a: Add export for `isAllureReportAvailable` and `ALLURE_PROJECT_ID_ANNOTATION` so it can be used outside of plugin +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + +## @backstage/plugin-analytics-module-ga@0.1.17-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.9.5-next.0 + +## @backstage/plugin-apache-airflow@0.1.14-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.9.5-next.0 + +## @backstage/plugin-api-docs@0.8.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/plugin-catalog@1.2.1-next.0 + - @backstage/core-components@0.9.5-next.0 + +## @backstage/plugin-app-backend@0.3.33-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + +## @backstage/plugin-auth-backend@0.14.1-next.0 + +### Patch Changes + +- f6aae90e4e: Added configurable algorithm field for TokenFactory +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + - @backstage/plugin-auth-node@0.2.2-next.0 + +## @backstage/plugin-auth-node@0.2.2-next.0 + +### Patch Changes + +- 9079a78078: Added configurable algorithms array for IdentityClient +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + +## @backstage/plugin-azure-devops@0.1.22-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + +## @backstage/plugin-azure-devops-backend@0.3.12-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + +## @backstage/plugin-badges@0.2.30-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + +## @backstage/plugin-badges-backend@0.1.27-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + +## @backstage/plugin-bazaar@0.1.21-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/plugin-catalog@1.2.1-next.0 + - @backstage/cli@0.17.2-next.0 + - @backstage/core-components@0.9.5-next.0 + +## @backstage/plugin-bazaar-backend@0.1.17-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + - @backstage/backend-test-utils@0.1.25-next.0 + +## @backstage/plugin-bitrise@0.1.33-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + +## @backstage/plugin-catalog@1.2.1-next.0 + +### Patch Changes + +- 449dcef98e: Updates the `isKind`, `รฌsComponentType`, and `isNamespace` to allow an array of possible values + +- 1f70704580: Accessibility updates: + + - Added screen reader elements to describe default table `Action` buttons + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + - @backstage/integration-react@1.1.1-next.0 + +## @backstage/plugin-catalog-backend-module-aws@0.1.6-next.0 + +### Patch Changes + +- eb2544b21b: Inline config interfaces +- Updated dependencies + - @backstage/backend-tasks@0.3.2-next.0 + - @backstage/backend-common@0.13.6-next.0 + - @backstage/integration@1.2.1-next.0 + - @backstage/plugin-catalog-backend@1.2.0-next.0 + +## @backstage/plugin-catalog-backend-module-azure@0.1.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + - @backstage/integration@1.2.1-next.0 + - @backstage/plugin-catalog-backend@1.2.0-next.0 + +## @backstage/plugin-catalog-backend-module-bitbucket@0.1.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + - @backstage/integration@1.2.1-next.0 + - @backstage/plugin-catalog-backend@1.2.0-next.0 + +## @backstage/plugin-catalog-backend-module-gerrit@0.1.1-next.0 + +### Patch Changes + +- eb2544b21b: Inline config interfaces +- Updated dependencies + - @backstage/backend-tasks@0.3.2-next.0 + - @backstage/backend-common@0.13.6-next.0 + - @backstage/integration@1.2.1-next.0 + - @backstage/plugin-catalog-backend@1.2.0-next.0 + +## @backstage/plugin-catalog-backend-module-github@0.1.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-tasks@0.3.2-next.0 + - @backstage/backend-common@0.13.6-next.0 + - @backstage/integration@1.2.1-next.0 + - @backstage/plugin-catalog-backend@1.2.0-next.0 + +## @backstage/plugin-catalog-backend-module-gitlab@0.1.4-next.0 + +### Patch Changes + +- 3ac4522537: do not create location object if file with component definition do not exists in project, that decrease count of request to gitlab with 404 status code. Now we can create processor with new flag to enable this logic: + + ```ts + const processor = GitLabDiscoveryProcessor.fromConfig(config, { + logger, + skipReposWithoutExactFileMatch: true, + }); + ``` + + **WARNING:** This new functionality does not support globs in the repo file path + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + - @backstage/integration@1.2.1-next.0 + - @backstage/plugin-catalog-backend@1.2.0-next.0 + +## @backstage/plugin-catalog-backend-module-msgraph@0.3.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-tasks@0.3.2-next.0 + - @backstage/plugin-catalog-backend@1.2.0-next.0 + +## @backstage/plugin-catalog-graph@0.2.18-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + +## @backstage/plugin-catalog-import@0.8.9-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + - @backstage/integration@1.2.1-next.0 + - @backstage/integration-react@1.1.1-next.0 + +## @backstage/plugin-catalog-react@1.1.1-next.0 + +### Patch Changes + +- 1f70704580: Accessibility updates: + + - Wrapped the `EntityLifecyclePicker`, `EntityOwnerPicker`, `EntityTagPicker`, in `label` elements + - Changed group name `Typography` component to `span` (from default `h6`), added `aria-label` to the `List` component, and `role` of `menuitem` to the container of the `MenuItem` component + +- Updated dependencies + - @backstage/core-components@0.9.5-next.0 + - @backstage/integration@1.2.1-next.0 + +## @backstage/plugin-cicd-statistics@0.1.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + +## @backstage/plugin-cicd-statistics-module-gitlab@0.1.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-cicd-statistics@0.1.8-next.0 + +## @backstage/plugin-circleci@0.3.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + +## @backstage/plugin-cloudbuild@0.3.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + +## @backstage/plugin-code-climate@0.1.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + +## @backstage/plugin-code-coverage@0.1.33-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + +## @backstage/plugin-code-coverage-backend@0.1.31-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + - @backstage/integration@1.2.1-next.0 + +## @backstage/plugin-codescene@0.1.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.9.5-next.0 + +## @backstage/plugin-config-schema@0.1.29-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.9.5-next.0 + +## @backstage/plugin-cost-insights@0.11.28-next.0 + +### Patch Changes + +- eb2544b21b: Add missing `export` in configuration schema. +- Updated dependencies + - @backstage/core-components@0.9.5-next.0 + +## @backstage/plugin-explore@0.3.37-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + +## @backstage/plugin-firehydrant@0.1.23-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + +## @backstage/plugin-fossa@0.2.38-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + +## @backstage/plugin-gcalendar@0.3.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.9.5-next.0 + +## @backstage/plugin-gcp-projects@0.3.25-next.0 + +### Patch Changes + +- 6968b65ba1: Updated dependency `@react-hookz/web` to `^14.0.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.0 + +## @backstage/plugin-git-release-manager@0.3.19-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.9.5-next.0 + - @backstage/integration@1.2.1-next.0 + +## @backstage/plugin-github-actions@0.5.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + - @backstage/integration@1.2.1-next.0 + +## @backstage/plugin-github-deployments@0.1.37-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + - @backstage/integration@1.2.1-next.0 + - @backstage/integration-react@1.1.1-next.0 + +## @backstage/plugin-gitops-profiles@0.3.24-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.9.5-next.0 + +## @backstage/plugin-gocd@0.1.12-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + +## @backstage/plugin-graphiql@0.2.38-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.9.5-next.0 + +## @backstage/plugin-graphql-backend@0.1.23-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + +## @backstage/plugin-home@0.4.22-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + - @backstage/plugin-stack-overflow@0.1.2-next.0 + +## @backstage/plugin-ilert@0.1.32-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + +## @backstage/plugin-jenkins@0.7.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + +## @backstage/plugin-jenkins-backend@0.1.23-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + - @backstage/plugin-auth-node@0.2.2-next.0 + +## @backstage/plugin-kafka@0.3.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + +## @backstage/plugin-kafka-backend@0.2.26-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + +## @backstage/plugin-kubernetes@0.6.6-next.0 + +### Patch Changes + +- 4328737af6: Add support to fetch data for Stateful Sets and display an accordion in the same way as with Deployments +- 81304e3e91: Fix for HPA matching when deploying same HPA in multiple namespaces +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + - @backstage/plugin-kubernetes-common@0.3.0-next.0 + +## @backstage/plugin-lighthouse@0.3.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + +## @backstage/plugin-newrelic@0.3.24-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.9.5-next.0 + +## @backstage/plugin-newrelic-dashboard@0.1.14-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + +## @backstage/plugin-org@0.5.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + +## @backstage/plugin-pagerduty@0.3.33-next.0 + +### Patch Changes + +- 76bf6400fe: Fix alert that was not showing after creating an incident. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + +## @backstage/plugin-periskop@0.1.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + +## @backstage/plugin-periskop-backend@0.1.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + +## @backstage/plugin-permission-backend@0.5.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + - @backstage/plugin-auth-node@0.2.2-next.0 + - @backstage/plugin-permission-node@0.6.2-next.0 + +## @backstage/plugin-permission-node@0.6.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + - @backstage/plugin-auth-node@0.2.2-next.0 + +## @backstage/plugin-proxy-backend@0.2.27-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + +## @backstage/plugin-rollbar@0.4.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + +## @backstage/plugin-rollbar-backend@0.1.30-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + +## @backstage/plugin-scaffolder-backend-module-cookiecutter@0.2.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-backend@1.3.0-next.0 + - @backstage/backend-common@0.13.6-next.0 + - @backstage/integration@1.2.1-next.0 + +## @backstage/plugin-scaffolder-backend-module-rails@0.4.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-backend@1.3.0-next.0 + - @backstage/backend-common@0.13.6-next.0 + - @backstage/integration@1.2.1-next.0 + +## @backstage/plugin-scaffolder-backend-module-yeoman@0.2.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-backend@1.3.0-next.0 + +## @backstage/plugin-search@0.8.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + +## @backstage/plugin-search-backend@0.5.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + - @backstage/plugin-search-backend-node@0.6.2-next.0 + - @backstage/plugin-auth-node@0.2.2-next.0 + - @backstage/plugin-permission-node@0.6.2-next.0 + +## @backstage/plugin-search-backend-module-elasticsearch@0.1.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-backend-node@0.6.2-next.0 + +## @backstage/plugin-search-backend-module-pg@0.3.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + - @backstage/plugin-search-backend-node@0.6.2-next.0 + +## @backstage/plugin-search-backend-node@0.6.2-next.0 + +### Patch Changes + +- e7794a0aaa: propagate indexing errors so they don't appear successful to the task scheduler +- Updated dependencies + - @backstage/backend-tasks@0.3.2-next.0 + +## @backstage/plugin-sentry@0.3.44-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + +## @backstage/plugin-shortcuts@0.2.7-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.9.5-next.0 + +## @backstage/plugin-sonarqube@0.3.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + +## @backstage/plugin-splunk-on-call@0.3.30-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + +## @backstage/plugin-stack-overflow@0.1.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.9.5-next.0 + - @backstage/plugin-home@0.4.22-next.0 + +## @backstage/plugin-tech-insights@0.2.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + +## @backstage/plugin-tech-insights-backend@0.4.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-tasks@0.3.2-next.0 + - @backstage/backend-common@0.13.6-next.0 + - @backstage/plugin-tech-insights-node@0.3.1-next.0 + +## @backstage/plugin-tech-insights-backend-module-jsonfc@0.1.17-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + - @backstage/plugin-tech-insights-node@0.3.1-next.0 + +## @backstage/plugin-tech-insights-node@0.3.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + +## @backstage/plugin-tech-radar@0.5.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.9.5-next.0 + +## @backstage/plugin-techdocs@1.1.2-next.0 + +### Patch Changes + +- 881fbd7e8d: Fix `EntityTechdocsContent` component to use objects instead of `` elements, otherwise "outlet" will be null on sub-pages and add-ons won't render. + +- 17c059dfd0: Restructures reader style transformations to improve code readability: + + - Extracts the style rules to separate files; + - Creates a hook that processes each rule; + - And creates another hook that returns a transformer responsible for injecting them into the head tag of a given element. + +- 3b45ad701f: Packages a set of tweaks to the TechDocs addons rendering process: + + - Prevents displaying sidebars until page styles are loaded and the sidebar position is updated; + - Prevents new sidebar locations from being created every time the reader page is rendered if these locations already exist; + - Centers the styles loaded event to avoid having multiple locations setting the opacity style in Shadow Dom causing the screen to flash multiple times. + +- 816f7475ec: Convert `sanitizeDOM` transformer to hook as part of code readability improvements in dom file. + +- 50ff56a80f: Change the `EntityDocsPage` path to be more specific and also add integration tests for `sub-routes` on this page. + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + - @backstage/integration@1.2.1-next.0 + - @backstage/plugin-techdocs-react@1.0.1-next.0 + - @backstage/integration-react@1.1.1-next.0 + +## @backstage/plugin-techdocs-addons-test-utils@1.0.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@1.2.1-next.0 + - @backstage/core-components@0.9.5-next.0 + - @backstage/plugin-techdocs@1.1.2-next.0 + - @backstage/plugin-techdocs-react@1.0.1-next.0 + - @backstage/integration-react@1.1.1-next.0 + +## @backstage/plugin-techdocs-backend@1.1.2-next.0 + +### Patch Changes + +- 5d66d4ff67: Output logs from a TechDocs build to a logging transport in addition to existing + frontend event stream, for capturing these logs to other sources. + + This allows users to capture debugging information around why tech docs fail to build + without needing to rely on end users capturing information from their web browser. + + The most common use case is to log to the same place as the rest of the backend + application logs. + + Sample usage: + + import { DockerContainerRunner } from '@backstage/backend-common'; + import { + createRouter, + Generators, + Preparers, + Publisher, + } from '@backstage/plugin-techdocs-backend'; + import Docker from 'dockerode'; + import { Router } from 'express'; + import { PluginEnvironment } from '../types'; + + export default async function createPlugin( + env: PluginEnvironment, + ): Promise { + const preparers = await Preparers.fromConfig(env.config, { + logger: env.logger, + reader: env.reader, + }); + + const dockerClient = new Docker(); + const containerRunner = new DockerContainerRunner({ dockerClient }); + + const generators = await Generators.fromConfig(env.config, { + logger: env.logger, + containerRunner, + }); + + const publisher = await Publisher.fromConfig(env.config, { + logger: env.logger, + discovery: env.discovery, + }); + + await publisher.getReadiness(); + + return await createRouter({ + preparers, + generators, + publisher, + logger: env.logger, + // Passing a buildLogTransport as a parameter in createRouter will enable + // capturing build logs to a backend log stream + buildLogTransport: env.logger, + config: env.config, + discovery: env.discovery, + cache: env.cache, + }); + } + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + - @backstage/integration@1.2.1-next.0 + - @backstage/plugin-techdocs-node@1.1.2-next.0 + +## @backstage/plugin-techdocs-module-addons-contrib@1.0.1-next.0 + +### Patch Changes + +- 6968b65ba1: Updated dependency `@react-hookz/web` to `^14.0.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.0 + - @backstage/integration@1.2.1-next.0 + - @backstage/plugin-techdocs-react@1.0.1-next.0 + - @backstage/integration-react@1.1.1-next.0 + +## @backstage/plugin-techdocs-node@1.1.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + - @backstage/integration@1.2.1-next.0 + +## @backstage/plugin-techdocs-react@1.0.1-next.0 + +### Patch Changes + +- 3b45ad701f: Creates a `TechDocsShadowDom` component that takes a tree of elements and an `onAppend` handler: + + - Calls the `onAppend` handler when appending the element tree to the shadow root; + - Also dispatches an event when styles are loaded to let transformers know that the computed styles are ready to be consumed. + +- Updated dependencies + - @backstage/core-components@0.9.5-next.0 + +## @backstage/plugin-todo@0.2.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + +## @backstage/plugin-todo-backend@0.1.30-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + - @backstage/integration@1.2.1-next.0 + +## @backstage/plugin-user-settings@0.4.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.9.5-next.0 + +## @backstage/plugin-xcmetrics@0.2.26-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.9.5-next.0 + +## example-app@0.2.72-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-kubernetes@0.6.6-next.0 + - @backstage/plugin-cost-insights@0.11.28-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/plugin-catalog@1.2.1-next.0 + - @backstage/cli@0.17.2-next.0 + - @backstage/plugin-pagerduty@0.3.33-next.0 + - @backstage/core-components@0.9.5-next.0 + - @backstage/plugin-gcp-projects@0.3.25-next.0 + - @backstage/plugin-techdocs-module-addons-contrib@1.0.1-next.0 + - @backstage/plugin-scaffolder@1.3.0-next.0 + - @backstage/plugin-techdocs@1.1.2-next.0 + - @backstage/plugin-techdocs-react@1.0.1-next.0 + - @backstage/plugin-airbrake@0.3.6-next.0 + - @backstage/plugin-api-docs@0.8.6-next.0 + - @backstage/plugin-azure-devops@0.1.22-next.0 + - @backstage/plugin-badges@0.2.30-next.0 + - @backstage/plugin-catalog-graph@0.2.18-next.0 + - @backstage/plugin-catalog-import@0.8.9-next.0 + - @backstage/plugin-circleci@0.3.6-next.0 + - @backstage/plugin-cloudbuild@0.3.6-next.0 + - @backstage/plugin-code-coverage@0.1.33-next.0 + - @backstage/plugin-explore@0.3.37-next.0 + - @backstage/plugin-github-actions@0.5.6-next.0 + - @backstage/plugin-gocd@0.1.12-next.0 + - @backstage/plugin-home@0.4.22-next.0 + - @backstage/plugin-jenkins@0.7.5-next.0 + - @backstage/plugin-kafka@0.3.6-next.0 + - @backstage/plugin-lighthouse@0.3.6-next.0 + - @backstage/plugin-newrelic-dashboard@0.1.14-next.0 + - @backstage/plugin-org@0.5.6-next.0 + - @backstage/plugin-rollbar@0.4.6-next.0 + - @backstage/plugin-search@0.8.2-next.0 + - @backstage/plugin-sentry@0.3.44-next.0 + - @backstage/plugin-tech-insights@0.2.2-next.0 + - @backstage/plugin-todo@0.2.8-next.0 + - @backstage/app-defaults@1.0.3-next.0 + - @backstage/integration-react@1.1.1-next.0 + - @backstage/plugin-apache-airflow@0.1.14-next.0 + - @backstage/plugin-gcalendar@0.3.2-next.0 + - @backstage/plugin-graphiql@0.2.38-next.0 + - @backstage/plugin-newrelic@0.3.24-next.0 + - @backstage/plugin-shortcuts@0.2.7-next.0 + - @backstage/plugin-stack-overflow@0.1.2-next.0 + - @backstage/plugin-tech-radar@0.5.13-next.0 + - @backstage/plugin-user-settings@0.4.5-next.0 + +## example-backend@0.2.72-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-tasks@0.3.2-next.0 + - @backstage/plugin-scaffolder-backend@1.3.0-next.0 + - @backstage/plugin-kubernetes-backend@0.6.0-next.0 + - @backstage/backend-common@0.13.6-next.0 + - @backstage/plugin-auth-backend@0.14.1-next.0 + - @backstage/integration@1.2.1-next.0 + - @backstage/plugin-search-backend-node@0.6.2-next.0 + - @backstage/plugin-catalog-backend@1.2.0-next.0 + - @backstage/plugin-auth-node@0.2.2-next.0 + - @backstage/plugin-techdocs-backend@1.1.2-next.0 + - example-app@0.2.72-next.0 + - @backstage/plugin-app-backend@0.3.33-next.0 + - @backstage/plugin-azure-devops-backend@0.3.12-next.0 + - @backstage/plugin-badges-backend@0.1.27-next.0 + - @backstage/plugin-code-coverage-backend@0.1.31-next.0 + - @backstage/plugin-graphql-backend@0.1.23-next.0 + - @backstage/plugin-jenkins-backend@0.1.23-next.0 + - @backstage/plugin-kafka-backend@0.2.26-next.0 + - @backstage/plugin-permission-backend@0.5.8-next.0 + - @backstage/plugin-permission-node@0.6.2-next.0 + - @backstage/plugin-proxy-backend@0.2.27-next.0 + - @backstage/plugin-rollbar-backend@0.1.30-next.0 + - @backstage/plugin-scaffolder-backend-module-rails@0.4.1-next.0 + - @backstage/plugin-search-backend@0.5.3-next.0 + - @backstage/plugin-search-backend-module-elasticsearch@0.1.5-next.0 + - @backstage/plugin-search-backend-module-pg@0.3.4-next.0 + - @backstage/plugin-tech-insights-backend@0.4.1-next.0 + - @backstage/plugin-tech-insights-backend-module-jsonfc@0.1.17-next.0 + - @backstage/plugin-tech-insights-node@0.3.1-next.0 + - @backstage/plugin-todo-backend@0.1.30-next.0 + +## techdocs-cli-embedded-app@0.2.71-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@1.2.1-next.0 + - @backstage/cli@0.17.2-next.0 + - @backstage/core-components@0.9.5-next.0 + - @backstage/plugin-techdocs@1.1.2-next.0 + - @backstage/plugin-techdocs-react@1.0.1-next.0 + - @backstage/app-defaults@1.0.3-next.0 + - @backstage/integration-react@1.1.1-next.0 + +## @internal/plugin-todo-list@1.0.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.9.5-next.0 + +## @internal/plugin-todo-list-backend@1.0.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + - @backstage/plugin-auth-node@0.2.2-next.0 diff --git a/docs/releases/v1.3.0-next.1-changelog.md b/docs/releases/v1.3.0-next.1-changelog.md new file mode 100644 index 0000000000..3d110614a1 --- /dev/null +++ b/docs/releases/v1.3.0-next.1-changelog.md @@ -0,0 +1,1597 @@ +# Release v1.3.0-next.1 + +## @backstage/plugin-bitbucket-cloud-common@0.1.0-next.0 + +### Minor Changes + +- 1dffa7dd4d: Add new common library `bitbucket-cloud-common` with a client for Bitbucket Cloud. + + This client can be reused across all packages and might be the future place for additional + features like managing the rate limits, etc. + + The client itself was generated in parts using the `@openapitools/openapi-generator-cli`. + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.2.1-next.1 + +## @backstage/plugin-catalog-backend-module-bitbucket@0.2.0-next.1 + +### Minor Changes + +- 1c01c0fd14: Integrate `@backstage/plugin-bitbucket-cloud-common` as replacement for the `BitbucketCloudClient`. + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/backend-common@0.13.6-next.1 + - @backstage/integration@1.2.1-next.1 + - @backstage/plugin-catalog-backend@1.2.0-next.1 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-bitbucket-cloud-common@0.1.0-next.0 + +## @backstage/plugin-catalog-backend-module-bitbucket-cloud@0.1.0-next.0 + +### Minor Changes + +- dfc4efcbf0: Add new plugin `catalog-backend-module-bitbucket-cloud` with `BitbucketCloudEntityProvider`. + + This entity provider is an alternative/replacement to the `BitbucketDiscoveryProcessor` **_(for Bitbucket Cloud only!)_**. + It replaces use cases using `search=true` and should be powerful enough as a complete replacement. + + If any feature for Bitbucket Cloud is missing and preventing you from switching, please raise an issue. + + **Before:** + + ```typescript + // packages/backend/src/plugins/catalog.ts + + builder.addProcessor( + BitbucketDiscoveryProcessor.fromConfig(env.config, { logger: env.logger }), + ); + ``` + + ```yaml + # app-config.yaml + + catalog: + locations: + - type: bitbucket-discovery + target: 'https://bitbucket.org/workspaces/workspace-name/projects/apis-*/repos/service-*?search=true&catalogPath=/catalog-info.yaml' + ``` + + **After:** + + ```typescript + // packages/backend/src/plugins/catalog.ts + builder.addEntityProvider( + BitbucketCloudEntityProvider.fromConfig(env.config, { + logger: env.logger, + schedule: env.scheduler.createScheduledTaskRunner({ + frequency: { minutes: 30 }, + timeout: { minutes: 3 }, + }), + }), + ); + ``` + + ```yaml + # app-config.yaml + + catalog: + providers: + bitbucketCloud: + yourProviderId: # identifies your ingested dataset + catalogPath: /catalog-info.yaml # default value + filters: # optional + projectKey: '^apis-.* # optional; RegExp + repoSlug: '^service-.* # optional; RegExp + workspace: workspace-name + ``` + +### Patch Changes + +- Updated dependencies + - @backstage/backend-tasks@0.3.2-next.1 + - @backstage/integration@1.2.1-next.1 + - @backstage/plugin-catalog-backend@1.2.0-next.1 + - @backstage/plugin-bitbucket-cloud-common@0.1.0-next.0 + +## @backstage/plugin-github-pull-requests-board@0.1.0-next.0 + +### Minor Changes + +- fc9927c81d: Add Github Pull Requests board plugin + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/integration@1.2.1-next.1 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## @backstage/plugin-scaffolder-backend@1.3.0-next.1 + +### Minor Changes + +- c042c5eaff: Add an option to not protect the default branch. + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/backend-common@0.13.6-next.1 + - @backstage/catalog-client@1.0.3-next.0 + - @backstage/integration@1.2.1-next.1 + - @backstage/plugin-catalog-backend@1.2.0-next.1 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-scaffolder-common@1.1.1-next.0 + +## @backstage/app-defaults@1.0.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-app-api@1.0.3-next.0 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/plugin-permission-react@0.4.2-next.0 + +## @backstage/backend-common@0.13.6-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/config-loader@1.1.2-next.0 + - @backstage/integration@1.2.1-next.1 + +## @backstage/backend-tasks@0.3.2-next.1 + +### Patch Changes + +- f7146b516f: Updated dependency `cron` to `^2.0.0`. + Updated dependency `@types/cron` to `^2.0.0`. +- 7f108513b8: Add error logging when a background task throws an error rather than silently swallowing it. +- Updated dependencies + - @backstage/backend-common@0.13.6-next.1 + +## @backstage/backend-test-utils@0.1.25-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/cli@0.17.2-next.1 + - @backstage/backend-common@0.13.6-next.1 + +## @backstage/catalog-client@1.0.3-next.0 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/catalog-model@1.0.3-next.0 + +## @backstage/catalog-model@1.0.3-next.0 + +### Patch Changes + +- 131a99e909: Added targetRef to common.schema.json to match the Typescript type + +## @backstage/cli@0.17.2-next.1 + +### Patch Changes + +- bd58365d09: Updated dependency `run-script-webpack-plugin` to `^0.0.14`. +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/config-loader@1.1.2-next.0 + - @backstage/release-manifests@0.0.4-next.0 + +## @backstage/config-loader@1.1.2-next.0 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. + +## @backstage/core-app-api@1.0.3-next.0 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-plugin-api@1.0.3-next.0 + +## @backstage/core-components@0.9.5-next.1 + +### Patch Changes + +- feb4e8de07: Fix EntityPage tab scrolling overflow bug on Firefox + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. + +- bff65e6958: The `SidebarPinStateContext` and `SidebarContext` have been deprecated and will be removed in a future release. Instead, use `` + `useSidebarPinState()` and/or `` + `useSidebarOpenState()`. + + This was done to ensure that sidebar state can be shared successfully across components exported by different packages, regardless of what version of this package is resolved and installed for each individual package. + +- Updated dependencies + - @backstage/core-plugin-api@1.0.3-next.0 + +## @backstage/core-plugin-api@1.0.3-next.0 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. + +## @backstage/create-app@0.4.28-next.1 + +### Patch Changes + +- bff65e6958: Use of `SidebarContext` has been deprecated and will be removed in a future release. Instead, `useSidebarOpenState()` should be used to consume the context and `` should be used to provide it. + + To prepare your app, update `packages/app/src/components/Root/Root.tsx` as follows: + + ```diff + import { + Sidebar, + sidebarConfig, + - SidebarContext + SidebarDivider, + // ... + SidebarSpace, + + useSidebarOpenState, + } from '@backstage/core-components'; + + // ... + + const SidebarLogo = () => { + const classes = useSidebarLogoStyles(); + - const { isOpen } = useContext(SidebarContext); + + const { isOpen } = useSidebarOpenState(); + + // ... + }; + ``` + +## @backstage/dev-utils@1.0.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-app-api@1.0.3-next.0 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/integration-react@1.1.1-next.1 + - @backstage/test-utils@1.1.1-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + - @backstage/app-defaults@1.0.3-next.1 + +## @backstage/integration@1.2.1-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. + +## @backstage/integration-react@1.1.1-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/integration@1.2.1-next.1 + +## @backstage/release-manifests@0.0.4-next.0 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. + +## @backstage/search-common@0.3.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-common@0.3.5-next.0 + +## @techdocs/cli@1.1.2-next.1 + +### Patch Changes + +- bff65e6958: Updated sidebar-related logic to use `` + `useSidebarPinState()` and/or `` + `useSidebarOpenState()` from `@backstage/core-components`. +- Updated dependencies + - @backstage/backend-common@0.13.6-next.1 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-techdocs-node@1.1.2-next.1 + +## @backstage/techdocs-common@0.11.16-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-techdocs-node@1.1.2-next.1 + +## @backstage/test-utils@1.1.1-next.0 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-app-api@1.0.3-next.0 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/plugin-permission-common@0.6.2-next.0 + - @backstage/plugin-permission-react@0.4.2-next.0 + +## @backstage/plugin-adr@0.1.1-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/integration-react@1.1.1-next.1 + - @backstage/plugin-catalog-react@1.1.1-next.1 + - @backstage/plugin-search-react@0.2.1-next.0 + - @backstage/plugin-search-common@0.3.5-next.0 + - @backstage/plugin-adr-common@0.1.1-next.1 + +## @backstage/plugin-adr-backend@0.1.1-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/backend-common@0.13.6-next.1 + - @backstage/catalog-client@1.0.3-next.0 + - @backstage/integration@1.2.1-next.1 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-search-common@0.3.5-next.0 + - @backstage/plugin-adr-common@0.1.1-next.1 + +## @backstage/plugin-adr-common@0.1.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.2.1-next.1 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-search-common@0.3.5-next.0 + +## @backstage/plugin-airbrake@0.3.6-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/test-utils@1.1.1-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + - @backstage/dev-utils@1.0.3-next.1 + +## @backstage/plugin-airbrake-backend@0.2.6-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/backend-common@0.13.6-next.1 + +## @backstage/plugin-allure@0.1.22-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## @backstage/plugin-analytics-module-ga@0.1.17-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + +## @backstage/plugin-apache-airflow@0.1.14-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + +## @backstage/plugin-api-docs@0.8.6-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog@1.2.1-next.1 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## @backstage/plugin-app-backend@0.3.33-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/backend-common@0.13.6-next.1 + - @backstage/config-loader@1.1.2-next.0 + +## @backstage/plugin-auth-backend@0.14.1-next.1 + +### Patch Changes + +- 5e055079f0: Increased key field size for signing_keys table to account for larger signature keys +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- 467facc6ea: Fix improper binding of 'this' in ALB Auth provider +- Updated dependencies + - @backstage/backend-common@0.13.6-next.1 + - @backstage/catalog-client@1.0.3-next.0 + - @backstage/plugin-auth-node@0.2.2-next.1 + - @backstage/catalog-model@1.0.3-next.0 + +## @backstage/plugin-auth-node@0.2.2-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/backend-common@0.13.6-next.1 + +## @backstage/plugin-azure-devops@0.1.22-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## @backstage/plugin-azure-devops-backend@0.3.12-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/backend-common@0.13.6-next.1 + +## @backstage/plugin-badges@0.2.30-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## @backstage/plugin-badges-backend@0.1.27-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.1 + - @backstage/catalog-client@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + +## @backstage/plugin-bazaar@0.1.21-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/cli@0.17.2-next.1 + - @backstage/catalog-client@1.0.3-next.0 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog@1.2.1-next.1 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## @backstage/plugin-bitrise@0.1.33-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## @backstage/plugin-catalog@1.2.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/catalog-client@1.0.3-next.0 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/integration-react@1.1.1-next.1 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + - @backstage/plugin-search-react@0.2.1-next.0 + - @backstage/plugin-catalog-common@1.0.3-next.0 + - @backstage/plugin-search-common@0.3.5-next.0 + +## @backstage/plugin-catalog-backend@1.2.0-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/backend-common@0.13.6-next.1 + - @backstage/catalog-client@1.0.3-next.0 + - @backstage/integration@1.2.1-next.1 + - @backstage/plugin-permission-common@0.6.2-next.0 + - @backstage/plugin-permission-node@0.6.2-next.1 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-common@1.0.3-next.0 + - @backstage/plugin-search-common@0.3.5-next.0 + - @backstage/plugin-scaffolder-common@1.1.1-next.0 + +## @backstage/plugin-catalog-backend-module-aws@0.1.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-tasks@0.3.2-next.1 + - @backstage/backend-common@0.13.6-next.1 + - @backstage/integration@1.2.1-next.1 + - @backstage/plugin-catalog-backend@1.2.0-next.1 + - @backstage/catalog-model@1.0.3-next.0 + +## @backstage/plugin-catalog-backend-module-azure@0.1.4-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/backend-common@0.13.6-next.1 + - @backstage/integration@1.2.1-next.1 + - @backstage/plugin-catalog-backend@1.2.0-next.1 + - @backstage/catalog-model@1.0.3-next.0 + +## @backstage/plugin-catalog-backend-module-gerrit@0.1.1-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/backend-tasks@0.3.2-next.1 + - @backstage/backend-common@0.13.6-next.1 + - @backstage/integration@1.2.1-next.1 + - @backstage/plugin-catalog-backend@1.2.0-next.1 + - @backstage/catalog-model@1.0.3-next.0 + +## @backstage/plugin-catalog-backend-module-github@0.1.4-next.1 + +### Patch Changes + +- 8335a6f6f3: Adds an edit URL to the GitHub Teams Group entities. +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/backend-tasks@0.3.2-next.1 + - @backstage/backend-common@0.13.6-next.1 + - @backstage/integration@1.2.1-next.1 + - @backstage/plugin-catalog-backend@1.2.0-next.1 + - @backstage/catalog-model@1.0.3-next.0 + +## @backstage/plugin-catalog-backend-module-gitlab@0.1.4-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/backend-common@0.13.6-next.1 + - @backstage/integration@1.2.1-next.1 + - @backstage/plugin-catalog-backend@1.2.0-next.1 + - @backstage/catalog-model@1.0.3-next.0 + +## @backstage/plugin-catalog-backend-module-ldap@0.5.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-tasks@0.3.2-next.1 + - @backstage/plugin-catalog-backend@1.2.0-next.1 + - @backstage/catalog-model@1.0.3-next.0 + +## @backstage/plugin-catalog-backend-module-msgraph@0.3.3-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/backend-tasks@0.3.2-next.1 + - @backstage/plugin-catalog-backend@1.2.0-next.1 + - @backstage/catalog-model@1.0.3-next.0 + +## @backstage/plugin-catalog-common@1.0.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-common@0.6.2-next.0 + - @backstage/search-common@0.3.5-next.0 + +## @backstage/plugin-catalog-graph@0.2.18-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/catalog-client@1.0.3-next.0 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## @backstage/plugin-catalog-graphql@0.3.10-next.0 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/catalog-model@1.0.3-next.0 + +## @backstage/plugin-catalog-import@0.8.9-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- 05be420971: Updated catalog import page text so they go in the correct hierarchy order +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/catalog-client@1.0.3-next.0 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/integration-react@1.1.1-next.1 + - @backstage/integration@1.2.1-next.1 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## @backstage/plugin-catalog-react@1.1.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/catalog-client@1.0.3-next.0 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/integration@1.2.1-next.1 + - @backstage/plugin-permission-common@0.6.2-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-permission-react@0.4.2-next.0 + - @backstage/plugin-catalog-common@1.0.3-next.0 + +## @backstage/plugin-cicd-statistics@0.1.8-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## @backstage/plugin-cicd-statistics-module-gitlab@0.1.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-cicd-statistics@0.1.8-next.1 + +## @backstage/plugin-circleci@0.3.6-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## @backstage/plugin-cloudbuild@0.3.6-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## @backstage/plugin-code-climate@0.1.6-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## @backstage/plugin-code-coverage@0.1.33-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## @backstage/plugin-code-coverage-backend@0.1.31-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/backend-common@0.13.6-next.1 + - @backstage/catalog-client@1.0.3-next.0 + - @backstage/integration@1.2.1-next.1 + - @backstage/catalog-model@1.0.3-next.0 + +## @backstage/plugin-codescene@0.1.1-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + +## @backstage/plugin-config-schema@0.1.29-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + +## @backstage/plugin-cost-insights@0.11.28-next.1 + +### Patch Changes + +- 2297510941: Fixed css to show large tooltips on cost overview graph +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + +## @backstage/plugin-explore@0.3.37-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/plugin-explore-react@0.0.18-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## @backstage/plugin-explore-react@0.0.18-next.0 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-plugin-api@1.0.3-next.0 + +## @backstage/plugin-firehydrant@0.1.23-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## @backstage/plugin-fossa@0.2.38-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## @backstage/plugin-gcalendar@0.3.2-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + +## @backstage/plugin-gcp-projects@0.3.25-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + +## @backstage/plugin-git-release-manager@0.3.19-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/integration@1.2.1-next.1 + +## @backstage/plugin-github-actions@0.5.6-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/integration@1.2.1-next.1 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## @backstage/plugin-github-deployments@0.1.37-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/integration-react@1.1.1-next.1 + - @backstage/integration@1.2.1-next.1 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## @backstage/plugin-gitops-profiles@0.3.24-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + +## @backstage/plugin-gocd@0.1.12-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## @backstage/plugin-graphiql@0.2.38-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- 464c33f932: Fix for custom headers not being included in requests. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + +## @backstage/plugin-graphql-backend@0.1.23-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/backend-common@0.13.6-next.1 + - @backstage/plugin-catalog-graphql@0.3.10-next.0 + +## @backstage/plugin-home@0.4.22-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/plugin-stack-overflow@0.1.2-next.1 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## @backstage/plugin-ilert@0.1.32-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## @backstage/plugin-jenkins@0.7.5-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + - @backstage/plugin-jenkins-common@0.1.5-next.0 + +## @backstage/plugin-jenkins-backend@0.1.23-next.1 + +### Patch Changes + +- 83f6a64d2c: bug fix: provide backstage token for rebuild api call +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/backend-common@0.13.6-next.1 + - @backstage/catalog-client@1.0.3-next.0 + - @backstage/plugin-auth-node@0.2.2-next.1 + - @backstage/plugin-permission-common@0.6.2-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-jenkins-common@0.1.5-next.0 + +## @backstage/plugin-jenkins-common@0.1.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-common@0.6.2-next.0 + - @backstage/plugin-catalog-common@1.0.3-next.0 + +## @backstage/plugin-kafka@0.3.6-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## @backstage/plugin-kafka-backend@0.2.26-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.1 + - @backstage/catalog-model@1.0.3-next.0 + +## @backstage/plugin-kubernetes@0.6.6-next.1 + +### Patch Changes + +- b9b8bbc7d9: show request/limit CPU and Memory on the UI +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + - @backstage/plugin-kubernetes-common@0.3.0-next.1 + +## @backstage/plugin-kubernetes-backend@0.6.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.1 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-kubernetes-common@0.3.0-next.1 + +## @backstage/plugin-kubernetes-common@0.3.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.0.3-next.0 + +## @backstage/plugin-lighthouse@0.3.6-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## @backstage/plugin-newrelic@0.3.24-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + +## @backstage/plugin-newrelic-dashboard@0.1.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## @backstage/plugin-org@0.5.6-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## @backstage/plugin-pagerduty@0.3.33-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## @backstage/plugin-periskop@0.1.4-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## @backstage/plugin-periskop-backend@0.1.4-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/backend-common@0.13.6-next.1 + +## @backstage/plugin-permission-backend@0.5.8-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/backend-common@0.13.6-next.1 + - @backstage/plugin-auth-node@0.2.2-next.1 + - @backstage/plugin-permission-common@0.6.2-next.0 + - @backstage/plugin-permission-node@0.6.2-next.1 + +## @backstage/plugin-permission-common@0.6.2-next.0 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. + +## @backstage/plugin-permission-node@0.6.2-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/backend-common@0.13.6-next.1 + - @backstage/plugin-auth-node@0.2.2-next.1 + - @backstage/plugin-permission-common@0.6.2-next.0 + +## @backstage/plugin-permission-react@0.4.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/plugin-permission-common@0.6.2-next.0 + +## @backstage/plugin-rollbar@0.4.6-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## @backstage/plugin-rollbar-backend@0.1.30-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/backend-common@0.13.6-next.1 + +## @backstage/plugin-scaffolder@1.3.0-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/catalog-client@1.0.3-next.0 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/integration-react@1.1.1-next.1 + - @backstage/integration@1.2.1-next.1 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + - @backstage/plugin-permission-react@0.4.2-next.0 + - @backstage/plugin-catalog-common@1.0.3-next.0 + - @backstage/plugin-scaffolder-common@1.1.1-next.0 + +## @backstage/plugin-scaffolder-backend-module-cookiecutter@0.2.8-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/backend-common@0.13.6-next.1 + - @backstage/integration@1.2.1-next.1 + - @backstage/plugin-scaffolder-backend@1.3.0-next.1 + +## @backstage/plugin-scaffolder-common@1.1.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.0.3-next.0 + +## @backstage/plugin-search@0.8.2-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + - @backstage/plugin-search-react@0.2.1-next.0 + - @backstage/plugin-search-common@0.3.5-next.0 + +## @backstage/plugin-search-backend@0.5.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.1 + - @backstage/plugin-auth-node@0.2.2-next.1 + - @backstage/plugin-permission-common@0.6.2-next.0 + - @backstage/plugin-permission-node@0.6.2-next.1 + - @backstage/plugin-search-backend-node@0.6.2-next.1 + - @backstage/plugin-search-common@0.3.5-next.0 + +## @backstage/plugin-search-backend-module-elasticsearch@0.1.5-next.1 + +### Patch Changes + +- 281cec1b61: Use more precise matching for query filters +- Updated dependencies + - @backstage/plugin-search-backend-node@0.6.2-next.1 + - @backstage/plugin-search-common@0.3.5-next.0 + +## @backstage/plugin-search-backend-module-pg@0.3.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.1 + - @backstage/plugin-search-backend-node@0.6.2-next.1 + - @backstage/plugin-search-common@0.3.5-next.0 + +## @backstage/plugin-search-backend-node@0.6.2-next.1 + +### Patch Changes + +- 3bb25a9acc: Introducing a `NewlineDelimitedJsonCollatorFactory`, which can be used to create search indices from newline delimited JSON files stored in external storage readable via a configured `UrlReader` instance. + + This is useful if you have an independent process periodically generating `*.ndjson` files consisting of `IndexableDocument` objects and want to be able to generate a fresh index based on the latest version of such a file. + +- 3bb25a9acc: Fixed a bug that prevented `TestPipeline.withSubject` from identifying valid `Readable` subjects that were technically transform streams. + +- Updated dependencies + - @backstage/backend-tasks@0.3.2-next.1 + - @backstage/backend-common@0.13.6-next.1 + - @backstage/plugin-permission-common@0.6.2-next.0 + - @backstage/plugin-search-common@0.3.5-next.0 + +## @backstage/plugin-search-common@0.3.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-common@0.6.2-next.0 + +## @backstage/plugin-search-react@0.2.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/plugin-search-common@0.3.5-next.0 + +## @backstage/plugin-sentry@0.3.44-next.1 + +### Patch Changes + +- c55f6cb22d: Exported `isSentryAvailable` which can be used to determine if sentry is available. +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## @backstage/plugin-shortcuts@0.2.7-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + +## @backstage/plugin-sonarqube@0.3.6-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## @backstage/plugin-splunk-on-call@0.3.30-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## @backstage/plugin-stack-overflow@0.1.2-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/plugin-home@0.4.22-next.1 + - @backstage/plugin-search-common@0.3.5-next.0 + +## @backstage/plugin-stack-overflow-backend@0.1.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-common@0.3.5-next.0 + +## @backstage/plugin-tech-insights@0.2.2-next.1 + +### Patch Changes + +- 09d2f4d179: Export TechInsightsClient so it may be extended by custom implementations +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## @backstage/plugin-tech-insights-backend@0.4.1-next.1 + +### Patch Changes + +- 4fee8f59e3: Updated tech-insights fetch/latest endpoint to return the actual latest row based on the timestamp +- Updated dependencies + - @backstage/backend-tasks@0.3.2-next.1 + - @backstage/backend-common@0.13.6-next.1 + - @backstage/catalog-client@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + +## @backstage/plugin-tech-radar@0.5.13-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + +## @backstage/plugin-techdocs@1.1.2-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- bff65e6958: Updated sidebar-related logic to use `` + `useSidebarPinState()` and/or `` + `useSidebarOpenState()` from `@backstage/core-components`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/integration-react@1.1.1-next.1 + - @backstage/integration@1.2.1-next.1 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + - @backstage/plugin-search-react@0.2.1-next.0 + - @backstage/plugin-techdocs-react@1.0.1-next.1 + - @backstage/plugin-search-common@0.3.5-next.0 + +## @backstage/plugin-techdocs-addons-test-utils@1.0.1-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-app-api@1.0.3-next.0 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/integration-react@1.1.1-next.1 + - @backstage/test-utils@1.1.1-next.0 + - @backstage/plugin-techdocs@1.1.2-next.1 + - @backstage/plugin-catalog@1.2.1-next.1 + - @backstage/plugin-search-react@0.2.1-next.0 + - @backstage/plugin-techdocs-react@1.0.1-next.1 + +## @backstage/plugin-techdocs-backend@1.1.2-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/backend-common@0.13.6-next.1 + - @backstage/catalog-client@1.0.3-next.0 + - @backstage/integration@1.2.1-next.1 + - @backstage/plugin-permission-common@0.6.2-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-common@1.0.3-next.0 + - @backstage/plugin-search-common@0.3.5-next.0 + - @backstage/plugin-techdocs-node@1.1.2-next.1 + +## @backstage/plugin-techdocs-module-addons-contrib@1.0.1-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/integration-react@1.1.1-next.1 + - @backstage/integration@1.2.1-next.1 + - @backstage/plugin-techdocs-react@1.0.1-next.1 + +## @backstage/plugin-techdocs-node@1.1.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.1 + - @backstage/integration@1.2.1-next.1 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-search-common@0.3.5-next.0 + +## @backstage/plugin-techdocs-react@1.0.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + +## @backstage/plugin-todo@0.2.8-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## @backstage/plugin-todo-backend@0.1.30-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/backend-common@0.13.6-next.1 + - @backstage/catalog-client@1.0.3-next.0 + - @backstage/integration@1.2.1-next.1 + - @backstage/catalog-model@1.0.3-next.0 + +## @backstage/plugin-user-settings@0.4.5-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- bff65e6958: Updated sidebar-related logic to use `` + `useSidebarPinState()` and/or `` + `useSidebarOpenState()` from `@backstage/core-components`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + +## @backstage/plugin-xcmetrics@0.2.26-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + +## example-app@0.2.72-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-sentry@0.3.44-next.1 + - @backstage/plugin-kubernetes@0.6.6-next.1 + - @backstage/plugin-tech-insights@0.2.2-next.1 + - @backstage/plugin-cost-insights@0.11.28-next.1 + - @backstage/core-components@0.9.5-next.1 + - @backstage/cli@0.17.2-next.1 + - @backstage/core-app-api@1.0.3-next.0 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/integration-react@1.1.1-next.1 + - @backstage/plugin-airbrake@0.3.6-next.1 + - @backstage/plugin-apache-airflow@0.1.14-next.1 + - @backstage/plugin-api-docs@0.8.6-next.1 + - @backstage/plugin-azure-devops@0.1.22-next.1 + - @backstage/plugin-badges@0.2.30-next.1 + - @backstage/plugin-catalog-import@0.8.9-next.1 + - @backstage/plugin-circleci@0.3.6-next.1 + - @backstage/plugin-cloudbuild@0.3.6-next.1 + - @backstage/plugin-code-coverage@0.1.33-next.1 + - @backstage/plugin-explore@0.3.37-next.1 + - @backstage/plugin-gcalendar@0.3.2-next.1 + - @backstage/plugin-gcp-projects@0.3.25-next.1 + - @backstage/plugin-github-actions@0.5.6-next.1 + - @backstage/plugin-gocd@0.1.12-next.1 + - @backstage/plugin-graphiql@0.2.38-next.1 + - @backstage/plugin-home@0.4.22-next.1 + - @backstage/plugin-jenkins@0.7.5-next.1 + - @backstage/plugin-kafka@0.3.6-next.1 + - @backstage/plugin-lighthouse@0.3.6-next.1 + - @backstage/plugin-newrelic@0.3.24-next.1 + - @backstage/plugin-org@0.5.6-next.1 + - @backstage/plugin-pagerduty@0.3.33-next.1 + - @backstage/plugin-rollbar@0.4.6-next.1 + - @backstage/plugin-scaffolder@1.3.0-next.1 + - @backstage/plugin-search@0.8.2-next.1 + - @backstage/plugin-shortcuts@0.2.7-next.1 + - @backstage/plugin-stack-overflow@0.1.2-next.1 + - @backstage/plugin-tech-radar@0.5.13-next.1 + - @backstage/plugin-techdocs-module-addons-contrib@1.0.1-next.1 + - @backstage/plugin-techdocs@1.1.2-next.1 + - @backstage/plugin-todo@0.2.8-next.1 + - @backstage/plugin-user-settings@0.4.5-next.1 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog@1.2.1-next.1 + - @backstage/plugin-catalog-graph@0.2.18-next.1 + - @backstage/plugin-catalog-react@1.1.1-next.1 + - @backstage/app-defaults@1.0.3-next.1 + - @backstage/plugin-search-react@0.2.1-next.0 + - @backstage/plugin-newrelic-dashboard@0.1.14-next.1 + - @backstage/plugin-permission-react@0.4.2-next.0 + - @backstage/plugin-techdocs-react@1.0.1-next.1 + - @backstage/plugin-catalog-common@1.0.3-next.0 + - @backstage/plugin-search-common@0.3.5-next.0 + +## example-backend@0.2.72-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-tech-insights-backend@0.4.1-next.1 + - @backstage/plugin-auth-backend@0.14.1-next.1 + - @backstage/plugin-jenkins-backend@0.1.23-next.1 + - @backstage/backend-tasks@0.3.2-next.1 + - @backstage/backend-common@0.13.6-next.1 + - @backstage/catalog-client@1.0.3-next.0 + - @backstage/integration@1.2.1-next.1 + - @backstage/plugin-app-backend@0.3.33-next.1 + - @backstage/plugin-auth-node@0.2.2-next.1 + - @backstage/plugin-azure-devops-backend@0.3.12-next.1 + - @backstage/plugin-catalog-backend@1.2.0-next.1 + - @backstage/plugin-code-coverage-backend@0.1.31-next.1 + - @backstage/plugin-graphql-backend@0.1.23-next.1 + - @backstage/plugin-permission-backend@0.5.8-next.1 + - @backstage/plugin-permission-common@0.6.2-next.0 + - @backstage/plugin-permission-node@0.6.2-next.1 + - @backstage/plugin-rollbar-backend@0.1.30-next.1 + - @backstage/plugin-scaffolder-backend@1.3.0-next.1 + - @backstage/plugin-techdocs-backend@1.1.2-next.1 + - @backstage/plugin-todo-backend@0.1.30-next.1 + - @backstage/plugin-search-backend-module-elasticsearch@0.1.5-next.1 + - @backstage/plugin-search-backend-node@0.6.2-next.1 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-badges-backend@0.1.27-next.1 + - example-app@0.2.72-next.1 + - @backstage/plugin-search-backend@0.5.3-next.1 + - @backstage/plugin-kafka-backend@0.2.26-next.1 + - @backstage/plugin-kubernetes-backend@0.6.0-next.1 + - @backstage/plugin-search-backend-module-pg@0.3.4-next.1 + +## techdocs-cli-embedded-app@0.2.71-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/cli@0.17.2-next.1 + - @backstage/core-app-api@1.0.3-next.0 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/integration-react@1.1.1-next.1 + - @backstage/test-utils@1.1.1-next.0 + - @backstage/plugin-techdocs@1.1.2-next.1 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog@1.2.1-next.1 + - @backstage/app-defaults@1.0.3-next.1 + - @backstage/plugin-techdocs-react@1.0.1-next.1 + +## @internal/plugin-todo-list@1.0.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + +## @internal/plugin-todo-list-common@1.0.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-common@0.6.2-next.0 diff --git a/docs/releases/v1.3.0-next.2-changelog.md b/docs/releases/v1.3.0-next.2-changelog.md new file mode 100644 index 0000000000..fc4bb19080 --- /dev/null +++ b/docs/releases/v1.3.0-next.2-changelog.md @@ -0,0 +1,678 @@ +# Release v1.3.0-next.2 + +## @backstage/backend-common@0.14.0-next.2 + +### Minor Changes + +- 55647ec7df: **BREAKING**: Server-to-server tokens that are authenticated by the `ServerTokenManager` now must have an `exp` claim that has not expired. Tokens where the `exp` claim is in the past or missing are considered invalid and will throw an error. This is a followup to the deprecation from the `1.2` release of Backstage where perpetual tokens were deprecated. Be sure to update any usage of the `getToken()` method to have it be called every time a token is needed. Do not store tokens for later use. + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.2.1-next.2 + +## @backstage/plugin-pagerduty@0.4.0-next.2 + +### Minor Changes + +- b157c2eb1c: **Breaking**: Use identityApi to provide auth token for pagerduty API calls. + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.9.5-next.2 + +## @backstage/plugin-scaffolder@1.3.0-next.2 + +### Minor Changes + +- dc39366bdb: - Added a new page under `/create/tasks` to show tasks that have been run by the Scaffolder. + - Ability to filter these tasks by the signed in user, and all tasks. + - Added optional method to the `ScaffolderApi` interface called `listTasks` to get tasks with an required `filterByOwnership` parameter. + +### Patch Changes + +- ac0c7e45ee: Fixes review mask in `MultistepJsonForm` to work as documented. `show: true` no longer needed when mask is set. +- fd505f40c0: Handle binary files and files that are too large during dry-run content upload. +- Updated dependencies + - @backstage/plugin-catalog-common@1.0.3-next.1 + - @backstage/core-components@0.9.5-next.2 + - @backstage/integration@1.2.1-next.2 + +## @backstage/plugin-scaffolder-backend@1.3.0-next.2 + +### Minor Changes + +- ce0d8d7eb1: Fixed a bug in `publish:github` action that didn't permit to add users as collaborators. + This fix required changing the way parameters are passed to the action. + In order to add a team as collaborator, now you must use the `team` field instead of `username`. + In order to add a user as collaborator, you must use the `user` field. + + It's still possible to use the field `username` but is deprecated in favor of `team`. + + ```yaml + - id: publish + name: Publish + action: publish:github + input: + repoUrl: ... + collaborators: + - access: ... + team: my_team + - access: ... + user: my_username + ``` + +- 582003a059: - Added an optional `list` method on the `TaskBroker` and `TaskStore` interface to list tasks by an optional `userEntityRef` + - Implemented a `list` method on the `DatabaseTaskStore` class to list tasks by an optional `userEntityRef` + - Added a route under `/v2/tasks` to list tasks by a `userEntityRef` using the `createdBy` query parameter + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0-next.2 + - @backstage/integration@1.2.1-next.2 + - @backstage/plugin-catalog-backend@1.2.0-next.2 + +## @backstage/backend-tasks@0.3.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0-next.2 + +## @backstage/backend-test-utils@0.1.25-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/cli@0.17.2-next.2 + - @backstage/backend-common@0.14.0-next.2 + +## @backstage/cli@0.17.2-next.2 + +### Patch Changes + +- 026cfe525a: Fix the public path configuration of the frontend app build so that a trailing `/` is always appended when needed. +- 9002ebd76b: Updated dependency `@rollup/plugin-commonjs` to `^22.0.0`. +- 1a33e8b287: Updated dependency `minimatch` to `5.1.0`. + +## @backstage/core-components@0.9.5-next.2 + +### Patch Changes + +- ee2cd642c5: Updated dependency `rc-progress` to `3.3.3`. +- 1cf9caecd6: fix Sidebar Contexts deprecation message + +## @backstage/create-app@0.4.28-next.2 + +### Patch Changes + +- aaf7652084: Bump version of `cypress` in newly scaffolded Backstage Applications. To apply this change to your own instance, please make the following change to `packages/app/package.json` under `devDependencies`. + + ```diff + - "cypress": "^7.3.0", + + "cypress": "^9.7.0", + ``` + +## @backstage/integration@1.2.1-next.2 + +### Patch Changes + +- e37c71b5a4: Updated to support deployments of Azure DevOps Server under TFS or similar sub path + +## @backstage/search-common@0.3.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-common@0.3.5-next.1 + +## @techdocs/cli@1.1.2-next.2 + +### Patch Changes + +- f96e98f4cd: Updated dependency `cypress` to `^10.0.0`. +- Updated dependencies + - @backstage/backend-common@0.14.0-next.2 + - @backstage/plugin-techdocs-node@1.1.2-next.2 + +## @backstage/techdocs-common@0.11.16-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-techdocs-node@1.1.2-next.2 + +## @backstage/plugin-adr-backend@0.1.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-common@0.3.5-next.1 + - @backstage/backend-common@0.14.0-next.2 + - @backstage/integration@1.2.1-next.2 + +## @backstage/plugin-airbrake-backend@0.2.6-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0-next.2 + +## @backstage/plugin-app-backend@0.3.33-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0-next.2 + +## @backstage/plugin-auth-backend@0.14.1-next.2 + +### Patch Changes + +- bc6fb57094: Updated dependency `passport` to `^0.6.0`. +- Updated dependencies + - @backstage/backend-common@0.14.0-next.2 + - @backstage/plugin-auth-node@0.2.2-next.2 + +## @backstage/plugin-auth-node@0.2.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0-next.2 + +## @backstage/plugin-azure-devops-backend@0.3.12-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0-next.2 + +## @backstage/plugin-badges-backend@0.1.27-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0-next.2 + +## @backstage/plugin-bazaar-backend@0.1.17-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0-next.2 + - @backstage/backend-test-utils@0.1.25-next.2 + +## @backstage/plugin-catalog-backend@1.2.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-common@0.3.5-next.1 + - @backstage/plugin-catalog-common@1.0.3-next.1 + - @backstage/backend-common@0.14.0-next.2 + - @backstage/integration@1.2.1-next.2 + - @backstage/plugin-permission-node@0.6.2-next.2 + +## @backstage/plugin-catalog-backend-module-aws@0.1.6-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0-next.2 + - @backstage/integration@1.2.1-next.2 + - @backstage/backend-tasks@0.3.2-next.2 + - @backstage/plugin-catalog-backend@1.2.0-next.2 + +## @backstage/plugin-catalog-backend-module-azure@0.1.4-next.2 + +### Patch Changes + +- b8884fd579: Add a new provider `AzureDevOpsEntityProvider` as replacement for `AzureDevOpsDiscoveryProcessor`. + + In order to migrate from the `AzureDevOpsDiscoveryProcessor` you need to apply + the following changes: + + **Before:** + + ```yaml + # app-config.yaml + + catalog: + locations: + - type: azure-discovery + target: https://dev.azure.com/myorg/myproject/_git/service-*?path=/catalog-info.yaml + ``` + + ```ts + /* packages/backend/src/plugins/catalog.ts */ + + import { AzureDevOpsDiscoveryProcessor } from '@backstage/plugin-catalog-backend-module-azure'; + + const builder = await CatalogBuilder.create(env); + /** ... other processors ... */ + builder.addProcessor(new AzureDevOpsDiscoveryProcessor(env.reader)); + ``` + + **After:** + + ```yaml + # app-config.yaml + + catalog: + providers: + azureDevOps: + anyProviderId: + host: selfhostedazure.yourcompany.com # This is only really needed for on-premise user, defaults to dev.azure.com + organization: myorg # For on-premise this would be your Collection + project: myproject + repository: service-* + path: /catalog-info.yaml + ``` + + ```ts + /* packages/backend/src/plugins/catalog.ts */ + + import { AzureDevOpsEntityProvider } from '@backstage/plugin-catalog-backend-module-azure'; + + const builder = await CatalogBuilder.create(env); + /** ... other processors and/or providers ... */ + builder.addEntityProvider( + AzureDevOpsEntityProvider.fromConfig(env.config, { + logger: env.logger, + schedule: env.scheduler.createScheduledTaskRunner({ + frequency: { minutes: 30 }, + timeout: { minutes: 3 }, + }), + }), + ); + ``` + + Visit for more details and options on configuration. + +- Updated dependencies + - @backstage/backend-common@0.14.0-next.2 + - @backstage/integration@1.2.1-next.2 + - @backstage/backend-tasks@0.3.2-next.2 + - @backstage/plugin-catalog-backend@1.2.0-next.2 + +## @backstage/plugin-catalog-backend-module-bitbucket@0.2.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0-next.2 + - @backstage/integration@1.2.1-next.2 + - @backstage/plugin-catalog-backend@1.2.0-next.2 + +## @backstage/plugin-catalog-backend-module-gerrit@0.1.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0-next.2 + - @backstage/integration@1.2.1-next.2 + - @backstage/backend-tasks@0.3.2-next.2 + - @backstage/plugin-catalog-backend@1.2.0-next.2 + +## @backstage/plugin-catalog-backend-module-github@0.1.4-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0-next.2 + - @backstage/integration@1.2.1-next.2 + - @backstage/backend-tasks@0.3.2-next.2 + - @backstage/plugin-catalog-backend@1.2.0-next.2 + +## @backstage/plugin-catalog-backend-module-gitlab@0.1.4-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0-next.2 + - @backstage/integration@1.2.1-next.2 + - @backstage/plugin-catalog-backend@1.2.0-next.2 + +## @backstage/plugin-catalog-common@1.0.3-next.1 + +### Patch Changes + +- 7d8acfc32e: Replaced all usages of `@backstage/search-common` with `@backstage/plugin-search-common` +- Updated dependencies + - @backstage/plugin-search-common@0.3.5-next.1 + +## @backstage/plugin-code-coverage-backend@0.1.31-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0-next.2 + - @backstage/integration@1.2.1-next.2 + +## @backstage/plugin-codescene@0.1.1-next.2 + +### Patch Changes + +- ee2cd642c5: Updated dependency `rc-progress` to `3.3.3`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.2 + +## @backstage/plugin-cost-insights@0.11.28-next.2 + +### Patch Changes + +- 2fc98ac50c: Fix broken app-config in the example in the README +- Updated dependencies + - @backstage/core-components@0.9.5-next.2 + +## @backstage/plugin-graphql-backend@0.1.23-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0-next.2 + +## @backstage/plugin-jenkins-backend@0.1.23-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0-next.2 + - @backstage/plugin-auth-node@0.2.2-next.2 + +## @backstage/plugin-kafka-backend@0.2.26-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0-next.2 + +## @backstage/plugin-kubernetes@0.6.6-next.2 + +### Patch Changes + +- 5553f09e80: ability to configure refresh interval on Kubernetes tab +- Updated dependencies + - @backstage/core-components@0.9.5-next.2 + +## @backstage/plugin-kubernetes-backend@0.6.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0-next.2 + +## @backstage/plugin-org@0.5.6-next.2 + +### Patch Changes + +- 306d0b4fdd: Added the ability to use an additional `filter` when fetching groups in `MyGroupsSidebarItem` component. Example: + + ```diff + // app/src/components/Root/Root.tsx + + + //... + }> + {/* Global nav, not org-specific */} + //... + + + //... + + + + ``` + +- Updated dependencies + - @backstage/core-components@0.9.5-next.2 + +## @backstage/plugin-periskop-backend@0.1.4-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0-next.2 + +## @backstage/plugin-permission-backend@0.5.8-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0-next.2 + - @backstage/plugin-auth-node@0.2.2-next.2 + - @backstage/plugin-permission-node@0.6.2-next.2 + +## @backstage/plugin-permission-node@0.6.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0-next.2 + - @backstage/plugin-auth-node@0.2.2-next.2 + +## @backstage/plugin-proxy-backend@0.2.27-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0-next.2 + +## @backstage/plugin-rollbar-backend@0.1.30-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0-next.2 + +## @backstage/plugin-scaffolder-backend-module-cookiecutter@0.2.8-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-backend@1.3.0-next.2 + - @backstage/backend-common@0.14.0-next.2 + - @backstage/integration@1.2.1-next.2 + +## @backstage/plugin-scaffolder-backend-module-rails@0.4.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-backend@1.3.0-next.2 + - @backstage/backend-common@0.14.0-next.2 + - @backstage/integration@1.2.1-next.2 + +## @backstage/plugin-search@0.8.2-next.2 + +### Patch Changes + +- 5388e6bdc5: Fixed a bug that could cause analytics events in other parts of Backstage to capture nonsensical values resembling search modal state under some circumstances. +- Updated dependencies + - @backstage/plugin-search-common@0.3.5-next.1 + - @backstage/core-components@0.9.5-next.2 + +## @backstage/plugin-search-backend@0.5.3-next.2 + +### Patch Changes + +- 7d8acfc32e: `RouterOptions` and `createRouter` now marked as public exports +- Updated dependencies + - @backstage/plugin-search-common@0.3.5-next.1 + - @backstage/backend-common@0.14.0-next.2 + - @backstage/plugin-search-backend-node@0.6.2-next.2 + - @backstage/plugin-auth-node@0.2.2-next.2 + - @backstage/plugin-permission-node@0.6.2-next.2 + +## @backstage/plugin-search-backend-module-elasticsearch@0.1.5-next.2 + +### Patch Changes + +- 7d8acfc32e: Additional types now exported publicly: + + - ElasticSearchAgentOptions + - ElasticSearchConcreteQuery + - ElasticSearchQueryTranslator + - ElasticSearchConnectionConstructor, + - ElasticSearchTransportConstructor, + - ElasticSearchNodeOptions, + - ElasticSearchOptions, + - ElasticSearchAuth, + +- Updated dependencies + - @backstage/plugin-search-common@0.3.5-next.1 + - @backstage/plugin-search-backend-node@0.6.2-next.2 + +## @backstage/plugin-search-backend-module-pg@0.3.4-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-common@0.3.5-next.1 + - @backstage/backend-common@0.14.0-next.2 + - @backstage/plugin-search-backend-node@0.6.2-next.2 + +## @backstage/plugin-search-backend-node@0.6.2-next.2 + +### Patch Changes + +- 7d8acfc32e: Replaced all `@beta` exports with `@public` exports +- Updated dependencies + - @backstage/plugin-search-common@0.3.5-next.1 + - @backstage/backend-common@0.14.0-next.2 + - @backstage/backend-tasks@0.3.2-next.2 + +## @backstage/plugin-search-common@0.3.5-next.1 + +### Patch Changes + +- 7d8acfc32e: `@beta` exports now replaced with `@public` exports + +## @backstage/plugin-sonarqube@0.3.6-next.2 + +### Patch Changes + +- ee2cd642c5: Updated dependency `rc-progress` to `3.3.3`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.2 + +## @backstage/plugin-tech-insights-backend@0.4.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0-next.2 + - @backstage/backend-tasks@0.3.2-next.2 + - @backstage/plugin-tech-insights-node@0.3.1-next.1 + +## @backstage/plugin-tech-insights-backend-module-jsonfc@0.1.17-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0-next.2 + - @backstage/plugin-tech-insights-node@0.3.1-next.1 + +## @backstage/plugin-tech-insights-node@0.3.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0-next.2 + +## @backstage/plugin-techdocs-backend@1.1.2-next.2 + +### Patch Changes + +- 61fba6e50b: In order to ensure a good, stable TechDocs user experience when running TechDocs with `techdocs.builder` set to `local`, the number of concurrent builds has been limited to 10. Any additional builds requested concurrently will be queued and handled as prior builds complete. In the unlikely event that you need to handle more concurrent builds, consider scaling out your TechDocs backend deployment or using the `external` option for `techdocs.builder`. +- Updated dependencies + - @backstage/plugin-search-common@0.3.5-next.1 + - @backstage/plugin-catalog-common@1.0.3-next.1 + - @backstage/backend-common@0.14.0-next.2 + - @backstage/integration@1.2.1-next.2 + - @backstage/plugin-techdocs-node@1.1.2-next.2 + +## @backstage/plugin-techdocs-node@1.1.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-common@0.3.5-next.1 + - @backstage/backend-common@0.14.0-next.2 + - @backstage/integration@1.2.1-next.2 + +## @backstage/plugin-todo-backend@0.1.30-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0-next.2 + - @backstage/integration@1.2.1-next.2 + +## example-app@0.2.72-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder@1.3.0-next.2 + - @backstage/cli@0.17.2-next.2 + - @backstage/plugin-pagerduty@0.4.0-next.2 + - @backstage/plugin-search-common@0.3.5-next.1 + - @backstage/plugin-cost-insights@0.11.28-next.2 + - @backstage/plugin-catalog-common@1.0.3-next.1 + - @backstage/plugin-kubernetes@0.6.6-next.2 + - @backstage/core-components@0.9.5-next.2 + - @backstage/plugin-search@0.8.2-next.2 + - @backstage/plugin-org@0.5.6-next.2 + +## example-backend@0.2.72-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-backend@1.3.0-next.2 + - @backstage/backend-common@0.14.0-next.2 + - @backstage/plugin-search-backend@0.5.3-next.2 + - @backstage/plugin-auth-backend@0.14.1-next.2 + - @backstage/plugin-search-backend-module-elasticsearch@0.1.5-next.2 + - @backstage/integration@1.2.1-next.2 + - @backstage/plugin-techdocs-backend@1.1.2-next.2 + - @backstage/plugin-search-backend-node@0.6.2-next.2 + - example-app@0.2.72-next.2 + - @backstage/backend-tasks@0.3.2-next.2 + - @backstage/plugin-app-backend@0.3.33-next.2 + - @backstage/plugin-auth-node@0.2.2-next.2 + - @backstage/plugin-azure-devops-backend@0.3.12-next.2 + - @backstage/plugin-badges-backend@0.1.27-next.2 + - @backstage/plugin-catalog-backend@1.2.0-next.2 + - @backstage/plugin-code-coverage-backend@0.1.31-next.2 + - @backstage/plugin-graphql-backend@0.1.23-next.2 + - @backstage/plugin-jenkins-backend@0.1.23-next.2 + - @backstage/plugin-kafka-backend@0.2.26-next.2 + - @backstage/plugin-kubernetes-backend@0.6.0-next.2 + - @backstage/plugin-permission-backend@0.5.8-next.2 + - @backstage/plugin-permission-node@0.6.2-next.2 + - @backstage/plugin-proxy-backend@0.2.27-next.1 + - @backstage/plugin-rollbar-backend@0.1.30-next.2 + - @backstage/plugin-scaffolder-backend-module-rails@0.4.1-next.1 + - @backstage/plugin-search-backend-module-pg@0.3.4-next.2 + - @backstage/plugin-tech-insights-backend@0.4.1-next.2 + - @backstage/plugin-tech-insights-backend-module-jsonfc@0.1.17-next.1 + - @backstage/plugin-tech-insights-node@0.3.1-next.1 + - @backstage/plugin-todo-backend@0.1.30-next.2 + +## @internal/plugin-todo-list-backend@1.0.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0-next.2 + - @backstage/plugin-auth-node@0.2.2-next.2 diff --git a/docs/releases/v1.3.0.md b/docs/releases/v1.3.0.md new file mode 100644 index 0000000000..8014e0a121 --- /dev/null +++ b/docs/releases/v1.3.0.md @@ -0,0 +1,65 @@ +--- +id: v1.3.0 +title: v1.3.0 +description: Backstage Release v1.3.0 +--- + +These are the release notes for the v1.3.0 release of [Backstage](https://backstage.io/). + +A huge thanks to the whole team of maintainers and contributors as well as the amazing Backstage Community for the hard work in getting this release developed and done. + +## Highlights + +### Scaffolder Dry Run and Template Editor + +The scaffolder plugin now has a new template editor in addition to the form editor, which is accessible via the context menu on the top right hand corner of the Create page. It allows you to load a template from a local directory, edit it with a preview, execute it in dry-run mode, and view the results. Note that the [File System Access API](https://developer.mozilla.org/en-US/docs/Web/API/File_System_Access_API) must be supported by your browser for this to be available. + +### TypeScript 4.7 + +The recommended TypeScript version has been bumped to `~4.7.0`, and thatโ€™s what the main Backstage repository uses right now for its builds. Each Backstage project manages their version separately however, so there is no rush or immediate effect on users - you can update the `typescript` dependency in your root `package.json` once you feel ready to do so. + +### Expiring Backend Tokens + +In 1.2 we introduced expiry times for server-to-server authentication tokens issued from the standard `TokenManager`. At that point in time, the expiry was only added to tokens and not yet enforced. In this release however, it is now also enforced, meaning that expired tokens are considered invalid and will be rejected. + +### Discovery providers + +Several new [entity providers](https://backstage.io/docs/features/software-catalog/life-of-an-entity) have been contributed as replacements for their corresponding discovery processors. Entity providers allow for more control and are [recommended](https://backstage.io/docs/features/software-catalog/external-integrations) over their processing counterparts. + +- `AzureDevOpsEntityProvider` as replacement for `AzureDevOpsDiscoveryProcessor`. PR [#11604](https://github.com/backstage/backstage/pull/11604) contributed by [@goenning](https://github.com/goenning) +- `GitlabDiscoveryEntityProvider` as replacement for `GitLabDiscoveryProcessor`. PR [#11886](https://github.com/backstage/backstage/pull/11886) contributed by [@ivangonzalezacuna](https://github.com/ivangonzalezacuna) +- `BitbucketCloudEntityProvider` as a replacement for `BitbucketDiscoveryProcessor` (for Bitbucket Cloud only). PR [#11345](https://github.com/backstage/backstage/pull/11345) contributed by [@pjungermann](โ€‹โ€‹https://github.com/pjungermann) + +### New plugin: Vault + +View secrets from [HashiCorp Vault](https://www.vaultproject.io/) alongside your components. PR [#11423](https://github.com/backstage/backstage/pull/11423) contributed by [@ivangonzalezacuna](https://github.com/ivangonzalezacuna) + +### New plugin: GitHub Pull Requests Board + +GitHub Pull Requests Board Plugin is a board that helps you visualize all open pull requests from all repositories owned by a team, with the main goal of reducing the time from opening a PR to merging it. PR [#11043](https://github.com/backstage/backstage/pull/11043) contributed by [@gregorytalita](https://github.com/gregorytalita) + +### New plugin: Dynatrace + +Displays tracing data from [Dynatrace](https://www.dynatrace.com/) alongside your components. PR [#11754](https://github.com/backstage/backstage/pull/11754) contributed by [@isand3r](https://github.com/isand3r) + +## Security Fixes + +`@backstage/plugin-scaffolder-backend`, please upgrade to the latest version if you are using this module. +`@backstage/plugin-techdocs-node`, please upgrade to the latest version if you are using this module. + +## Upgrade path + +We recommend that you keep your Backstage project up to date with this latest release. For more guidance on how to upgrade, check out the documentation for [keeping Backstage updated](https://backstage.io/docs/getting-started/keeping-backstage-updated). + +## Links and References + +Below you can find a list of links and references to help you learn about and start using this new release. + +- [Backstage official website](https://backstage.io/), [documentation](https://backstage.io/docs/), and [getting started guide](https://backstage.io/docs/getting-started/) +- [GitHub repository](https://github.com/backstage/backstage) +- Backstage's [versioning and support policy](https://backstage.io/docs/overview/versioning-policy) +- [Community Discord](https://discord.gg/bFESRKVt) for discussions and support +- [Changelog](https://github.com/backstage/backstage/tree/master/docs/releases/v1.3.0-changelog.md) +- Backstage [Demos](https://backstage.io/demos), [Blog](https://backstage.io/blog), [Roadmap](https://backstage.io/docs/overview/roadmap), and [Plugins](https://backstage.io/plugins) + +Sign up for our [newsletter](https://mailchi.mp/spotify/backstage-community) if you want to be informed about what is happening in the world of Backstage. diff --git a/microsite/data/on-demand/20220518-01.yaml b/microsite/data/on-demand/20220518-01.yaml index e98c79005a..e8d70df2d9 100644 --- a/microsite/data/on-demand/20220518-01.yaml +++ b/microsite/data/on-demand/20220518-01.yaml @@ -1,9 +1,9 @@ --- title: Adopters Community Sessions date: May 18, 2022 -category: Upcoming +category: Meetup description: Adopters Community Session โœจ. It's the monthly meetup where we all come together to listen to the latest maintainer updates, learn from each other about adopting, share exciting new demos or discuss any relevant topic like developer effectiveness, developer experience, developer portals, etc. -youtubeUrl: https://youtu.be/dEd1fl3wRv +youtubeUrl: https://youtu.be/dEd1fl3wRvo youtubeImgUrl: https://backstage.io/img/b-sessions.png rsvpUrl: https://calendar.google.com/calendar/embed?src=c_qup9gbhn9sqpuao6trttd8mk5s@group.calendar.google.com eventUrl: https://github.com/backstage/community/issues/46 diff --git a/microsite/data/on-demand/20220525.yaml b/microsite/data/on-demand/20220525.yaml new file mode 100644 index 0000000000..f9a3f87a8a --- /dev/null +++ b/microsite/data/on-demand/20220525.yaml @@ -0,0 +1,9 @@ +--- +title: Contributor Community Sessions +date: May 25, 2022 +category: Meetup +description: Join the maintainers and contributors for the Contributor Community Sessions +youtubeUrl: https://youtu.be/neNipVE5ffY +youtubeImgUrl: https://backstage.io/img/b-sessions.png +rsvpUrl: https://calendar.google.com/calendar/embed?src=c_qup9gbhn9sqpuao6trttd8mk5s@group.calendar.google.com +eventUrl: https://github.com/backstage/community/issues/46 diff --git a/microsite/data/on-demand/20220615-01.yaml b/microsite/data/on-demand/20220615-01.yaml new file mode 100644 index 0000000000..8ac660a71c --- /dev/null +++ b/microsite/data/on-demand/20220615-01.yaml @@ -0,0 +1,9 @@ +--- +title: Adopters Community Sessions +date: June 15, 2022 +category: Upcoming +description: Adopters Community Session โœจ. It's the monthly meetup where we all come together to listen to the latest maintainer updates, learn from each other about adopting, share exciting new demos or discuss any relevant topic like developer effectiveness, developer experience, developer portals, etc. +youtubeUrl: https://youtu.be/aKZnjnE5Wy8 +youtubeImgUrl: https://backstage.io/img/b-sessions.png +rsvpUrl: https://calendar.google.com/calendar/embed?src=c_qup9gbhn9sqpuao6trttd8mk5s@group.calendar.google.com +eventUrl: https://github.com/backstage/community/issues/49 diff --git a/microsite/data/on-demand/20220622-1.yaml b/microsite/data/on-demand/20220622-1.yaml new file mode 100644 index 0000000000..0bbd53525a --- /dev/null +++ b/microsite/data/on-demand/20220622-1.yaml @@ -0,0 +1,9 @@ +--- +title: Contributor Community Sessions +date: June 22, 2022 +category: Upcoming +description: Join the maintainers and contributors for the Contributor Community Sessions +youtubeUrl: https://youtu.be/aKZnjnE5Wy8 +youtubeImgUrl: https://backstage.io/img/b-sessions.png +rsvpUrl: https://calendar.google.com/calendar/embed?src=c_qup9gbhn9sqpuao6trttd8mk5s@group.calendar.google.com +eventUrl: https://github.com/backstage/community/issues/49 diff --git a/microsite/data/plugins/cloudify.yaml b/microsite/data/plugins/cloudify.yaml new file mode 100644 index 0000000000..e5bcbe1545 --- /dev/null +++ b/microsite/data/plugins/cloudify.yaml @@ -0,0 +1,9 @@ +--- +title: Cloudify +author: Cloudify +authorUrl: https://cloudify.co/ +category: Orchestration +description: Cloudify provides a remote execution and environment management backend for Kubernetes, Terraform, Ansible, etc. +documentation: https://github.com/cloudify-cosmo/backstage-cloudify-plugin#readme +iconUrl: https://avatars.githubusercontent.com/u/6260555?s=200&v=4 +npmPackageName: 'plugin-cloudify' diff --git a/microsite/data/plugins/dynatrace.yaml b/microsite/data/plugins/dynatrace.yaml new file mode 100644 index 0000000000..3ab85ae1eb --- /dev/null +++ b/microsite/data/plugins/dynatrace.yaml @@ -0,0 +1,12 @@ +--- +title: Dynatrace +author: TELUS +authorUrl: https://github.com/telus +category: Monitoring +description: View monitoring info from dynatrace for services in your software catalog. +documentation: https://github.com/backstage/backstage/tree/master/plugins/dynatrace +iconUrl: img/dynatrace.svg +npmPackageName: '@backstage/plugin-dynatrace' +tags: + - dynatrace + - monitoring diff --git a/microsite/data/plugins/github-pull-requests-board.yaml b/microsite/data/plugins/github-pull-requests-board.yaml new file mode 100644 index 0000000000..fa710f5fc8 --- /dev/null +++ b/microsite/data/plugins/github-pull-requests-board.yaml @@ -0,0 +1,9 @@ +--- +title: Github Pull Requests Board +author: DAZN +authorUrl: https://engineering.dazn.com +category: Source Control Mgmt +description: View all open GitHub pull requests owned by your team in Backstage. +documentation: https://github.com/backstage/backstage/tree/master/plugins/github-pull-requests-board +iconUrl: img/github-pull-requests-board-logo.svg +npmPackageName: '@backstage/plugin-github-pull-requests-board' diff --git a/microsite/data/plugins/grpc-playground.yaml b/microsite/data/plugins/grpc-playground.yaml new file mode 100644 index 0000000000..097759be11 --- /dev/null +++ b/microsite/data/plugins/grpc-playground.yaml @@ -0,0 +1,9 @@ +--- +title: GRPC Playground +author: Zalopay-OSS +authorUrl: https://github.com/zalopay-oss/ +category: Discovery +description: Easily view and test your gRPC API with a GUI Client, inspired from BloomRPC application. +documentation: https://github.com/zalopay-oss/backstage-grpc-playground +iconUrl: https://raw.githubusercontent.com/zalopay-oss/backstage-grpc-playground/main/images/gprc-logo.png +npmPackageName: 'backstage-grpc-playground' diff --git a/microsite/data/plugins/rootly.yaml b/microsite/data/plugins/rootly.yaml new file mode 100644 index 0000000000..c1769f637a --- /dev/null +++ b/microsite/data/plugins/rootly.yaml @@ -0,0 +1,9 @@ +--- +title: Rootly +author: Rootly +authorUrl: https://rootly.com/ +category: Incident Management +description: Import your Backstage entities into Rootly services and view incidents & analytics. +documentation: https://github.com/rootlyhq/backstage-plugin/blob/master/README.md +iconUrl: https://raw.githubusercontent.com/rootlyhq/backstage-plugin/master/docs/logo.png +npmPackageName: '@rootly/backstage-plugin' diff --git a/microsite/data/plugins/stack-overflow.yaml b/microsite/data/plugins/stack-overflow.yaml new file mode 100644 index 0000000000..0d734446d7 --- /dev/null +++ b/microsite/data/plugins/stack-overflow.yaml @@ -0,0 +1,9 @@ +--- +title: Stack Overflow +author: Spotify +authorUrl: https://github.com/spotify +category: Discovery +description: Provides Stack Overflow specific functionality that can be used in different ways (e.g. for homepage and search) to compose your Backstage App. +documentation: https://github.com/backstage/backstage/blob/master/plugins/stack-overflow +iconUrl: img/stack-overflow-logo.svg +npmPackageName: '@backstage/plugin-stack-overflow' diff --git a/microsite/data/plugins/vault.yaml b/microsite/data/plugins/vault.yaml new file mode 100644 index 0000000000..3214cf669a --- /dev/null +++ b/microsite/data/plugins/vault.yaml @@ -0,0 +1,11 @@ +--- +title: Vault +author: Spread Group +authorUrl: https://github.com/ivangonzalezacuna +category: Vault +description: Visualize a list secrets stored in your vault instance. +documentation: https://github.com/backstage/backstage/tree/master/plugins/vault +iconUrl: img/vault.png +npmPackageName: '@backstage/plugin-vault' +tags: + - vault diff --git a/microsite/pages/en/link.js b/microsite/pages/en/link.js index 727b15bfd8..6f56aa768c 100644 --- a/microsite/pages/en/link.js +++ b/microsite/pages/en/link.js @@ -5,7 +5,7 @@ const React = require('react'); const redirects = { 'bind-routes': '/docs/plugins/composability#binding-external-routes-in-the-app', - 'scm-auth': '/docs/auth/#custom-scmauthapi-implementation', + 'scm-auth': '/docs/auth/#scaffolder-configuration-software-templates', }; const fallback = '/docs'; diff --git a/microsite/pages/en/live.js b/microsite/pages/en/live.js index 9f8a0e24b0..062af419fc 100644 --- a/microsite/pages/en/live.js +++ b/microsite/pages/en/live.js @@ -34,7 +34,7 @@ const Background = props => { diff --git a/microsite/sidebars.json b/microsite/sidebars.json index a8dc4e53ee..29fb3a0534 100644 --- a/microsite/sidebars.json +++ b/microsite/sidebars.json @@ -1,6 +1,11 @@ { "releases": { - "Release Notes": ["releases/v1.2.0", "releases/v1.1.0", "releases/v1.0.0"] + "Release Notes": [ + "releases/v1.3.0", + "releases/v1.2.0", + "releases/v1.1.0", + "releases/v1.0.0" + ] }, "docs": { "Overview": [ @@ -149,6 +154,14 @@ "integrations/bitbucket/discovery" ] }, + { + "type": "subcategory", + "label": "Bitbucket Cloud", + "ids": [ + "integrations/bitbucketCloud/locations", + "integrations/bitbucketCloud/discovery" + ] + }, { "type": "subcategory", "label": "Datadog", @@ -198,6 +211,7 @@ "plugins/plugin-development", "plugins/structure-of-a-plugin", "plugins/integrating-plugin-into-software-catalog", + "plugins/integrating-search-into-plugins", "plugins/composability", "plugins/analytics", { @@ -250,28 +264,27 @@ "auth/oauth2-proxy/provider" ] }, - "auth/add-auth-provider", "auth/identity-resolver", "auth/auth-backend", "auth/oauth", - "auth/auth-backend-classes", + "auth/add-auth-provider", "auth/troubleshooting", "auth/glossary" ], "Permissions": [ - "permission/overview", - "permission/concepts", - "permission/getting-started", - "permission/writing-a-policy", - "permission/custom-rules", + "permissions/overview", + "permissions/concepts", + "permissions/getting-started", + "permissions/writing-a-policy", + "permissions/custom-rules", { "type": "subcategory", "label": "Tutorial: using Permissions in your plugin", "ids": [ - "permission/plugin-authors/01-setup", - "permission/plugin-authors/02-adding-a-basic-permission-check", - "permission/plugin-authors/03-adding-a-resource-permission-check", - "permission/plugin-authors/04-authorizing-access-to-paginated-data" + "permissions/plugin-authors/01-setup", + "permissions/plugin-authors/02-adding-a-basic-permission-check", + "permissions/plugin-authors/03-adding-a-resource-permission-check", + "permissions/plugin-authors/04-authorizing-access-to-paginated-data" ] } ], @@ -279,7 +292,6 @@ "deployment/index", "deployment/docker", "deployment/k8s", - "deployment/helm", "deployment/heroku" ], "Designing for Backstage": [ diff --git a/microsite/static/img/dynatrace.svg b/microsite/static/img/dynatrace.svg new file mode 100644 index 0000000000..de114c0005 --- /dev/null +++ b/microsite/static/img/dynatrace.svg @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/microsite/static/img/github-pull-requests-board-logo.svg b/microsite/static/img/github-pull-requests-board-logo.svg new file mode 100644 index 0000000000..0114b50d08 --- /dev/null +++ b/microsite/static/img/github-pull-requests-board-logo.svg @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/microsite/static/img/stack-overflow-logo.svg b/microsite/static/img/stack-overflow-logo.svg new file mode 100644 index 0000000000..d2fba99094 --- /dev/null +++ b/microsite/static/img/stack-overflow-logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/microsite/static/img/vault.png b/microsite/static/img/vault.png new file mode 100644 index 0000000000..9b5d59622d Binary files /dev/null and b/microsite/static/img/vault.png differ diff --git a/microsite/yarn.lock b/microsite/yarn.lock index 448647da15..f4831e2b42 100644 --- a/microsite/yarn.lock +++ b/microsite/yarn.lock @@ -2477,9 +2477,9 @@ etag@~1.8.1: integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= eventsource@^1.0.7: - version "1.0.7" - resolved "https://registry.npmjs.org/eventsource/-/eventsource-1.0.7.tgz#8fbc72c93fcd34088090bc0a4e64f4b5cee6d8d0" - integrity sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ== + version "1.1.1" + resolved "https://registry.npmjs.org/eventsource/-/eventsource-1.1.1.tgz#4544a35a57d7120fba4fa4c86cb4023b2c09df2f" + integrity sha512-qV5ZC0h7jYIAOhArFJgSfdyz6rALJyb270714o7ZtNnw2WSJ+eexhKtE0O8LYPRsHZHf2osHKZBxGPvm3kPkCA== dependencies: original "^1.0.0" @@ -5210,9 +5210,9 @@ prepend-http@^2.0.0: integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= prettier@^2.6.2: - version "2.6.2" - resolved "https://registry.npmjs.org/prettier/-/prettier-2.6.2.tgz#e26d71a18a74c3d0f0597f55f01fb6c06c206032" - integrity sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew== + version "2.7.1" + resolved "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz#e235806850d057f97bb08368a4f7d899f7760c64" + integrity sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g== prismjs@^1.22.0: version "1.27.0" diff --git a/mkdocs.yml b/mkdocs.yml index caaee37f29..04d62d32d4 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -97,6 +97,9 @@ nav: - Bitbucket: - Locations: 'integrations/bitbucket/locations.md' - Discovery: 'integrations/bitbucket/discovery.md' + - Bitbucket Cloud: + - Locations: 'integrations/bitbucketCloud/locations.md' + - Discovery: 'integrations/bitbucketCloud/discovery.md' - Datadog: - Installation: 'integrations/datadog-rum/installation.md' - Gerrit: @@ -153,18 +156,16 @@ nav: - OneLogin: 'auth/onelogin/provider.md' - OAuth2Proxy: 'auth/oauth2-proxy/provider.md' - Bitbucket: 'auth/bitbucket/provider.md' - - Adding authentication providers: 'auth/add-auth-provider.md' - Sign in resolvers: 'auth/identity-resolver.md' - Auth backend: 'auth/auth-backend.md' - OAuth and OpenID Connect: 'auth/oauth.md' - - Auth backend classes: 'auth/auth-backend-classes.md' + - Contributing New Providers: 'auth/add-auth-provider.md' - Troubleshooting Auth: 'auth/troubleshooting.md' - Glossary: 'auth/glossary.md' - Deployment: - Deploying Backstage: 'deployment/index.md' - Docker: 'deployment/docker.md' - Kubernetes: 'deployment/k8s.md' - - Helm: 'deployment/helm.md' - Heroku: 'deployment/heroku.md' - Designing for Backstage: - Design: 'dls/design.md' diff --git a/package.json b/package.json index 7752322535..dd2a85e757 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "@types/react": "^17", "@types/react-dom": "^17" }, - "version": "1.2.0", + "version": "1.3.0", "dependencies": { "@manypkg/get-packages": "^1.1.3", "@microsoft/api-documenter": "^7.17.11", @@ -69,14 +69,14 @@ "eslint-plugin-notice": "^0.9.10", "fs-extra": "10.1.0", "husky": "^8.0.0", - "lerna": "^4.0.0", - "lint-staged": "^12.2.0", + "lerna": "^5.0.0", + "lint-staged": "^13.0.0", "minimist": "^1.2.5", "prettier": "^2.2.1", "semver": "^7.3.2", "shx": "^0.3.2", "ts-node": "^10.4.0", - "typescript": "~4.6.4", + "typescript": "~4.7.0", "yarn-lock-check": "^1.0.5" }, "prettier": "@spotify/prettier-config", diff --git a/packages/app-defaults/CHANGELOG.md b/packages/app-defaults/CHANGELOG.md index b0534e7d84..fb1756eba2 100644 --- a/packages/app-defaults/CHANGELOG.md +++ b/packages/app-defaults/CHANGELOG.md @@ -1,5 +1,32 @@ # @backstage/app-defaults +## 1.0.3 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.9.5 + - @backstage/core-app-api@1.0.3 + - @backstage/core-plugin-api@1.0.3 + - @backstage/plugin-permission-react@0.4.2 + +## 1.0.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-app-api@1.0.3-next.0 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/plugin-permission-react@0.4.2-next.0 + +## 1.0.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.9.5-next.0 + ## 1.0.2 ### Patch Changes diff --git a/packages/app-defaults/package.json b/packages/app-defaults/package.json index 4d931d9560..0a35b16c3a 100644 --- a/packages/app-defaults/package.json +++ b/packages/app-defaults/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/app-defaults", "description": "Provides the default wiring of a Backstage App", - "version": "1.0.2", + "version": "1.0.3", "private": false, "publishConfig": { "access": "public", @@ -33,10 +33,10 @@ "start": "backstage-cli package start" }, "dependencies": { - "@backstage/core-components": "^0.9.4", - "@backstage/core-app-api": "^1.0.2", - "@backstage/core-plugin-api": "^1.0.2", - "@backstage/plugin-permission-react": "^0.4.1", + "@backstage/core-components": "^0.9.5", + "@backstage/core-app-api": "^1.0.3", + "@backstage/core-plugin-api": "^1.0.3", + "@backstage/plugin-permission-react": "^0.4.2", "@backstage/theme": "^0.2.15", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -46,8 +46,8 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/test-utils": "^1.1.0", + "@backstage/cli": "^0.17.2", + "@backstage/test-utils": "^1.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@types/jest": "^26.0.7", diff --git a/packages/app/CHANGELOG.md b/packages/app/CHANGELOG.md index 9d30db9dbc..74601b6417 100644 --- a/packages/app/CHANGELOG.md +++ b/packages/app/CHANGELOG.md @@ -1,5 +1,189 @@ # example-app +## 0.2.72 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-kubernetes@0.6.6 + - @backstage/plugin-cost-insights@0.11.28 + - @backstage/plugin-scaffolder@1.3.0 + - @backstage/plugin-catalog@1.3.0 + - @backstage/plugin-techdocs@1.2.0 + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/cli@0.17.2 + - @backstage/plugin-user-settings@0.4.5 + - @backstage/plugin-pagerduty@0.4.0 + - @backstage/plugin-search-common@0.3.5 + - @backstage/plugin-search@0.9.0 + - @backstage/plugin-search-react@0.2.1 + - @backstage/plugin-sentry@0.3.44 + - @backstage/plugin-tech-insights@0.2.2 + - @backstage/plugin-catalog-common@1.0.3 + - @backstage/core-components@0.9.5 + - @backstage/plugin-dynatrace@0.1.0 + - @backstage/plugin-tech-radar@0.5.13 + - @backstage/core-app-api@1.0.3 + - @backstage/core-plugin-api@1.0.3 + - @backstage/integration-react@1.1.1 + - @backstage/plugin-airbrake@0.3.6 + - @backstage/plugin-apache-airflow@0.1.14 + - @backstage/plugin-api-docs@0.8.6 + - @backstage/plugin-azure-devops@0.1.22 + - @backstage/plugin-badges@0.2.30 + - @backstage/plugin-catalog-import@0.8.9 + - @backstage/plugin-circleci@0.3.6 + - @backstage/plugin-cloudbuild@0.3.6 + - @backstage/plugin-code-coverage@0.1.33 + - @backstage/plugin-explore@0.3.37 + - @backstage/plugin-gcalendar@0.3.2 + - @backstage/plugin-gcp-projects@0.3.25 + - @backstage/plugin-github-actions@0.5.6 + - @backstage/plugin-gocd@0.1.12 + - @backstage/plugin-graphiql@0.2.38 + - @backstage/plugin-home@0.4.22 + - @backstage/plugin-jenkins@0.7.5 + - @backstage/plugin-kafka@0.3.6 + - @backstage/plugin-lighthouse@0.3.6 + - @backstage/plugin-newrelic@0.3.24 + - @backstage/plugin-org@0.5.6 + - @backstage/plugin-rollbar@0.4.6 + - @backstage/plugin-shortcuts@0.2.7 + - @backstage/plugin-stack-overflow@0.1.2 + - @backstage/plugin-techdocs-module-addons-contrib@1.0.1 + - @backstage/plugin-todo@0.2.8 + - @backstage/catalog-model@1.0.3 + - @backstage/plugin-techdocs-react@1.0.1 + - @backstage/app-defaults@1.0.3 + - @backstage/plugin-catalog-graph@0.2.18 + - @backstage/plugin-newrelic-dashboard@0.1.14 + - @backstage/plugin-permission-react@0.4.2 + +## 0.2.72-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder@1.3.0-next.2 + - @backstage/cli@0.17.2-next.2 + - @backstage/plugin-pagerduty@0.4.0-next.2 + - @backstage/plugin-search-common@0.3.5-next.1 + - @backstage/plugin-cost-insights@0.11.28-next.2 + - @backstage/plugin-catalog-common@1.0.3-next.1 + - @backstage/plugin-kubernetes@0.6.6-next.2 + - @backstage/core-components@0.9.5-next.2 + - @backstage/plugin-search@0.8.2-next.2 + - @backstage/plugin-org@0.5.6-next.2 + +## 0.2.72-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-sentry@0.3.44-next.1 + - @backstage/plugin-kubernetes@0.6.6-next.1 + - @backstage/plugin-tech-insights@0.2.2-next.1 + - @backstage/plugin-cost-insights@0.11.28-next.1 + - @backstage/core-components@0.9.5-next.1 + - @backstage/cli@0.17.2-next.1 + - @backstage/core-app-api@1.0.3-next.0 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/integration-react@1.1.1-next.1 + - @backstage/plugin-airbrake@0.3.6-next.1 + - @backstage/plugin-apache-airflow@0.1.14-next.1 + - @backstage/plugin-api-docs@0.8.6-next.1 + - @backstage/plugin-azure-devops@0.1.22-next.1 + - @backstage/plugin-badges@0.2.30-next.1 + - @backstage/plugin-catalog-import@0.8.9-next.1 + - @backstage/plugin-circleci@0.3.6-next.1 + - @backstage/plugin-cloudbuild@0.3.6-next.1 + - @backstage/plugin-code-coverage@0.1.33-next.1 + - @backstage/plugin-explore@0.3.37-next.1 + - @backstage/plugin-gcalendar@0.3.2-next.1 + - @backstage/plugin-gcp-projects@0.3.25-next.1 + - @backstage/plugin-github-actions@0.5.6-next.1 + - @backstage/plugin-gocd@0.1.12-next.1 + - @backstage/plugin-graphiql@0.2.38-next.1 + - @backstage/plugin-home@0.4.22-next.1 + - @backstage/plugin-jenkins@0.7.5-next.1 + - @backstage/plugin-kafka@0.3.6-next.1 + - @backstage/plugin-lighthouse@0.3.6-next.1 + - @backstage/plugin-newrelic@0.3.24-next.1 + - @backstage/plugin-org@0.5.6-next.1 + - @backstage/plugin-pagerduty@0.3.33-next.1 + - @backstage/plugin-rollbar@0.4.6-next.1 + - @backstage/plugin-scaffolder@1.3.0-next.1 + - @backstage/plugin-search@0.8.2-next.1 + - @backstage/plugin-shortcuts@0.2.7-next.1 + - @backstage/plugin-stack-overflow@0.1.2-next.1 + - @backstage/plugin-tech-radar@0.5.13-next.1 + - @backstage/plugin-techdocs-module-addons-contrib@1.0.1-next.1 + - @backstage/plugin-techdocs@1.1.2-next.1 + - @backstage/plugin-todo@0.2.8-next.1 + - @backstage/plugin-user-settings@0.4.5-next.1 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog@1.2.1-next.1 + - @backstage/plugin-catalog-graph@0.2.18-next.1 + - @backstage/plugin-catalog-react@1.1.1-next.1 + - @backstage/app-defaults@1.0.3-next.1 + - @backstage/plugin-search-react@0.2.1-next.0 + - @backstage/plugin-newrelic-dashboard@0.1.14-next.1 + - @backstage/plugin-permission-react@0.4.2-next.0 + - @backstage/plugin-techdocs-react@1.0.1-next.1 + - @backstage/plugin-catalog-common@1.0.3-next.0 + - @backstage/plugin-search-common@0.3.5-next.0 + +## 0.2.72-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-kubernetes@0.6.6-next.0 + - @backstage/plugin-cost-insights@0.11.28-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/plugin-catalog@1.2.1-next.0 + - @backstage/cli@0.17.2-next.0 + - @backstage/plugin-pagerduty@0.3.33-next.0 + - @backstage/core-components@0.9.5-next.0 + - @backstage/plugin-gcp-projects@0.3.25-next.0 + - @backstage/plugin-techdocs-module-addons-contrib@1.0.1-next.0 + - @backstage/plugin-scaffolder@1.3.0-next.0 + - @backstage/plugin-techdocs@1.1.2-next.0 + - @backstage/plugin-techdocs-react@1.0.1-next.0 + - @backstage/plugin-airbrake@0.3.6-next.0 + - @backstage/plugin-api-docs@0.8.6-next.0 + - @backstage/plugin-azure-devops@0.1.22-next.0 + - @backstage/plugin-badges@0.2.30-next.0 + - @backstage/plugin-catalog-graph@0.2.18-next.0 + - @backstage/plugin-catalog-import@0.8.9-next.0 + - @backstage/plugin-circleci@0.3.6-next.0 + - @backstage/plugin-cloudbuild@0.3.6-next.0 + - @backstage/plugin-code-coverage@0.1.33-next.0 + - @backstage/plugin-explore@0.3.37-next.0 + - @backstage/plugin-github-actions@0.5.6-next.0 + - @backstage/plugin-gocd@0.1.12-next.0 + - @backstage/plugin-home@0.4.22-next.0 + - @backstage/plugin-jenkins@0.7.5-next.0 + - @backstage/plugin-kafka@0.3.6-next.0 + - @backstage/plugin-lighthouse@0.3.6-next.0 + - @backstage/plugin-newrelic-dashboard@0.1.14-next.0 + - @backstage/plugin-org@0.5.6-next.0 + - @backstage/plugin-rollbar@0.4.6-next.0 + - @backstage/plugin-search@0.8.2-next.0 + - @backstage/plugin-sentry@0.3.44-next.0 + - @backstage/plugin-tech-insights@0.2.2-next.0 + - @backstage/plugin-todo@0.2.8-next.0 + - @backstage/app-defaults@1.0.3-next.0 + - @backstage/integration-react@1.1.1-next.0 + - @backstage/plugin-apache-airflow@0.1.14-next.0 + - @backstage/plugin-gcalendar@0.3.2-next.0 + - @backstage/plugin-graphiql@0.2.38-next.0 + - @backstage/plugin-newrelic@0.3.24-next.0 + - @backstage/plugin-shortcuts@0.2.7-next.0 + - @backstage/plugin-stack-overflow@0.1.2-next.0 + - @backstage/plugin-tech-radar@0.5.13-next.0 + - @backstage/plugin-user-settings@0.4.5-next.0 + ## 0.2.71 ### Patch Changes diff --git a/packages/app/package.json b/packages/app/package.json index b94b4eb6d4..df9119d602 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -1,65 +1,66 @@ { "name": "example-app", - "version": "0.2.71", + "version": "0.2.72", "private": true, "backstage": { "role": "frontend" }, "bundled": true, "dependencies": { - "@backstage/app-defaults": "^1.0.2", - "@backstage/catalog-model": "^1.0.2", - "@backstage/cli": "^0.17.1", + "@backstage/app-defaults": "^1.0.3", + "@backstage/catalog-model": "^1.0.3", + "@backstage/cli": "^0.17.2", "@backstage/config": "^1.0.1", - "@backstage/core-app-api": "^1.0.2", - "@backstage/core-components": "^0.9.4", - "@backstage/core-plugin-api": "^1.0.2", - "@backstage/integration-react": "^1.1.0", - "@backstage/plugin-airbrake": "^0.3.5", - "@backstage/plugin-api-docs": "^0.8.5", - "@backstage/plugin-azure-devops": "^0.1.21", - "@backstage/plugin-apache-airflow": "^0.1.13", - "@backstage/plugin-badges": "^0.2.29", - "@backstage/plugin-catalog": "^1.2.0", - "@backstage/plugin-catalog-common": "^1.0.2", - "@backstage/plugin-catalog-graph": "^0.2.17", - "@backstage/plugin-catalog-import": "^0.8.8", - "@backstage/plugin-catalog-react": "^1.1.0", - "@backstage/plugin-circleci": "^0.3.5", - "@backstage/plugin-cloudbuild": "^0.3.5", - "@backstage/plugin-code-coverage": "^0.1.32", - "@backstage/plugin-cost-insights": "^0.11.27", - "@backstage/plugin-explore": "^0.3.36", - "@backstage/plugin-gcalendar": "^0.3.1", - "@backstage/plugin-gcp-projects": "^0.3.24", - "@backstage/plugin-github-actions": "^0.5.5", - "@backstage/plugin-gocd": "^0.1.11", - "@backstage/plugin-graphiql": "^0.2.37", - "@backstage/plugin-home": "^0.4.21", - "@backstage/plugin-jenkins": "^0.7.4", - "@backstage/plugin-kafka": "^0.3.5", - "@backstage/plugin-kubernetes": "^0.6.5", - "@backstage/plugin-lighthouse": "^0.3.5", - "@backstage/plugin-newrelic": "^0.3.23", - "@backstage/plugin-newrelic-dashboard": "^0.1.13", - "@backstage/plugin-org": "^0.5.5", - "@backstage/plugin-pagerduty": "0.3.32", - "@backstage/plugin-permission-react": "^0.4.1", - "@backstage/plugin-rollbar": "^0.4.5", - "@backstage/plugin-scaffolder": "^1.2.0", - "@backstage/plugin-search": "^0.8.1", - "@backstage/plugin-search-react": "^0.2.0", - "@backstage/plugin-search-common": "^0.3.4", - "@backstage/plugin-sentry": "^0.3.43", - "@backstage/plugin-shortcuts": "^0.2.6", - "@backstage/plugin-stack-overflow": "^0.1.1", - "@backstage/plugin-tech-radar": "^0.5.12", - "@backstage/plugin-techdocs": "^1.1.1", - "@backstage/plugin-techdocs-react": "^1.0.0", - "@backstage/plugin-techdocs-module-addons-contrib": "^1.0.0", - "@backstage/plugin-todo": "^0.2.7", - "@backstage/plugin-user-settings": "^0.4.4", - "@backstage/plugin-tech-insights": "^0.2.1", + "@backstage/core-app-api": "^1.0.3", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", + "@backstage/integration-react": "^1.1.1", + "@backstage/plugin-airbrake": "^0.3.6", + "@backstage/plugin-api-docs": "^0.8.6", + "@backstage/plugin-azure-devops": "^0.1.22", + "@backstage/plugin-apache-airflow": "^0.1.14", + "@backstage/plugin-badges": "^0.2.30", + "@backstage/plugin-catalog": "^1.3.0", + "@backstage/plugin-catalog-common": "^1.0.3", + "@backstage/plugin-catalog-graph": "^0.2.18", + "@backstage/plugin-catalog-import": "^0.8.9", + "@backstage/plugin-catalog-react": "^1.1.1", + "@backstage/plugin-circleci": "^0.3.6", + "@backstage/plugin-cloudbuild": "^0.3.6", + "@backstage/plugin-code-coverage": "^0.1.33", + "@backstage/plugin-cost-insights": "^0.11.28", + "@backstage/plugin-dynatrace": "^0.1.0", + "@backstage/plugin-explore": "^0.3.37", + "@backstage/plugin-gcalendar": "^0.3.2", + "@backstage/plugin-gcp-projects": "^0.3.25", + "@backstage/plugin-github-actions": "^0.5.6", + "@backstage/plugin-gocd": "^0.1.12", + "@backstage/plugin-graphiql": "^0.2.38", + "@backstage/plugin-home": "^0.4.22", + "@backstage/plugin-jenkins": "^0.7.5", + "@backstage/plugin-kafka": "^0.3.6", + "@backstage/plugin-kubernetes": "^0.6.6", + "@backstage/plugin-lighthouse": "^0.3.6", + "@backstage/plugin-newrelic": "^0.3.24", + "@backstage/plugin-newrelic-dashboard": "^0.1.14", + "@backstage/plugin-org": "^0.5.6", + "@backstage/plugin-pagerduty": "0.4.0", + "@backstage/plugin-permission-react": "^0.4.2", + "@backstage/plugin-rollbar": "^0.4.6", + "@backstage/plugin-scaffolder": "^1.3.0", + "@backstage/plugin-search": "^0.9.0", + "@backstage/plugin-search-react": "^0.2.1", + "@backstage/plugin-search-common": "^0.3.5", + "@backstage/plugin-sentry": "^0.3.44", + "@backstage/plugin-shortcuts": "^0.2.7", + "@backstage/plugin-stack-overflow": "^0.1.2", + "@backstage/plugin-tech-radar": "^0.5.13", + "@backstage/plugin-techdocs": "^1.2.0", + "@backstage/plugin-techdocs-react": "^1.0.1", + "@backstage/plugin-techdocs-module-addons-contrib": "^1.0.1", + "@backstage/plugin-todo": "^0.2.8", + "@backstage/plugin-user-settings": "^0.4.5", + "@backstage/plugin-tech-insights": "^0.2.2", "@backstage/theme": "^0.2.15", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -80,7 +81,7 @@ "zen-observable": "^0.8.15" }, "devDependencies": { - "@backstage/test-utils": "^1.1.0", + "@backstage/test-utils": "^1.1.1", "@rjsf/core": "^3.2.1", "@testing-library/cypress": "^8.0.2", "@testing-library/jest-dom": "^5.10.1", @@ -92,7 +93,7 @@ "@types/react-dom": "*", "@types/zen-observable": "^0.8.0", "cross-env": "^7.0.0", - "cypress": "^9.5.0", + "cypress": "^10.0.0", "eslint-plugin-cypress": "^2.10.3", "start-server-and-test": "^1.10.11" }, diff --git a/packages/app/src/components/Root/Root.tsx b/packages/app/src/components/Root/Root.tsx index ac62c81899..30da01841c 100644 --- a/packages/app/src/components/Root/Root.tsx +++ b/packages/app/src/components/Root/Root.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import React, { useContext, PropsWithChildren } from 'react'; +import React, { PropsWithChildren } from 'react'; import { Link, makeStyles } from '@material-ui/core'; import HomeIcon from '@material-ui/icons/Home'; import ExtensionIcon from '@material-ui/icons/Extension'; @@ -39,13 +39,13 @@ import { Shortcuts } from '@backstage/plugin-shortcuts'; import { Sidebar, sidebarConfig, - SidebarContext, SidebarDivider, SidebarGroup, SidebarItem, SidebarPage, SidebarScrollWrapper, SidebarSpace, + useSidebarOpenState, } from '@backstage/core-components'; import { MyGroupsSidebarItem } from '@backstage/plugin-org'; import GroupIcon from '@material-ui/icons/People'; @@ -68,7 +68,7 @@ const useSidebarLogoStyles = makeStyles({ const SidebarLogo = () => { const classes = useSidebarLogoStyles(); - const { isOpen } = useContext(SidebarContext); + const { isOpen } = useSidebarOpenState(); return (
diff --git a/packages/app/src/components/catalog/EntityPage.tsx b/packages/app/src/components/catalog/EntityPage.tsx index 22d6cc3ab1..1958aabd2e 100644 --- a/packages/app/src/components/catalog/EntityPage.tsx +++ b/packages/app/src/components/catalog/EntityPage.tsx @@ -73,6 +73,10 @@ import { isCloudbuildAvailable, } from '@backstage/plugin-cloudbuild'; import { EntityCodeCoverageContent } from '@backstage/plugin-code-coverage'; +import { + DynatraceTab, + isDynatraceAvailable, +} from '@backstage/plugin-dynatrace'; import { EntityGithubActionsContent, EntityRecentGithubActionsRunsCard, @@ -140,6 +144,12 @@ import { EntityGoCdContent, isGoCdAvailable } from '@backstage/plugin-gocd'; import React, { ReactNode, useMemo, useState } from 'react'; +import { TechDocsAddons } from '@backstage/plugin-techdocs-react'; +import { + TextSize, + ReportIssue, +} from '@backstage/plugin-techdocs-module-addons-contrib'; + const customEntityFilterKind = ['Component', 'API', 'System']; const EntityLayoutWrapper = (props: { children?: ReactNode }) => { @@ -168,6 +178,15 @@ const EntityLayoutWrapper = (props: { children?: ReactNode }) => { ); }; +const techdocsContent = ( + + + + + + +); + /** * NOTE: This page is designed to work on small screens such as mobile devices. * This is based on Material UI Grid. If breakpoints are used, each grid item must set the `xs` prop to a column size or to `true`, @@ -398,7 +417,7 @@ const serviceEntityPage = ( - + {techdocsContent} + + + + ); @@ -465,8 +492,9 @@ const websiteEntityPage = ( - + {techdocsContent} + + + + + - + {techdocsContent} diff --git a/packages/app/src/components/home/templates/DefaultTemplate.stories.tsx b/packages/app/src/components/home/templates/DefaultTemplate.stories.tsx index 8b9c5100b1..638fe8bc29 100644 --- a/packages/app/src/components/home/templates/DefaultTemplate.stories.tsx +++ b/packages/app/src/components/home/templates/DefaultTemplate.stories.tsx @@ -27,6 +27,7 @@ import { starredEntitiesApiRef, MockStarredEntitiesApi, entityRouteRef, + catalogApiRef, } from '@backstage/plugin-catalog-react'; import { configApiRef } from '@backstage/core-plugin-api'; import { ConfigReader } from '@backstage/config'; @@ -39,6 +40,45 @@ import { HomePageStackOverflowQuestions } from '@backstage/plugin-stack-overflow import { Grid, makeStyles } from '@material-ui/core'; import React, { ComponentType } from 'react'; +const entities = [ + { + apiVersion: '1', + kind: 'Component', + metadata: { + name: 'mock-starred-entity', + title: 'Mock Starred Entity!', + }, + }, + { + apiVersion: '1', + kind: 'Component', + metadata: { + name: 'mock-starred-entity-2', + title: 'Mock Starred Entity 2!', + }, + }, + { + apiVersion: '1', + kind: 'Component', + metadata: { + name: 'mock-starred-entity-3', + title: 'Mock Starred Entity 3!', + }, + }, + { + apiVersion: '1', + kind: 'Component', + metadata: { + name: 'mock-starred-entity-4', + title: 'Mock Starred Entity 4!', + }, + }, +]; + +const mockCatalogApi = { + getEntities: async () => ({ items: entities }), +}; + const starredEntitiesApi = new MockStarredEntitiesApi(); starredEntitiesApi.toggleStarred('component:default/example-starred-entity'); starredEntitiesApi.toggleStarred('component:default/example-starred-entity-2'); @@ -53,6 +93,7 @@ export default { <> Promise.resolve({ results: [] }) }], [ diff --git a/packages/app/src/components/search/SearchModal.tsx b/packages/app/src/components/search/SearchModal.tsx index 3fdacf08b6..99fc964cb2 100644 --- a/packages/app/src/components/search/SearchModal.tsx +++ b/packages/app/src/components/search/SearchModal.tsx @@ -26,23 +26,27 @@ import { useTheme, } from '@material-ui/core'; import LaunchIcon from '@material-ui/icons/Launch'; -import { Link, useContent } from '@backstage/core-components'; +import { + CatalogIcon, + DocsIcon, + Link, + useContent, +} from '@backstage/core-components'; import { useApi, useRouteRef } from '@backstage/core-plugin-api'; import { CatalogSearchResultListItem } from '@backstage/plugin-catalog'; import { catalogApiRef, CATALOG_FILTER_EXISTS, } from '@backstage/plugin-catalog-react'; +import { searchPlugin, SearchType } from '@backstage/plugin-search'; import { DefaultResultListItem, - SearchBar, SearchFilter, - searchPlugin, + SearchBar, SearchResult, SearchResultPager, - SearchType, -} from '@backstage/plugin-search'; -import { useSearch } from '@backstage/plugin-search-react'; + useSearch, +} from '@backstage/plugin-search-react'; import { TechDocsSearchResultListItem } from '@backstage/plugin-techdocs'; const useStyles = makeStyles(theme => ({ @@ -180,24 +184,28 @@ export const SearchModal = ({ toggleModal }: { toggleModal: () => void }) => { {({ results }) => ( - {results.map(({ type, document, highlight }) => { + {results.map(({ type, document, highlight, rank }) => { let resultItem; switch (type) { case 'software-catalog': resultItem = ( } key={document.location} result={document} highlight={highlight} + rank={rank} /> ); break; case 'techdocs': resultItem = ( } key={document.location} result={document} highlight={highlight} + rank={rank} /> ); break; @@ -207,6 +215,7 @@ export const SearchModal = ({ toggleModal }: { toggleModal: () => void }) => { key={document.location} result={document} highlight={highlight} + rank={rank} /> ); } diff --git a/packages/app/src/components/search/SearchPage.tsx b/packages/app/src/components/search/SearchPage.tsx index d1b56bbaf0..053fa81da8 100644 --- a/packages/app/src/components/search/SearchPage.tsx +++ b/packages/app/src/components/search/SearchPage.tsx @@ -21,7 +21,7 @@ import { Header, Lifecycle, Page, - SidebarPinStateContext, + useSidebarPinState, } from '@backstage/core-components'; import { useApi } from '@backstage/core-plugin-api'; import { CatalogSearchResultListItem } from '@backstage/plugin-catalog'; @@ -29,18 +29,18 @@ import { catalogApiRef, CATALOG_FILTER_EXISTS, } from '@backstage/plugin-catalog-react'; +import { SearchType } from '@backstage/plugin-search'; import { DefaultResultListItem, SearchBar, SearchFilter, SearchResult, SearchResultPager, - SearchType, -} from '@backstage/plugin-search'; -import { useSearch } from '@backstage/plugin-search-react'; + useSearch, +} from '@backstage/plugin-search-react'; import { TechDocsSearchResultListItem } from '@backstage/plugin-techdocs'; import { Grid, List, makeStyles, Paper, Theme } from '@material-ui/core'; -import React, { useContext } from 'react'; +import React from 'react'; const useStyles = makeStyles((theme: Theme) => ({ bar: { @@ -59,7 +59,7 @@ const useStyles = makeStyles((theme: Theme) => ({ const SearchPage = () => { const classes = useStyles(); - const { isMobile } = useContext(SidebarPinStateContext); + const { isMobile } = useSidebarPinState(); const { types } = useSearch(); const catalogApi = useApi(catalogApiRef); @@ -132,22 +132,26 @@ const SearchPage = () => { {({ results }) => ( - {results.map(({ type, document, highlight }) => { + {results.map(({ type, document, highlight, rank }) => { switch (type) { case 'software-catalog': return ( } key={document.location} result={document} highlight={highlight} + rank={rank} /> ); case 'techdocs': return ( } key={document.location} result={document} highlight={highlight} + rank={rank} /> ); default: @@ -156,6 +160,7 @@ const SearchPage = () => { key={document.location} result={document} highlight={highlight} + rank={rank} /> ); } diff --git a/packages/backend-common/CHANGELOG.md b/packages/backend-common/CHANGELOG.md index 6787a3e12f..9e5a28f76b 100644 --- a/packages/backend-common/CHANGELOG.md +++ b/packages/backend-common/CHANGELOG.md @@ -1,5 +1,62 @@ # @backstage/backend-common +## 0.14.0 + +### Minor Changes + +- 55647ec7df: **BREAKING**: Server-to-server tokens that are authenticated by the `ServerTokenManager` now must have an `exp` claim that has not expired. Tokens where the `exp` claim is in the past or missing are considered invalid and will throw an error. This is a followup to the deprecation from the `1.2` release of Backstage where perpetual tokens were deprecated. Be sure to update any usage of the `getToken()` method to have it be called every time a token is needed. Do not store tokens for later use. + +### Patch Changes + +- f72a6b8c62: Applied the `luxon` dependency fix from the `0.13.4` patch release. +- 5b22a8c97f: Applied the AWS S3 reading patch from the `0.13.5` patch release. +- f5283a42e2: Updated dependency `@google-cloud/storage` to `^6.0.0`. +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/integration@1.2.1 + - @backstage/config-loader@1.1.2 + +## 0.14.0-next.2 + +### Minor Changes + +- 55647ec7df: **BREAKING**: Server-to-server tokens that are authenticated by the `ServerTokenManager` now must have an `exp` claim that has not expired. Tokens where the `exp` claim is in the past or missing are considered invalid and will throw an error. This is a followup to the deprecation from the `1.2` release of Backstage where perpetual tokens were deprecated. Be sure to update any usage of the `getToken()` method to have it be called every time a token is needed. Do not store tokens for later use. + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.2.1-next.2 + +## 0.13.6-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/config-loader@1.1.2-next.0 + - @backstage/integration@1.2.1-next.1 + +## 0.13.6-next.0 + +### Patch Changes + +- f72a6b8c62: Applied the `luxon` dependency fix from the `0.13.4` patch release. +- 5b22a8c97f: Applied the AWS S3 reading patch from the `0.13.5` patch release. +- Updated dependencies + - @backstage/integration@1.2.1-next.0 + +## 0.13.5 + +### Patch Changes + +- 667d2ed6f8: Fix a bug in the URL Reading towards AWS S3 where it would hang indefinitely. + +## 0.13.4 + +### Patch Changes + +- 739be2b079: Fixed potential crash by bumping the `luxon` dependency to `^2.3.1`. + ## 0.13.3 ### Patch Changes diff --git a/packages/backend-common/package.json b/packages/backend-common/package.json index f4c404fb11..d4da05eaa6 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.13.3", + "version": "0.14.0", "main": "src/index.ts", "types": "src/index.ts", "private": false, @@ -36,11 +36,11 @@ "dependencies": { "@backstage/cli-common": "^0.1.9", "@backstage/config": "^1.0.1", - "@backstage/config-loader": "^1.1.1", + "@backstage/config-loader": "^1.1.2", "@backstage/errors": "^1.0.0", - "@backstage/integration": "^1.2.0", + "@backstage/integration": "^1.2.1", "@backstage/types": "^1.0.0", - "@google-cloud/storage": "^5.8.0", + "@google-cloud/storage": "^6.0.0", "@manypkg/get-packages": "^1.1.3", "@octokit/rest": "^18.5.3", "@types/cors": "^2.8.6", @@ -68,7 +68,7 @@ "knex": "^1.0.2", "lodash": "^4.17.21", "logform": "^2.3.2", - "luxon": "^2.0.2", + "luxon": "^2.3.1", "minimatch": "^5.0.0", "minimist": "^1.2.5", "morgan": "^1.10.0", @@ -91,8 +91,8 @@ } }, "devDependencies": { - "@backstage/backend-test-utils": "^0.1.24", - "@backstage/cli": "^0.17.1", + "@backstage/backend-test-utils": "^0.1.25", + "@backstage/cli": "^0.17.2", "@types/archiver": "^5.1.0", "@types/base64-stream": "^1.0.2", "@types/compression": "^1.7.0", @@ -112,7 +112,7 @@ "better-sqlite3": "^7.5.0", "http-errors": "^2.0.0", "mock-fs": "^5.1.0", - "msw": "^0.35.0", + "msw": "^0.42.0", "mysql2": "^2.2.5", "recursive-readdir": "^2.2.2", "supertest": "^6.1.3" diff --git a/packages/backend-common/src/reading/AwsS3UrlReader.test.ts b/packages/backend-common/src/reading/AwsS3UrlReader.test.ts index ea969b43a9..454b8a2c5a 100644 --- a/packages/backend-common/src/reading/AwsS3UrlReader.test.ts +++ b/packages/backend-common/src/reading/AwsS3UrlReader.test.ts @@ -18,6 +18,9 @@ import { ConfigReader } from '@backstage/config'; import { JsonObject } from '@backstage/types'; import { getVoidLogger } from '../logging'; import { DefaultReadTreeResponseFactory } from './tree'; +import { rest } from 'msw'; +import { setupServer } from 'msw/node'; +import { setupRequestMockHandlers } from '@backstage/backend-test-utils'; import { AwsS3UrlReader, parseUrl } from './AwsS3UrlReader'; import { AwsS3Integration, @@ -36,6 +39,15 @@ const treeResponseFactory = DefaultReadTreeResponseFactory.create({ describe('parseUrl', () => { it('supports all aws formats', () => { + expect( + parseUrl('https://s3.amazonaws.com/my.bucket-3/a/puppy.jpg', { + host: 'amazonaws.com', + }), + ).toEqual({ + path: 'a/puppy.jpg', + bucket: 'my.bucket-3', + region: 'us-east-1', + }); expect( parseUrl('https://s3.us-west-2.amazonaws.com/my.bucket-3/a/puppy.jpg', { host: 'amazonaws.com', @@ -111,6 +123,9 @@ describe('parseUrl', () => { }); describe('AwsS3UrlReader', () => { + const worker = setupServer(); + setupRequestMockHandlers(worker); + const createReader = (config: JsonObject): UrlReaderPredicateTuple[] => { return AwsS3UrlReader.factory({ config: new ConfigReader(config), @@ -119,10 +134,6 @@ describe('AwsS3UrlReader', () => { }); }; - afterEach(() => { - AWSMock.restore(); - }); - it('creates a dummy reader without the awsS3 field', () => { const entries = createReader({ integrations: {}, @@ -209,40 +220,34 @@ describe('AwsS3UrlReader', () => { }); describe('read', () => { - let awsS3UrlReader: AwsS3UrlReader; + const [{ reader }] = createReader({ + integrations: { + awsS3: [ + { + host: 'amazonaws.com', + accessKeyId: 'fake-access-key', + secretAccessKey: 'fake-secret-key', + }, + ], + }, + }); - beforeAll(() => { - AWSMock.setSDKInstance(aws); - AWSMock.mock( - 'S3', - 'getObject', - Buffer.from( - require('fs').readFileSync( - path.resolve( - __dirname, - '__fixtures__/awsS3/awsS3-mock-object.yaml', + beforeEach(() => { + worker.use( + rest.get( + 'https://test-bucket.s3.amazonaws.com/awsS3-mock-object.yaml', + (_, res, ctx) => + res( + ctx.status(200), + ctx.set('ETag', '123abc'), + ctx.body('site_name: Test'), ), - ), ), ); - - const s3 = new aws.S3(); - awsS3UrlReader = new AwsS3UrlReader( - new AwsS3Integration( - readAwsS3IntegrationConfig( - new ConfigReader({ - host: 'amazonaws.com', - accessKeyId: 'fake-access-key', - secretAccessKey: 'fake-secret-key', - }), - ), - ), - { s3, treeResponseFactory }, - ); }); it('returns contents of an object in a bucket', async () => { - const response = await awsS3UrlReader.read( + const response = await reader.read( 'https://test-bucket.s3.us-east-2.amazonaws.com/awsS3-mock-object.yaml', ); expect(response.toString().trim()).toBe('site_name: Test'); @@ -250,7 +255,7 @@ describe('AwsS3UrlReader', () => { it('rejects unknown targets', async () => { await expect( - awsS3UrlReader.read( + reader.read( 'https://test-bucket.s3.us-east-2.NOTamazonaws.com/file.yaml', ), ).rejects.toThrow( @@ -262,59 +267,53 @@ describe('AwsS3UrlReader', () => { }); describe('readUrl', () => { - let awsS3UrlReader: AwsS3UrlReader; + const [{ reader }] = createReader({ + integrations: { + awsS3: [ + { + host: 'amazonaws.com', + accessKeyId: 'fake-access-key', + secretAccessKey: 'fake-secret-key', + }, + ], + }, + }); beforeEach(() => { - AWSMock.setSDKInstance(aws); - - AWSMock.mock( - 'S3', - 'getObject', - Buffer.from( - require('fs').readFileSync( - path.resolve( - __dirname, - '__fixtures__/awsS3/awsS3-mock-object.yaml', + worker.use( + rest.get( + 'https://test-bucket.s3.amazonaws.com/awsS3-mock-object.yaml', + (_, res, ctx) => + res( + ctx.status(200), + ctx.set('ETag', '123abc'), + ctx.body('site_name: Test'), ), - ), ), ); - - const s3 = new aws.S3(); - - awsS3UrlReader = new AwsS3UrlReader( - new AwsS3Integration( - readAwsS3IntegrationConfig( - new ConfigReader({ - host: 'amazonaws.com', - accessKeyId: 'fake-access-key', - secretAccessKey: 'fake-secret-key', - }), - ), - ), - { s3, treeResponseFactory }, - ); }); it('returns contents of an object in a bucket via buffer', async () => { - const response = await awsS3UrlReader.readUrl( + const response = await reader.readUrl!( 'https://test-bucket.s3.us-east-2.amazonaws.com/awsS3-mock-object.yaml', ); + expect(response.etag).toBe('123abc'); const buffer = await response.buffer(); expect(buffer.toString().trim()).toBe('site_name: Test'); }); it('returns contents of an object in a bucket via stream', async () => { - const response = await awsS3UrlReader.readUrl( + const response = await reader.readUrl!( 'https://test-bucket.s3.us-east-2.amazonaws.com/awsS3-mock-object.yaml', ); + expect(response.etag).toBe('123abc'); const fromStream = await getRawBody(response.stream!()); expect(fromStream.toString().trim()).toBe('site_name: Test'); }); it('rejects unknown targets', async () => { await expect( - awsS3UrlReader.readUrl( + reader.readUrl!( 'https://test-bucket.s3.us-east-2.NOTamazonaws.com/file.yaml', ), ).rejects.toThrow( @@ -325,45 +324,73 @@ describe('AwsS3UrlReader', () => { }); }); - describe('readUrl with etag', () => { - let awsS3UrlReader: AwsS3UrlReader; + describe('readUrl towards custom host', () => { + const [{ reader }] = createReader({ + integrations: { + awsS3: [ + { + host: 'localhost:4566', + accessKeyId: 'fake-access-key', + secretAccessKey: 'fake-secret-key', + endpoint: 'http://localhost:4566', + s3ForcePathStyle: true, + }, + ], + }, + }); - beforeAll(() => { - AWSMock.setSDKInstance(aws); - - AWSMock.mock('S3', 'getObject', (_, callback) => { - const error: aws.AWSError = { - code: 'NotModified', - message: 'Not Modified', - statusCode: 304, - name: 'oops', - time: new Date('2019-01-01T00:00:00.000Z'), - }; - callback(error, undefined); - }); - - const s3 = new aws.S3(); - - awsS3UrlReader = new AwsS3UrlReader( - new AwsS3Integration( - readAwsS3IntegrationConfig( - new ConfigReader({ - host: 'amazonaws.com', - accessKeyId: 'fake-access-key', - secretAccessKey: 'fake-secret-key', - }), - ), + beforeEach(() => { + worker.use( + rest.get( + 'http://localhost:4566/test-bucket/awsS3-mock-object.yaml', + (_, res, ctx) => + res( + ctx.status(200), + ctx.set('ETag', '123abc'), + ctx.body('site_name: Test'), + ), + ), + ); + }); + + it('returns contents of an object in a bucket via buffer', async () => { + const response = await reader.readUrl!( + 'http://localhost:4566/test-bucket/awsS3-mock-object.yaml', + ); + expect(response.etag).toBe('123abc'); + const buffer = await response.buffer(); + expect(buffer.toString().trim()).toBe('site_name: Test'); + }); + }); + + describe('readUrl with etag', () => { + const [{ reader }] = createReader({ + integrations: { + awsS3: [ + { + host: 'amazonaws.com', + accessKeyId: 'fake-access-key', + secretAccessKey: 'fake-secret-key', + }, + ], + }, + }); + + beforeEach(() => { + worker.use( + rest.get( + 'https://test-bucket.s3.amazonaws.com/awsS3-mock-object.yaml', + (_, res, ctx) => res(ctx.status(304)), ), - { s3, treeResponseFactory }, ); }); it('returns contents of an object in a bucket', async () => { await expect( - awsS3UrlReader.readUrl( + reader.readUrl!( 'https://test-bucket.s3.us-east-2.amazonaws.com/awsS3-mock-object.yaml', { - etag: 'abc123', + etag: '123abc', }, ), ).rejects.toThrow(NotModifiedError); @@ -424,47 +451,4 @@ describe('AwsS3UrlReader', () => { expect(body.toString().trim()).toBe('site_name: Test'); }); }); - - describe('readNonAwsHost', () => { - let awsS3UrlReader: AwsS3UrlReader; - - beforeAll(() => { - AWSMock.setSDKInstance(aws); - AWSMock.mock( - 'S3', - 'getObject', - Buffer.from( - require('fs').readFileSync( - path.resolve( - __dirname, - '__fixtures__/awsS3/awsS3-mock-object.yaml', - ), - ), - ), - ); - - const s3 = new aws.S3(); - awsS3UrlReader = new AwsS3UrlReader( - new AwsS3Integration( - readAwsS3IntegrationConfig( - new ConfigReader({ - host: 'localhost:4566', - accessKeyId: 'fake-access-key', - secretAccessKey: 'fake-secret-key', - endpoint: 'http://localhost:4566', - s3ForcePathStyle: true, - }), - ), - ), - { s3, treeResponseFactory }, - ); - }); - - it('returns contents of an object in a bucket', async () => { - const response = await awsS3UrlReader.read( - 'http://localhost:4566/test-bucket/awsS3-mock-object.yaml', - ); - expect(response.toString().trim()).toBe('site_name: Test'); - }); - }); }); diff --git a/packages/backend-common/src/reading/AwsS3UrlReader.ts b/packages/backend-common/src/reading/AwsS3UrlReader.ts index dbd2c66a62..7c314cf036 100644 --- a/packages/backend-common/src/reading/AwsS3UrlReader.ts +++ b/packages/backend-common/src/reading/AwsS3UrlReader.ts @@ -35,6 +35,8 @@ import { ForwardedError, NotModifiedError } from '@backstage/errors'; import { ListObjectsV2Output, ObjectList } from 'aws-sdk/clients/s3'; import { ReadUrlResponseFactory } from './ReadUrlResponseFactory'; +const DEFAULT_REGION = 'us-east-1'; + /** * Path style URLs: https://s3.(region).amazonaws.com/(bucket)/(key) * The region can also be on the old form: https://s3-(region).amazonaws.com/(bucket)/(key) @@ -57,7 +59,7 @@ export function parseUrl( // Treat Amazon hosted separately because it has special region logic if (config.host === 'amazonaws.com') { const match = host.match( - /^(?:([a-z0-9.-]+)\.)?s3[.-]([a-z0-9-]+)\.amazonaws\.com$/, + /^(?:([a-z0-9.-]+)\.)?s3(?:[.-]([a-z0-9-]+))?\.amazonaws\.com$/, ); if (!match) { throw new Error(`Invalid AWS S3 URL ${url}`); @@ -76,14 +78,14 @@ export function parseUrl( return { path: pathname.substring(slashIndex + 1), bucket: pathname.substring(0, slashIndex), - region: hostRegion, + region: hostRegion ?? DEFAULT_REGION, }; } return { path: pathname, bucket: hostBucket, - region: hostRegion, + region: hostRegion ?? DEFAULT_REGION, }; } @@ -214,10 +216,38 @@ export class AwsS3UrlReader implements UrlReader { const request = this.deps.s3.getObject(params); options?.signal?.addEventListener('abort', () => request.abort()); - const etag = (await request.promise()).ETag; - return ReadUrlResponseFactory.fromReadable(request.createReadStream(), { - etag, + // Since we're consuming the read stream we need to consume headers and errors via events. + const etagPromise = new Promise((resolve, reject) => { + request.on('httpHeaders', (status, headers) => { + if (status < 400) { + if (status === 200) { + resolve(headers.etag); + } else if (status !== 304 /* not modified */) { + reject( + new Error( + `S3 readUrl request received unexpected status '${status}' in response`, + ), + ); + } + } + }); + request.on('error', error => reject(error)); + request.on('complete', () => + reject( + new Error('S3 readUrl request completed without receiving headers'), + ), + ); + }); + + const stream = request.createReadStream(); + stream.on('error', () => { + // The AWS SDK forwards request errors to the stream, so we need to + // ignore those errors here or the process will crash. + }); + + return ReadUrlResponseFactory.fromReadable(stream, { + etag: await etagPromise, }); } catch (e) { if (e.statusCode === 304) { diff --git a/packages/backend-common/src/reading/FetchUrlReader.ts b/packages/backend-common/src/reading/FetchUrlReader.ts index b83f08c22f..5734beddcf 100644 --- a/packages/backend-common/src/reading/FetchUrlReader.ts +++ b/packages/backend-common/src/reading/FetchUrlReader.ts @@ -39,8 +39,8 @@ export class FetchUrlReader implements UrlReader { * targets to allow, containing the following fields: * * `host`: - * Either full hostnames to match, or subdomain wildcard matchers with a leading `*`. - * For example `example.com` and `*.example.com` are valid values, `prod.*.example.com` is not. + * Either full hostnames to match, or subdomain wildcard matchers with a leading '*'. + * For example 'example.com' and '*.example.com' are valid values, 'prod.*.example.com' is not. * * `paths`: * An optional list of paths which are allowed. If the list is omitted all paths are allowed. diff --git a/packages/backend-common/src/reading/ReadUrlResponseFactory.test.ts b/packages/backend-common/src/reading/ReadUrlResponseFactory.test.ts index c8a659e792..ff975de9bb 100644 --- a/packages/backend-common/src/reading/ReadUrlResponseFactory.test.ts +++ b/packages/backend-common/src/reading/ReadUrlResponseFactory.test.ts @@ -78,8 +78,7 @@ describe('ReadUrlResponseFactory', () => { let readable: NodeJS.ReadableStream; beforeEach(() => { - // @ts-ignore - readable = new Stream({ encoding: 'utf-8' }); + readable = new Stream({ encoding: 'utf-8' }) as NodeJS.ReadableStream; readable.readable = true; // Write data asynchronously, as soon as possible. diff --git a/packages/backend-common/src/tokens/ServerTokenManager.test.ts b/packages/backend-common/src/tokens/ServerTokenManager.test.ts index 6a72b99908..cc7854b2ee 100644 --- a/packages/backend-common/src/tokens/ServerTokenManager.test.ts +++ b/packages/backend-common/src/tokens/ServerTokenManager.test.ts @@ -171,7 +171,6 @@ describe('ServerTokenManager', () => { it('should throw for expired tokens, and re-issue new ones', async () => { jest.useFakeTimers(); - const warn = jest.spyOn(logger, 'warn'); const tokenManager = ServerTokenManager.fromConfig(configWithSecret, { logger, @@ -179,14 +178,12 @@ describe('ServerTokenManager', () => { const { token: token1 } = await tokenManager.getToken(); await expect(tokenManager.authenticate(token1)).resolves.not.toThrow(); - expect(warn.mock.calls.length).toBe(0); // Right before the reissue timeout, it still returns the same token jest.advanceTimersByTime(9 * 60 * 1000); const { token: token1Again } = await tokenManager.getToken(); expect(token1).toEqual(token1Again); await expect(tokenManager.authenticate(token1)).resolves.not.toThrow(); - expect(warn.mock.calls.length).toBe(0); // Right after the reissue timeout, the old ones are still valid but returning a new token jest.advanceTimersByTime(2 * 60 * 1000); @@ -194,14 +191,47 @@ describe('ServerTokenManager', () => { expect(token1).not.toEqual(token2); await expect(tokenManager.authenticate(token1)).resolves.not.toThrow(); await expect(tokenManager.authenticate(token2)).resolves.not.toThrow(); - expect(warn.mock.calls.length).toBe(0); // After expiry of the first one, it gets warnings but the newest one is still valid jest.advanceTimersByTime(52 * 60 * 1000); - await expect(tokenManager.authenticate(token1)).resolves.not.toThrow(); + await expect(tokenManager.authenticate(token1)).rejects.toThrow( + 'Invalid server token; caused by JWTExpired: "exp" claim timestamp check failed', + ); await expect(tokenManager.authenticate(token2)).resolves.not.toThrow(); - expect(warn.mock.calls[0][0]).toMatchInlineSnapshot( - '"#### DEPRECATION WARNING: #### Server-to-server token had an expired exp claim, support for this has been deprecated and will result in errors in a future release"', + }); + + it('should work with a manually crafted JWT', async () => { + const secret = 'a1b2c3'; + const token = await new jose.SignJWT({}) + .setProtectedHeader({ alg: 'HS256' }) + .setSubject('backstage-server') + .setExpirationTime(Date.now() + 1000 * 60 * 60) + .sign(jose.base64url.decode(secret)); + + const tokenManager = ServerTokenManager.fromConfig( + new ConfigReader({ + backend: { auth: { keys: [{ secret }] } }, + }), + { logger }, + ); + await expect(tokenManager.authenticate(token)).resolves.toBeUndefined(); + }); + + it('should reject tokens without exp claim', async () => { + const secret = 'a1b2c3'; + const token = await new jose.SignJWT({}) + .setProtectedHeader({ alg: 'HS256' }) + .setSubject('backstage-server') + .sign(jose.base64url.decode(secret)); + + const tokenManager = ServerTokenManager.fromConfig( + new ConfigReader({ + backend: { auth: { keys: [{ secret }] } }, + }), + { logger }, + ); + await expect(tokenManager.authenticate(token)).rejects.toThrow( + 'Invalid server token; caused by AuthenticationError: Server-to-server token had no exp claim', ); }); }); diff --git a/packages/backend-common/src/tokens/ServerTokenManager.ts b/packages/backend-common/src/tokens/ServerTokenManager.ts index 329c2f164c..970dcfb83d 100644 --- a/packages/backend-common/src/tokens/ServerTokenManager.ts +++ b/packages/backend-common/src/tokens/ServerTokenManager.ts @@ -15,7 +15,7 @@ */ import { Config } from '@backstage/config'; -import { AuthenticationError, NotAllowedError } from '@backstage/errors'; +import { AuthenticationError } from '@backstage/errors'; import { base64url, exportJWK, generateSecret, jwtVerify, SignJWT } from 'jose'; import { DateTime, Duration } from 'luxon'; import { Logger } from 'winston'; @@ -64,8 +64,6 @@ export class ServerTokenManager implements TokenManager { private signingKey: Uint8Array; private privateKeyPromise: Promise | undefined; private currentTokenPromise: Promise<{ token: string }> | undefined; - private warnedForMissingExpClaim = false; - private warnedForExpiredExpClaim = false; /** * Creates a token manager that issues static dummy tokens and never fails @@ -184,36 +182,20 @@ export class ServerTokenManager implements TokenManager { const { protectedHeader: { alg }, payload: { sub, exp }, - } = await jwtVerify(token, key, { - // TODO(freben): Holding on to tokens and reusing them is deprecated; remove this tolerance in a future release - clockTolerance: 3e9, - }); + } = await jwtVerify(token, key); if (alg !== TOKEN_ALG) { - throw new NotAllowedError(`Illegal alg "${alg}"`); + throw new AuthenticationError(`Illegal alg "${alg}"`); } if (sub !== TOKEN_SUB) { - throw new NotAllowedError(`Illegal sub "${sub}"`); + throw new AuthenticationError(`Illegal sub "${sub}"`); } - // TODO(freben): Passing in tokens without an exp is deprecated; change this warning to an error in a future release if (typeof exp !== 'number') { - if (!this.warnedForMissingExpClaim) { - this.warnedForMissingExpClaim = true; - this.options.logger.warn( - `#### DEPRECATION WARNING: #### Server-to-server token had no exp claim, support for this has been deprecated and will result in errors in a future release`, - ); - } - } - // TODO(freben): Holding on to tokens and reusing them is deprecated; remove this tolerance in a future release - else if (exp * 1000 < Date.now()) { - if (!this.warnedForExpiredExpClaim) { - this.warnedForExpiredExpClaim = true; - this.options.logger.warn( - `#### DEPRECATION WARNING: #### Server-to-server token had an expired exp claim, support for this has been deprecated and will result in errors in a future release`, - ); - } + throw new AuthenticationError( + 'Server-to-server token had no exp claim', + ); } return; } catch (e) { @@ -222,6 +204,6 @@ export class ServerTokenManager implements TokenManager { } } - throw new AuthenticationError(`Invalid server token: ${verifyError}`); + throw new AuthenticationError('Invalid server token', verifyError); } } diff --git a/packages/backend-tasks/CHANGELOG.md b/packages/backend-tasks/CHANGELOG.md index 0a74dc0a1d..34be280bbc 100644 --- a/packages/backend-tasks/CHANGELOG.md +++ b/packages/backend-tasks/CHANGELOG.md @@ -1,5 +1,41 @@ # @backstage/backend-tasks +## 0.3.2 + +### Patch Changes + +- fde10d24f6: Allow tasks that fail to retry on a loop emitting a warning log every time it fails with the amount of attempts it has +- f7146b516f: Updated dependency `cron` to `^2.0.0`. + Updated dependency `@types/cron` to `^2.0.0`. +- 7f108513b8: Add error logging when a background task throws an error rather than silently swallowing it. +- Updated dependencies + - @backstage/backend-common@0.14.0 + +## 0.3.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0-next.2 + +## 0.3.2-next.1 + +### Patch Changes + +- f7146b516f: Updated dependency `cron` to `^2.0.0`. + Updated dependency `@types/cron` to `^2.0.0`. +- 7f108513b8: Add error logging when a background task throws an error rather than silently swallowing it. +- Updated dependencies + - @backstage/backend-common@0.13.6-next.1 + +## 0.3.2-next.0 + +### Patch Changes + +- fde10d24f6: Allow tasks that fail to retry on a loop emitting a warning log every time it fails with the amount of attempts it has +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + ## 0.3.1 ### Patch Changes diff --git a/packages/backend-tasks/package.json b/packages/backend-tasks/package.json index 6a4ea9d70d..e96a71c597 100644 --- a/packages/backend-tasks/package.json +++ b/packages/backend-tasks/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/backend-tasks", "description": "Common distributed task management library for Backstage backends", - "version": "0.3.1", + "version": "0.3.2", "main": "src/index.ts", "types": "src/index.ts", "private": false, @@ -33,12 +33,12 @@ "start": "backstage-cli package start" }, "dependencies": { - "@backstage/backend-common": "^0.13.3", + "@backstage/backend-common": "^0.14.0", "@backstage/config": "^1.0.1", "@backstage/errors": "^1.0.0", "@backstage/types": "^1.0.0", "@types/luxon": "^2.0.4", - "cron": "^1.8.2", + "cron": "^2.0.0", "knex": "^1.0.2", "lodash": "^4.17.21", "luxon": "^2.0.2", @@ -48,9 +48,9 @@ "zod": "^3.9.5" }, "devDependencies": { - "@backstage/backend-test-utils": "^0.1.24", - "@backstage/cli": "^0.17.1", - "@types/cron": "^1.7.3", + "@backstage/backend-test-utils": "^0.1.25", + "@backstage/cli": "^0.17.2", + "@types/cron": "^2.0.0", "wait-for-expect": "^3.0.2" }, "files": [ diff --git a/packages/backend-tasks/src/tasks/LocalTaskWorker.ts b/packages/backend-tasks/src/tasks/LocalTaskWorker.ts index b7dd7c96c1..be30988aa8 100644 --- a/packages/backend-tasks/src/tasks/LocalTaskWorker.ts +++ b/packages/backend-tasks/src/tasks/LocalTaskWorker.ts @@ -123,7 +123,7 @@ export class LocalTaskWorker { let dt: number; if (isCron) { - const nextRun = +new CronTime(settings.cadence).sendAt().toDate(); + const nextRun = +new CronTime(settings.cadence).sendAt().toJSDate(); dt = nextRun - Date.now(); } else { dt = diff --git a/packages/backend-tasks/src/tasks/PluginTaskSchedulerImpl.ts b/packages/backend-tasks/src/tasks/PluginTaskSchedulerImpl.ts index e43d0c1be6..e57ec93c1c 100644 --- a/packages/backend-tasks/src/tasks/PluginTaskSchedulerImpl.ts +++ b/packages/backend-tasks/src/tasks/PluginTaskSchedulerImpl.ts @@ -57,7 +57,12 @@ export class PluginTaskSchedulerImpl implements PluginTaskScheduler { if (scope === 'global') { const knex = await this.databaseFactory(); - const worker = new TaskWorker(task.id, task.fn, knex, this.logger); + const worker = new TaskWorker( + task.id, + task.fn, + knex, + this.logger.child({ task: task.id }), + ); await worker.start( { diff --git a/packages/backend-tasks/src/tasks/TaskWorker.test.ts b/packages/backend-tasks/src/tasks/TaskWorker.test.ts index 14688d0f98..8a1be3e8a9 100644 --- a/packages/backend-tasks/src/tasks/TaskWorker.test.ts +++ b/packages/backend-tasks/src/tasks/TaskWorker.test.ts @@ -119,6 +119,31 @@ describe('TaskWorker', () => { 60_000, ); + it.each(databases.eachSupportedId())( + 'logs error when the task throws, %p', + async databaseId => { + const knex = await databases.init(databaseId); + await migrateBackendTasks(knex); + + jest.spyOn(logger, 'error'); + const fn = jest.fn().mockRejectedValue(new Error('failed')); + const settings: TaskSettingsV2 = { + version: 2, + initialDelayDuration: undefined, + cadence: '* * * * * *', + timeoutAfterDuration: Duration.fromMillis(60000).toISO(), + }; + const checkFrequency = Duration.fromObject({ milliseconds: 100 }); + const worker = new TaskWorker('task1', fn, knex, logger, checkFrequency); + worker.start(settings); + + await waitForExpect(() => { + expect(logger.error).toBeCalled(); + }); + }, + 60_000, + ); + it.each(databases.eachSupportedId())( 'runs tasks more than once even when the task throws, %p', async databaseId => { diff --git a/packages/backend-tasks/src/tasks/TaskWorker.ts b/packages/backend-tasks/src/tasks/TaskWorker.ts index 07e1cd90ec..c99c08e4fb 100644 --- a/packages/backend-tasks/src/tasks/TaskWorker.ts +++ b/packages/backend-tasks/src/tasks/TaskWorker.ts @@ -145,6 +145,7 @@ export class TaskWorker { await this.fn(taskAbortController.signal); taskAbortController.abort(); // releases resources } catch (e) { + this.logger.error(e); await this.tryReleaseTask(ticket, taskSettings); return { result: 'failed' }; } finally { @@ -174,8 +175,9 @@ export class TaskWorker { } else if (isCron) { const time = new CronTime(settings.cadence) .sendAt() - .add({ seconds: -1 }) // immediately, if "* * * * * *" - .toISOString(); + .minus({ seconds: 1 }) // immediately, if "* * * * * *" + .toUTC() + .toISO(); startAt = this.knex.client.config.client.includes('sqlite3') ? this.knex.raw('datetime(?)', [time]) : this.knex.raw(`?`, [time]); @@ -278,7 +280,7 @@ export class TaskWorker { let nextRun: Knex.Raw; if (isCron) { - const time = new CronTime(settings.cadence).sendAt().toISOString(); + const time = new CronTime(settings.cadence).sendAt().toUTC().toISO(); this.logger.debug(`task: ${this.taskId} will next occur around ${time}`); nextRun = this.knex.client.config.client.includes('sqlite3') ? this.knex.raw('datetime(?)', [time]) diff --git a/packages/backend-test-utils/CHANGELOG.md b/packages/backend-test-utils/CHANGELOG.md index b84b877774..104f17a2e8 100644 --- a/packages/backend-test-utils/CHANGELOG.md +++ b/packages/backend-test-utils/CHANGELOG.md @@ -1,5 +1,39 @@ # @backstage/backend-test-utils +## 0.1.25 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/cli@0.17.2 + - @backstage/backend-common@0.14.0 + +## 0.1.25-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/cli@0.17.2-next.2 + - @backstage/backend-common@0.14.0-next.2 + +## 0.1.25-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/cli@0.17.2-next.1 + - @backstage/backend-common@0.13.6-next.1 + +## 0.1.25-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/cli@0.17.2-next.0 + - @backstage/backend-common@0.13.6-next.0 + ## 0.1.24 ### Patch Changes diff --git a/packages/backend-test-utils/package.json b/packages/backend-test-utils/package.json index c60b860b6b..abc55944ea 100644 --- a/packages/backend-test-utils/package.json +++ b/packages/backend-test-utils/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/backend-test-utils", "description": "Test helpers library for Backstage backends", - "version": "0.1.24", + "version": "0.1.25", "main": "src/index.ts", "types": "src/index.ts", "private": false, @@ -34,19 +34,19 @@ "start": "backstage-cli package start" }, "dependencies": { - "@backstage/backend-common": "^0.13.3", - "@backstage/cli": "^0.17.1", + "@backstage/backend-common": "^0.14.0", + "@backstage/cli": "^0.17.2", "@backstage/config": "^1.0.1", "better-sqlite3": "^7.5.0", "knex": "^1.0.2", - "msw": "^0.35.0", + "msw": "^0.42.0", "mysql2": "^2.2.5", "pg": "^8.3.0", "testcontainers": "^8.1.2", "uuid": "^8.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1" + "@backstage/cli": "^0.17.2" }, "files": [ "dist" diff --git a/packages/backend/CHANGELOG.md b/packages/backend/CHANGELOG.md index ae6beff70a..1602a34c37 100644 --- a/packages/backend/CHANGELOG.md +++ b/packages/backend/CHANGELOG.md @@ -1,5 +1,151 @@ # example-backend +## 0.2.72 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-tech-insights-backend@0.4.1 + - @backstage/plugin-catalog-backend@1.2.0 + - @backstage/plugin-auth-backend@0.14.1 + - @backstage/plugin-scaffolder-backend@1.3.0 + - @backstage/backend-tasks@0.3.2 + - @backstage/plugin-permission-node@0.6.2 + - @backstage/plugin-kubernetes-backend@0.6.0 + - @backstage/backend-common@0.14.0 + - @backstage/plugin-search-backend@0.5.3 + - @backstage/plugin-auth-node@0.2.2 + - @backstage/integration@1.2.1 + - @backstage/plugin-jenkins-backend@0.1.23 + - @backstage/plugin-search-backend-node@0.6.2 + - @backstage/catalog-client@1.0.3 + - @backstage/plugin-app-backend@0.3.33 + - @backstage/plugin-azure-devops-backend@0.3.12 + - @backstage/plugin-code-coverage-backend@0.1.31 + - @backstage/plugin-graphql-backend@0.1.23 + - @backstage/plugin-permission-backend@0.5.8 + - @backstage/plugin-permission-common@0.6.2 + - @backstage/plugin-rollbar-backend@0.1.30 + - @backstage/plugin-techdocs-backend@1.1.2 + - @backstage/plugin-todo-backend@0.1.30 + - @backstage/plugin-search-backend-module-elasticsearch@0.1.5 + - @backstage/plugin-search-backend-module-pg@0.3.4 + - @backstage/catalog-model@1.0.3 + - @backstage/plugin-tech-insights-node@0.3.1 + - example-app@0.2.72 + - @backstage/plugin-badges-backend@0.1.27 + - @backstage/plugin-kafka-backend@0.2.26 + - @backstage/plugin-proxy-backend@0.2.27 + - @backstage/plugin-scaffolder-backend-module-rails@0.4.1 + - @backstage/plugin-tech-insights-backend-module-jsonfc@0.1.17 + +## 0.2.72-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-backend@1.3.0-next.2 + - @backstage/backend-common@0.14.0-next.2 + - @backstage/plugin-search-backend@0.5.3-next.2 + - @backstage/plugin-auth-backend@0.14.1-next.2 + - @backstage/plugin-search-backend-module-elasticsearch@0.1.5-next.2 + - @backstage/integration@1.2.1-next.2 + - @backstage/plugin-techdocs-backend@1.1.2-next.2 + - @backstage/plugin-search-backend-node@0.6.2-next.2 + - example-app@0.2.72-next.2 + - @backstage/backend-tasks@0.3.2-next.2 + - @backstage/plugin-app-backend@0.3.33-next.2 + - @backstage/plugin-auth-node@0.2.2-next.2 + - @backstage/plugin-azure-devops-backend@0.3.12-next.2 + - @backstage/plugin-badges-backend@0.1.27-next.2 + - @backstage/plugin-catalog-backend@1.2.0-next.2 + - @backstage/plugin-code-coverage-backend@0.1.31-next.2 + - @backstage/plugin-graphql-backend@0.1.23-next.2 + - @backstage/plugin-jenkins-backend@0.1.23-next.2 + - @backstage/plugin-kafka-backend@0.2.26-next.2 + - @backstage/plugin-kubernetes-backend@0.6.0-next.2 + - @backstage/plugin-permission-backend@0.5.8-next.2 + - @backstage/plugin-permission-node@0.6.2-next.2 + - @backstage/plugin-proxy-backend@0.2.27-next.1 + - @backstage/plugin-rollbar-backend@0.1.30-next.2 + - @backstage/plugin-scaffolder-backend-module-rails@0.4.1-next.1 + - @backstage/plugin-search-backend-module-pg@0.3.4-next.2 + - @backstage/plugin-tech-insights-backend@0.4.1-next.2 + - @backstage/plugin-tech-insights-backend-module-jsonfc@0.1.17-next.1 + - @backstage/plugin-tech-insights-node@0.3.1-next.1 + - @backstage/plugin-todo-backend@0.1.30-next.2 + +## 0.2.72-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-tech-insights-backend@0.4.1-next.1 + - @backstage/plugin-auth-backend@0.14.1-next.1 + - @backstage/plugin-jenkins-backend@0.1.23-next.1 + - @backstage/backend-tasks@0.3.2-next.1 + - @backstage/backend-common@0.13.6-next.1 + - @backstage/catalog-client@1.0.3-next.0 + - @backstage/integration@1.2.1-next.1 + - @backstage/plugin-app-backend@0.3.33-next.1 + - @backstage/plugin-auth-node@0.2.2-next.1 + - @backstage/plugin-azure-devops-backend@0.3.12-next.1 + - @backstage/plugin-catalog-backend@1.2.0-next.1 + - @backstage/plugin-code-coverage-backend@0.1.31-next.1 + - @backstage/plugin-graphql-backend@0.1.23-next.1 + - @backstage/plugin-permission-backend@0.5.8-next.1 + - @backstage/plugin-permission-common@0.6.2-next.0 + - @backstage/plugin-permission-node@0.6.2-next.1 + - @backstage/plugin-rollbar-backend@0.1.30-next.1 + - @backstage/plugin-scaffolder-backend@1.3.0-next.1 + - @backstage/plugin-techdocs-backend@1.1.2-next.1 + - @backstage/plugin-todo-backend@0.1.30-next.1 + - @backstage/plugin-search-backend-module-elasticsearch@0.1.5-next.1 + - @backstage/plugin-search-backend-node@0.6.2-next.1 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-badges-backend@0.1.27-next.1 + - example-app@0.2.72-next.1 + - @backstage/plugin-search-backend@0.5.3-next.1 + - @backstage/plugin-kafka-backend@0.2.26-next.1 + - @backstage/plugin-kubernetes-backend@0.6.0-next.1 + - @backstage/plugin-search-backend-module-pg@0.3.4-next.1 + +## 0.2.72-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-tasks@0.3.2-next.0 + - @backstage/plugin-scaffolder-backend@1.3.0-next.0 + - @backstage/plugin-kubernetes-backend@0.6.0-next.0 + - @backstage/backend-common@0.13.6-next.0 + - @backstage/plugin-auth-backend@0.14.1-next.0 + - @backstage/integration@1.2.1-next.0 + - @backstage/plugin-search-backend-node@0.6.2-next.0 + - @backstage/plugin-catalog-backend@1.2.0-next.0 + - @backstage/plugin-auth-node@0.2.2-next.0 + - @backstage/plugin-techdocs-backend@1.1.2-next.0 + - example-app@0.2.72-next.0 + - @backstage/plugin-app-backend@0.3.33-next.0 + - @backstage/plugin-azure-devops-backend@0.3.12-next.0 + - @backstage/plugin-badges-backend@0.1.27-next.0 + - @backstage/plugin-code-coverage-backend@0.1.31-next.0 + - @backstage/plugin-graphql-backend@0.1.23-next.0 + - @backstage/plugin-jenkins-backend@0.1.23-next.0 + - @backstage/plugin-kafka-backend@0.2.26-next.0 + - @backstage/plugin-permission-backend@0.5.8-next.0 + - @backstage/plugin-permission-node@0.6.2-next.0 + - @backstage/plugin-proxy-backend@0.2.27-next.0 + - @backstage/plugin-rollbar-backend@0.1.30-next.0 + - @backstage/plugin-scaffolder-backend-module-rails@0.4.1-next.0 + - @backstage/plugin-search-backend@0.5.3-next.0 + - @backstage/plugin-search-backend-module-elasticsearch@0.1.5-next.0 + - @backstage/plugin-search-backend-module-pg@0.3.4-next.0 + - @backstage/plugin-tech-insights-backend@0.4.1-next.0 + - @backstage/plugin-tech-insights-backend-module-jsonfc@0.1.17-next.0 + - @backstage/plugin-tech-insights-node@0.3.1-next.0 + - @backstage/plugin-todo-backend@0.1.30-next.0 + ## 0.2.71 ### Patch Changes diff --git a/packages/backend/package.json b/packages/backend/package.json index d3c42db1c9..9dedf360c1 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -1,6 +1,6 @@ { "name": "example-backend", - "version": "0.2.71", + "version": "0.2.72", "main": "dist/index.cjs.js", "types": "src/index.ts", "license": "Apache-2.0", @@ -26,39 +26,39 @@ "build-image": "docker build ../.. -f Dockerfile --tag example-backend" }, "dependencies": { - "@backstage/backend-common": "^0.13.3", - "@backstage/backend-tasks": "^0.3.1", - "@backstage/catalog-client": "^1.0.2", - "@backstage/catalog-model": "^1.0.2", + "@backstage/backend-common": "^0.14.0", + "@backstage/backend-tasks": "^0.3.2", + "@backstage/catalog-client": "^1.0.3", + "@backstage/catalog-model": "^1.0.3", "@backstage/config": "^1.0.1", - "@backstage/integration": "^1.2.0", - "@backstage/plugin-app-backend": "^0.3.32", - "@backstage/plugin-auth-backend": "^0.14.0", - "@backstage/plugin-auth-node": "^0.2.1", - "@backstage/plugin-azure-devops-backend": "^0.3.11", - "@backstage/plugin-badges-backend": "^0.1.26", - "@backstage/plugin-catalog-backend": "^1.1.2", - "@backstage/plugin-code-coverage-backend": "^0.1.30", - "@backstage/plugin-graphql-backend": "^0.1.22", - "@backstage/plugin-jenkins-backend": "^0.1.22", - "@backstage/plugin-kubernetes-backend": "^0.5.1", - "@backstage/plugin-kafka-backend": "^0.2.25", - "@backstage/plugin-permission-backend": "^0.5.7", - "@backstage/plugin-permission-common": "^0.6.1", - "@backstage/plugin-permission-node": "^0.6.1", - "@backstage/plugin-proxy-backend": "^0.2.26", - "@backstage/plugin-rollbar-backend": "^0.1.29", - "@backstage/plugin-scaffolder-backend": "^1.2.0", - "@backstage/plugin-scaffolder-backend-module-rails": "^0.4.0", - "@backstage/plugin-search-backend": "^0.5.2", - "@backstage/plugin-search-backend-node": "^0.6.1", - "@backstage/plugin-search-backend-module-elasticsearch": "^0.1.4", - "@backstage/plugin-search-backend-module-pg": "^0.3.3", - "@backstage/plugin-techdocs-backend": "^1.1.1", - "@backstage/plugin-tech-insights-backend": "^0.4.0", - "@backstage/plugin-tech-insights-node": "^0.3.0", - "@backstage/plugin-tech-insights-backend-module-jsonfc": "^0.1.16", - "@backstage/plugin-todo-backend": "^0.1.29", + "@backstage/integration": "^1.2.1", + "@backstage/plugin-app-backend": "^0.3.33", + "@backstage/plugin-auth-backend": "^0.14.1", + "@backstage/plugin-auth-node": "^0.2.2", + "@backstage/plugin-azure-devops-backend": "^0.3.12", + "@backstage/plugin-badges-backend": "^0.1.27", + "@backstage/plugin-catalog-backend": "^1.2.0", + "@backstage/plugin-code-coverage-backend": "^0.1.31", + "@backstage/plugin-graphql-backend": "^0.1.23", + "@backstage/plugin-jenkins-backend": "^0.1.23", + "@backstage/plugin-kubernetes-backend": "^0.6.0", + "@backstage/plugin-kafka-backend": "^0.2.26", + "@backstage/plugin-permission-backend": "^0.5.8", + "@backstage/plugin-permission-common": "^0.6.2", + "@backstage/plugin-permission-node": "^0.6.2", + "@backstage/plugin-proxy-backend": "^0.2.27", + "@backstage/plugin-rollbar-backend": "^0.1.30", + "@backstage/plugin-scaffolder-backend": "^1.3.0", + "@backstage/plugin-scaffolder-backend-module-rails": "^0.4.1", + "@backstage/plugin-search-backend": "^0.5.3", + "@backstage/plugin-search-backend-node": "^0.6.2", + "@backstage/plugin-search-backend-module-elasticsearch": "^0.1.5", + "@backstage/plugin-search-backend-module-pg": "^0.3.4", + "@backstage/plugin-techdocs-backend": "^1.1.2", + "@backstage/plugin-tech-insights-backend": "^0.4.1", + "@backstage/plugin-tech-insights-node": "^0.3.1", + "@backstage/plugin-tech-insights-backend-module-jsonfc": "^0.1.17", + "@backstage/plugin-todo-backend": "^0.1.30", "@gitbeaker/node": "^35.1.0", "@octokit/rest": "^18.5.3", "better-sqlite3": "^7.5.0", @@ -75,7 +75,7 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/cli": "^0.17.1", + "@backstage/cli": "^0.17.2", "@types/dockerode": "^3.3.0", "@types/express": "^4.17.6", "@types/express-serve-static-core": "^4.17.5", diff --git a/packages/backend/src/plugins/auth.ts b/packages/backend/src/plugins/auth.ts index cd0c42afe5..8beda6f3fb 100644 --- a/packages/backend/src/plugins/auth.ts +++ b/packages/backend/src/plugins/auth.ts @@ -44,7 +44,27 @@ export default async function createPlugin( // It is here for demo purposes only. github: providers.github.create({ signIn: { - resolver: providers.github.resolvers.usernameMatchingUserEntityName(), + async resolver({ result: { fullProfile } }, ctx) { + const userId = fullProfile.username; + if (!userId) { + throw new Error( + `GitHub user profile does not contain a username`, + ); + } + + const userEntityRef = stringifyEntityRef({ + kind: 'User', + name: userId, + namespace: DEFAULT_NAMESPACE, + }); + + return ctx.issueToken({ + claims: { + sub: userEntityRef, + ent: [userEntityRef], + }, + }); + }, }, }), gitlab: providers.gitlab.create({ diff --git a/packages/catalog-client/CHANGELOG.md b/packages/catalog-client/CHANGELOG.md index a434db2f23..93d232dc24 100644 --- a/packages/catalog-client/CHANGELOG.md +++ b/packages/catalog-client/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/catalog-client +## 1.0.3 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- 35bc0a7c27: Update README to point to catalog-react for frontend usage +- Updated dependencies + - @backstage/catalog-model@1.0.3 + +## 1.0.3-next.0 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/catalog-model@1.0.3-next.0 + ## 1.0.2 ### Patch Changes diff --git a/packages/catalog-client/README.md b/packages/catalog-client/README.md index 574e23cf62..330c1f3ea1 100644 --- a/packages/catalog-client/README.md +++ b/packages/catalog-client/README.md @@ -5,10 +5,9 @@ Backstage Catalog. Backend code may import and use this package directly. -However, frontend code will not want to import this package directly - use the -`@backstage/plugin-catalog` package instead, which re-exports all of the types -and classes from this package. Thereby, you will also gain access to its -`catalogApiRef`. +However, frontend code will not want to instantiate a catalog client directly - +use the `@backstage/plugin-catalog-react` package instead, which exports a +`catalogApiRef` that can be leveraged like other frontend utility APIs. ## Links diff --git a/packages/catalog-client/package.json b/packages/catalog-client/package.json index 1ccd49ab6c..76818ed10f 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.0.2", + "version": "1.0.3", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -33,14 +33,14 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-model": "^1.0.2", + "@backstage/catalog-model": "^1.0.3", "@backstage/errors": "^1.0.0", "cross-fetch": "^3.1.5" }, "devDependencies": { - "@backstage/cli": "^0.17.1", + "@backstage/cli": "^0.17.2", "@types/jest": "^26.0.7", - "msw": "^0.35.0" + "msw": "^0.42.0" }, "files": [ "dist" diff --git a/packages/catalog-model/CHANGELOG.md b/packages/catalog-model/CHANGELOG.md index c6b2fc39e3..7d491dad5b 100644 --- a/packages/catalog-model/CHANGELOG.md +++ b/packages/catalog-model/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/catalog-model +## 1.0.3 + +### Patch Changes + +- 131a99e909: Added targetRef to common.schema.json to match the Typescript type + +## 1.0.3-next.0 + +### Patch Changes + +- 131a99e909: Added targetRef to common.schema.json to match the Typescript type + ## 1.0.2 ### Patch Changes diff --git a/packages/catalog-model/api-report.md b/packages/catalog-model/api-report.md index fca23d6ab9..8427ef4112 100644 --- a/packages/catalog-model/api-report.md +++ b/packages/catalog-model/api-report.md @@ -159,6 +159,7 @@ export type EntityLink = { url: string; title?: string; icon?: string; + type?: string; }; // @public diff --git a/packages/catalog-model/package.json b/packages/catalog-model/package.json index 41f6f15851..328482fd24 100644 --- a/packages/catalog-model/package.json +++ b/packages/catalog-model/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/catalog-model", "description": "Types and validators that help describe the model of a Backstage Catalog", - "version": "1.0.2", + "version": "1.0.3", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -43,7 +43,7 @@ "uuid": "^8.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", + "@backstage/cli": "^0.17.2", "@types/jest": "^26.0.7", "@types/json-schema": "^7.0.5", "@types/lodash": "^4.14.151", diff --git a/packages/catalog-model/src/entity/Entity.ts b/packages/catalog-model/src/entity/Entity.ts index 96f19b2c8a..80c9adaffb 100644 --- a/packages/catalog-model/src/entity/Entity.ts +++ b/packages/catalog-model/src/entity/Entity.ts @@ -210,4 +210,9 @@ export type EntityLink = { * An optional semantic key that represents a visual icon. */ icon?: string; + + /** + * An optional value to categorize links into specific groups + */ + type?: string; }; diff --git a/packages/catalog-model/src/schema/EntityMeta.schema.json b/packages/catalog-model/src/schema/EntityMeta.schema.json index cfdf9539c2..483e69f672 100644 --- a/packages/catalog-model/src/schema/EntityMeta.schema.json +++ b/packages/catalog-model/src/schema/EntityMeta.schema.json @@ -108,6 +108,12 @@ "description": "A key representing a visual icon to be displayed in the UI.", "examples": ["dashboard"], "minLength": 1 + }, + "type": { + "type": "string", + "description": "An optional value to categorize links into specific groups.", + "examples": ["runbook", "documentation", "logs", "dashboard"], + "minLength": 1 } } } diff --git a/packages/catalog-model/src/schema/shared/common.schema.json b/packages/catalog-model/src/schema/shared/common.schema.json index a9082d53e4..5a5eb97bb7 100644 --- a/packages/catalog-model/src/schema/shared/common.schema.json +++ b/packages/catalog-model/src/schema/shared/common.schema.json @@ -43,6 +43,11 @@ }, "target": { "$ref": "#reference" + }, + "targetRef": { + "type": "string", + "minLength": 1, + "description": "The entity ref of the target of this relation." } } }, diff --git a/packages/catalog-model/src/validation/CommonValidatorFunctions.ts b/packages/catalog-model/src/validation/CommonValidatorFunctions.ts index 8c814412c1..40a85c5331 100644 --- a/packages/catalog-model/src/validation/CommonValidatorFunctions.ts +++ b/packages/catalog-model/src/validation/CommonValidatorFunctions.ts @@ -24,7 +24,7 @@ import lodash from 'lodash'; */ export class CommonValidatorFunctions { /** - * Checks that the value is on the form or , and validates + * Checks that the value is on the form `` or ``, and validates * those parts separately. * * @param value - The value to check diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index 5ad3803a25..4da56c18bd 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,46 @@ # @backstage/cli +## 0.17.2 + +### Patch Changes + +- 026cfe525a: Fix the public path configuration of the frontend app build so that a trailing `/` is always appended when needed. +- 4f73352608: Updated Lockfile to support new versions of yarn as well as the legacy 1 version +- b8970b8941: Improved the `create-github-app` permissions selection prompt by converting it into a multi-select with clearer descriptions. The `members` permission is now also included in the list which is required for ingesting user data into the catalog. +- bd58365d09: Updated dependency `run-script-webpack-plugin` to `^0.0.14`. +- 9002ebd76b: Updated dependency `@rollup/plugin-commonjs` to `^22.0.0`. +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- 1a33e8b287: Updated dependency `minimatch` to `5.1.0`. +- 6de866ea74: Added console warning to frontend start when the `app.baseUrl` and `backend.baseUrl` are identical +- Updated dependencies + - @backstage/config-loader@1.1.2 + - @backstage/release-manifests@0.0.4 + +## 0.17.2-next.2 + +### Patch Changes + +- 026cfe525a: Fix the public path configuration of the frontend app build so that a trailing `/` is always appended when needed. +- 9002ebd76b: Updated dependency `@rollup/plugin-commonjs` to `^22.0.0`. +- 1a33e8b287: Updated dependency `minimatch` to `5.1.0`. + +## 0.17.2-next.1 + +### Patch Changes + +- bd58365d09: Updated dependency `run-script-webpack-plugin` to `^0.0.14`. +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/config-loader@1.1.2-next.0 + - @backstage/release-manifests@0.0.4-next.0 + +## 0.17.2-next.0 + +### Patch Changes + +- 4f73352608: Updated Lockfile to support new versions of yarn as well as the legacy 1 version +- 6de866ea74: Added console warning to frontend start when the `app.baseUrl` and `backend.baseUrl` are identical + ## 0.17.1 ### Patch Changes diff --git a/packages/cli/package.json b/packages/cli/package.json index e36a59b8e2..071fc74f97 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.17.1", + "version": "0.17.2", "private": false, "publishConfig": { "access": "public" @@ -33,15 +33,15 @@ "dependencies": { "@backstage/cli-common": "^0.1.9", "@backstage/config": "^1.0.1", - "@backstage/config-loader": "^1.1.1", + "@backstage/config-loader": "^1.1.2", "@backstage/errors": "^1.0.0", - "@backstage/release-manifests": "^0.0.3", + "@backstage/release-manifests": "^0.0.4", "@backstage/types": "^1.0.0", "@hot-loader/react-dom-v16": "npm:@hot-loader/react-dom@^16.0.2", "@hot-loader/react-dom-v17": "npm:@hot-loader/react-dom@^17.0.2", "@manypkg/get-packages": "^1.1.3", "@octokit/request": "^5.4.12", - "@rollup/plugin-commonjs": "^21.0.1", + "@rollup/plugin-commonjs": "^22.0.0", "@rollup/plugin-json": "^4.1.0", "@rollup/plugin-node-resolve": "^13.0.0", "@rollup/plugin-yaml": "^3.1.0", @@ -94,7 +94,7 @@ "json-schema": "^0.4.0", "lodash": "^4.17.21", "mini-css-extract-plugin": "^2.4.2", - "minimatch": "5.0.1", + "minimatch": "5.1.0", "node-fetch": "^2.6.7", "node-libs-browser": "^2.2.1", "npm-packlist": "^5.0.0", @@ -110,7 +110,7 @@ "rollup-plugin-esbuild": "^4.7.2", "rollup-plugin-postcss": "^4.0.0", "rollup-pluginutils": "^2.8.2", - "run-script-webpack-plugin": "^0.0.11", + "run-script-webpack-plugin": "^0.0.14", "semver": "^7.3.2", "style-loader": "^3.3.1", "sucrase": "^3.20.2", @@ -126,13 +126,13 @@ "zod": "^3.11.6" }, "devDependencies": { - "@backstage/backend-common": "^0.13.3", + "@backstage/backend-common": "^0.14.0", "@backstage/config": "^1.0.1", - "@backstage/core-app-api": "^1.0.2", - "@backstage/core-components": "^0.9.4", - "@backstage/core-plugin-api": "^1.0.2", - "@backstage/dev-utils": "^1.0.2", - "@backstage/test-utils": "^1.1.0", + "@backstage/core-app-api": "^1.0.3", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", + "@backstage/dev-utils": "^1.0.3", + "@backstage/test-utils": "^1.1.1", "@backstage/theme": "^0.2.15", "@types/diff": "^5.0.0", "@types/express": "^4.17.6", @@ -152,7 +152,7 @@ "@types/yarnpkg__lockfile": "^1.1.4", "del": "^6.0.0", "mock-fs": "^5.1.0", - "msw": "^0.35.0", + "msw": "^0.42.0", "nodemon": "^2.0.2", "ts-node": "^10.0.0" }, diff --git a/packages/cli/src/commands/create-github-app/GithubCreateAppServer.ts b/packages/cli/src/commands/create-github-app/GithubCreateAppServer.ts index 0fe321541e..70b8c0ca53 100644 --- a/packages/cli/src/commands/create-github-app/GithubCreateAppServer.ts +++ b/packages/cli/src/commands/create-github-app/GithubCreateAppServer.ts @@ -19,18 +19,6 @@ import openBrowser from 'react-dev-utils/openBrowser'; import { request } from '@octokit/request'; import express, { Express, Request, Response } from 'express'; -const MANIFEST_DATA = { - default_events: ['create', 'delete', 'push', 'repository'], - default_permissions: { - contents: 'read', - metadata: 'read', - }, - name: 'Backstage-', - url: 'https://backstage.io', - description: 'GitHub App for Backstage', - public: false, -}; - const FORM_PAGE = ` @@ -62,17 +50,17 @@ export class GithubCreateAppServer { static async run(options: { org: string; - readWrite: boolean; + permissions: string[]; }): Promise { const encodedOrg = encodeURIComponent(options.org); const actionUrl = `https://github.com/organizations/${encodedOrg}/settings/apps/new`; - const server = new GithubCreateAppServer(actionUrl, options.readWrite); + const server = new GithubCreateAppServer(actionUrl, options.permissions); return server.start(); } private constructor( private readonly actionUrl: string, - private readonly readWrite: boolean, + private readonly permissions: string[], ) { const webhookId = crypto .randomBytes(15) @@ -121,21 +109,29 @@ export class GithubCreateAppServer { if (!baseUrl) { throw new Error('baseUrl is not set'); } + const manifest = { - ...MANIFEST_DATA, + default_events: ['create', 'delete', 'push', 'repository'], + default_permissions: { + metadata: 'read', + ...(this.permissions.includes('members') && { members: 'read' }), + ...(this.permissions.includes('read') && { contents: 'read' }), + ...(this.permissions.includes('write') && { + contents: 'write', + actions: 'write', + }), + }, + name: 'Backstage-', + url: 'https://backstage.io', + description: 'GitHub App for Backstage', + public: false, redirect_url: `${baseUrl}/callback`, hook_attributes: { url: this.webhookUrl, active: false, }, - ...(this.readWrite && { - default_permissions: { - contents: 'write', - actions: 'write', - metadata: 'read', - }, - }), }; + const manifestJson = JSON.stringify(manifest).replace(/\"/g, '"'); let body = FORM_PAGE; diff --git a/packages/cli/src/commands/create-github-app/index.ts b/packages/cli/src/commands/create-github-app/index.ts index 175530402e..6b3e732f18 100644 --- a/packages/cli/src/commands/create-github-app/index.ts +++ b/packages/cli/src/commands/create-github-app/index.ts @@ -26,19 +26,38 @@ import openBrowser from 'react-dev-utils/openBrowser'; // due to lacking support for creating apps from manifests. // https://docs.github.com/en/free-pro-team@latest/developers/apps/creating-a-github-app-from-a-manifest export default async (org: string) => { - const answers: Answers = await inquirer.prompt([ - { - type: 'list', - name: 'appType', - message: chalk.blue('What will the app be used for [required]'), - choices: ['Read and Write (needed by Software Templates)', 'Read only'], - }, - ]); - const readWrite = answers.appType !== 'Read only'; + const answers: Answers = await inquirer.prompt({ + name: 'appType', + type: 'checkbox', + message: + 'Select permissions [required] (these can be changed later but then require approvals in all installations)', + choices: [ + { + name: 'Read access to content (required by Software Catalog to ingest data from repositories)', + value: 'read', + checked: true, + }, + { + name: 'Read access to members (required by Software Catalog to ingest GitHub teams)', + value: 'members', + checked: true, + }, + { + name: 'Read and Write to content and actions (required by Software Templates to create new repositories)', + value: 'write', + }, + ], + }); + + if (answers.appType.length === 0) { + console.log(chalk.red('You must select at least one permission')); + process.exit(1); + } + await verifyGithubOrg(org); const { slug, name, ...config } = await GithubCreateAppServer.run({ org, - readWrite, + permissions: answers.appType, }); const fileName = `github-app-${slug}-credentials.yaml`; diff --git a/packages/cli/src/commands/start/startFrontend.ts b/packages/cli/src/commands/start/startFrontend.ts index e5500c310b..f6923632c8 100644 --- a/packages/cli/src/commands/start/startFrontend.ts +++ b/packages/cli/src/commands/start/startFrontend.ts @@ -44,7 +44,7 @@ export async function startFrontend(options: StartAppOptions) { if (problemPackages.length > 1) { console.log( chalk.yellow( - `โš ๏ธ Some of the following packages may be outdated or have duplicate installations: + `โš ๏ธ Some of the following packages may be outdated or have duplicate installations: ${uniq(problemPackages).join(', ')} `, @@ -52,7 +52,7 @@ export async function startFrontend(options: StartAppOptions) { ); console.log( chalk.yellow( - `โš ๏ธ This can be resolved using the following command: + `โš ๏ธ This can be resolved using the following command: yarn backstage-cli versions:check --fix `, @@ -62,14 +62,34 @@ export async function startFrontend(options: StartAppOptions) { } const { name } = await fs.readJson(paths.resolveTarget('package.json')); + const config = await loadCliConfig({ + args: options.configPaths, + fromPackage: name, + withFilteredKeys: true, + }); + + const appBaseUrl = config.frontendConfig.getString('app.baseUrl'); + const backendBaseUrl = config.frontendConfig.getString('backend.baseUrl'); + if (appBaseUrl === backendBaseUrl) { + console.log( + chalk.yellow( + `โš ๏ธ Conflict between app baseUrl and backend baseUrl: + + app.baseUrl: ${appBaseUrl} + backend.baseUrl: ${appBaseUrl} + + Must have unique hostname and/or ports. + + This can be resolved by changing app.baseUrl and backend.baseUrl to point to their respective local development ports. +`, + ), + ); + } + const waitForExit = await serveBundle({ entry: options.entry, checksEnabled: options.checksEnabled, - ...(await loadCliConfig({ - args: options.configPaths, - fromPackage: name, - withFilteredKeys: true, - })), + ...config, }); await waitForExit(); diff --git a/packages/cli/src/lib/bundler/config.ts b/packages/cli/src/lib/bundler/config.ts index fb073a25e3..d723b963bd 100644 --- a/packages/cli/src/lib/bundler/config.ts +++ b/packages/cli/src/lib/bundler/config.ts @@ -89,6 +89,7 @@ export async function createConfig( const baseUrl = frontendConfig.getString('app.baseUrl'); const validBaseUrl = new URL(baseUrl); + const publicPath = validBaseUrl.pathname.replace(/\/$/, ''); if (checksEnabled) { plugins.push( new ForkTsCheckerWebpackPlugin({ @@ -121,7 +122,7 @@ export async function createConfig( new HtmlWebpackPlugin({ template: paths.targetHtml, templateParameters: { - publicPath: validBaseUrl.pathname.replace(/\/$/, ''), + publicPath, config: frontendConfig, }, }), @@ -194,7 +195,7 @@ export async function createConfig( }, output: { path: paths.targetDist, - publicPath: validBaseUrl.pathname, + publicPath: `${publicPath}/`, filename: isDev ? '[name].js' : 'static/[name].[fullhash:8].js', chunkFilename: isDev ? '[name].chunk.js' diff --git a/packages/cli/src/lib/bundler/server.ts b/packages/cli/src/lib/bundler/server.ts index e65e598106..d573f80e27 100644 --- a/packages/cli/src/lib/bundler/server.ts +++ b/packages/cli/src/lib/bundler/server.ts @@ -43,7 +43,6 @@ export async function serveBundle(options: ServeOptions) { const compiler = webpack(config); const server = new WebpackDevServer( - compiler as any, { hot: !process.env.CI, devMiddleware: { @@ -71,10 +70,11 @@ export async function serveBundle(options: ServeOptions) { webSocketURL: 'auto://0.0.0.0:0/ws', }, } as any, + compiler as any, ); await new Promise((resolve, reject) => { - server.listen(port, host, (err?: Error) => { + server.startCallback((err?: Error) => { if (err) { reject(err); return; diff --git a/packages/cli/templates/default-backend-plugin/package.json.hbs b/packages/cli/templates/default-backend-plugin/package.json.hbs index dacc1d601a..2416fd75cb 100644 --- a/packages/cli/templates/default-backend-plugin/package.json.hbs +++ b/packages/cli/templates/default-backend-plugin/package.json.hbs @@ -41,7 +41,7 @@ "@backstage/cli": "{{versionQuery '@backstage/cli'}}", "@types/supertest": "{{versionQuery '@types/supertest' '2.0.8'}}", "supertest": "{{versionQuery 'supertest' '4.0.2'}}", - "msw": "{{versionQuery 'msw' '0.35.0'}}" + "msw": "{{versionQuery 'msw' '0.42.0'}}" }, "files": [ "dist" diff --git a/packages/cli/templates/default-plugin/package.json.hbs b/packages/cli/templates/default-plugin/package.json.hbs index a174c1bafd..34a0548b29 100644 --- a/packages/cli/templates/default-plugin/package.json.hbs +++ b/packages/cli/templates/default-plugin/package.json.hbs @@ -49,7 +49,7 @@ "@testing-library/user-event": "{{versionQuery '@testing-library/user-event' '14.0.0'}}", "@types/jest": "{{versionQuery '@types/jest' '26.0.7'}}", "@types/node": "{{versionQuery '@types/node' '16.11.26'}}", - "msw": "{{versionQuery 'msw' '0.35.0'}}", + "msw": "{{versionQuery 'msw' '0.42.0'}}", "cross-fetch": "{{versionQuery 'cross-fetch' '3.1.5'}}" }, "files": [ diff --git a/packages/config-loader/CHANGELOG.md b/packages/config-loader/CHANGELOG.md index f72cf1d71e..d9228aa091 100644 --- a/packages/config-loader/CHANGELOG.md +++ b/packages/config-loader/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/config-loader +## 1.1.2 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. + +## 1.1.2-next.0 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. + ## 1.1.1 ### Patch Changes diff --git a/packages/config-loader/package.json b/packages/config-loader/package.json index 00629bf2fb..3448b336b7 100644 --- a/packages/config-loader/package.json +++ b/packages/config-loader/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/config-loader", "description": "Config loading functionality used by Backstage backend, and CLI", - "version": "1.1.1", + "version": "1.1.2", "private": false, "publishConfig": { "access": "public", @@ -57,7 +57,7 @@ "@types/node": "^16.11.26", "@types/yup": "^0.29.13", "mock-fs": "^5.1.0", - "msw": "^0.35.0" + "msw": "^0.42.0" }, "files": [ "dist" diff --git a/packages/config/package.json b/packages/config/package.json index d49d31b2f3..bc0693fa73 100644 --- a/packages/config/package.json +++ b/packages/config/package.json @@ -37,7 +37,7 @@ "lodash": "^4.17.21" }, "devDependencies": { - "@backstage/test-utils": "^1.1.0", + "@backstage/test-utils": "^1.1.1-next.0", "@types/jest": "^26.0.7", "@types/node": "^16.0.0" }, diff --git a/packages/core-app-api/CHANGELOG.md b/packages/core-app-api/CHANGELOG.md index b749a1caaa..8d78e51c62 100644 --- a/packages/core-app-api/CHANGELOG.md +++ b/packages/core-app-api/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/core-app-api +## 1.0.3 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- 19781483a2: Handle URLs as the first argument to `fetchApi`, when using the `plugin:` protocol +- Updated dependencies + - @backstage/core-plugin-api@1.0.3 + +## 1.0.3-next.0 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-plugin-api@1.0.3-next.0 + ## 1.0.2 ### Patch Changes diff --git a/packages/core-app-api/package.json b/packages/core-app-api/package.json index 28baeab4df..cbf9d7eb40 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.0.2", + "version": "1.0.3", "private": false, "publishConfig": { "access": "public", @@ -34,7 +34,7 @@ }, "dependencies": { "@backstage/config": "^1.0.1", - "@backstage/core-plugin-api": "^1.0.2", + "@backstage/core-plugin-api": "^1.0.3", "@backstage/types": "^1.0.0", "@backstage/version-bridge": "^1.0.1", "@types/prop-types": "^15.7.3", @@ -49,8 +49,8 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/test-utils": "^1.1.0", + "@backstage/cli": "^0.17.2", + "@backstage/test-utils": "^1.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/react-hooks": "^8.0.0", @@ -59,7 +59,7 @@ "@types/node": "^16.11.26", "@types/zen-observable": "^0.8.0", "cross-fetch": "^3.1.5", - "msw": "^0.35.0" + "msw": "^0.42.0" }, "files": [ "dist", diff --git a/packages/core-app-api/src/apis/implementations/FetchApi/PluginProtocolResolverFetchMiddleware.ts b/packages/core-app-api/src/apis/implementations/FetchApi/PluginProtocolResolverFetchMiddleware.ts index 6265e8d3d2..ff20b594b4 100644 --- a/packages/core-app-api/src/apis/implementations/FetchApi/PluginProtocolResolverFetchMiddleware.ts +++ b/packages/core-app-api/src/apis/implementations/FetchApi/PluginProtocolResolverFetchMiddleware.ts @@ -55,7 +55,14 @@ export class PluginProtocolResolverFetchMiddleware implements FetchMiddleware { } const target = `${join(base, pathname)}${search}${hash}`; - return next(target, typeof input === 'string' ? init : input); + return next( + target, + typeof input === 'string' || isUrl(input) ? init : input, + ); }; } } + +function isUrl(a: unknown): a is URL { + return typeof a === 'object' && a?.constructor === URL; +} diff --git a/packages/core-app-api/src/apis/implementations/IdentityApi/AppIdentityProxy.test.ts b/packages/core-app-api/src/apis/implementations/IdentityApi/AppIdentityProxy.test.ts index aa15182499..3d4f83b05d 100644 --- a/packages/core-app-api/src/apis/implementations/IdentityApi/AppIdentityProxy.test.ts +++ b/packages/core-app-api/src/apis/implementations/IdentityApi/AppIdentityProxy.test.ts @@ -31,7 +31,7 @@ describe('AppIdentityProxy', () => { it('should forward user identities', async () => { const proxy = new AppIdentityProxy(); - proxy.setTarget(mockIdentityApi); + proxy.setTarget(mockIdentityApi, { signOutTargetUrl: '/' }); const logs = await withLogCollector(async () => { mockIdentityApi.getBackstageIdentity.mockResolvedValueOnce({ @@ -55,7 +55,7 @@ describe('AppIdentityProxy', () => { it('should warn about invalid user entity refs', async () => { const proxy = new AppIdentityProxy(); - proxy.setTarget(mockIdentityApi); + proxy.setTarget(mockIdentityApi, { signOutTargetUrl: '/' }); const logs = await withLogCollector(async () => { mockIdentityApi.getBackstageIdentity.mockResolvedValueOnce({ @@ -79,4 +79,16 @@ describe('AppIdentityProxy', () => { error: [], }); }); + + it('should navigate to target URL on sign out', async () => { + const proxy = new AppIdentityProxy(); + proxy.setTarget(mockIdentityApi, { signOutTargetUrl: '/foo' }); + Object.defineProperty(window, 'location', { + writable: true, + value: {}, + }); + + await proxy.signOut(); + expect(location.href).toBe('/foo'); + }); }); diff --git a/packages/core-app-api/src/apis/implementations/IdentityApi/AppIdentityProxy.ts b/packages/core-app-api/src/apis/implementations/IdentityApi/AppIdentityProxy.ts index 3ef3a1f6d3..2df351bc18 100644 --- a/packages/core-app-api/src/apis/implementations/IdentityApi/AppIdentityProxy.ts +++ b/packages/core-app-api/src/apis/implementations/IdentityApi/AppIdentityProxy.ts @@ -49,6 +49,7 @@ export class AppIdentityProxy implements IdentityApi { private target?: CompatibilityIdentityApi; private waitForTarget: Promise; private resolveTarget: (api: CompatibilityIdentityApi) => void = () => {}; + private signOutTargetUrl = '/'; constructor() { this.waitForTarget = new Promise(resolve => { @@ -57,8 +58,12 @@ export class AppIdentityProxy implements IdentityApi { } // This is called by the app manager once the sign-in page provides us with an implementation - setTarget(identityApi: CompatibilityIdentityApi) { + setTarget( + identityApi: CompatibilityIdentityApi, + targetOptions: { signOutTargetUrl: string }, + ) { this.target = identityApi; + this.signOutTargetUrl = targetOptions.signOutTargetUrl; this.resolveTarget(identityApi); } @@ -119,6 +124,6 @@ export class AppIdentityProxy implements IdentityApi { async signOut(): Promise { await this.waitForTarget.then(target => target.signOut()); - location.reload(); + location.href = this.signOutTargetUrl; } } diff --git a/packages/core-app-api/src/app/AppManager.tsx b/packages/core-app-api/src/app/AppManager.tsx index 75bab5c2df..f47ddf676a 100644 --- a/packages/core-app-api/src/app/AppManager.tsx +++ b/packages/core-app-api/src/app/AppManager.tsx @@ -334,41 +334,49 @@ export class AppManager implements BackstageApp { children: ReactElement; }) => { const [identityApi, setIdentityApi] = useState(); + const configApi = useApi(configApiRef); + const basePath = getBasePath(configApi); if (!identityApi) { return ; } - this.appIdentityProxy.setTarget(identityApi); + this.appIdentityProxy.setTarget(identityApi, { + signOutTargetUrl: basePath || '/', + }); return children; }; const AppRouter = ({ children }: PropsWithChildren<{}>) => { const configApi = useApi(configApiRef); - const mountPath = `${getBasePath(configApi)}/*`; + const basePath = getBasePath(configApi); + const mountPath = `${basePath}/*`; const { routeObjects } = useContext(InternalAppContext); // If the app hasn't configured a sign-in page, we just continue as guest. if (!SignInPageComponent) { - this.appIdentityProxy.setTarget({ - getUserId: () => 'guest', - getIdToken: async () => undefined, - getProfile: () => ({ - email: 'guest@example.com', - displayName: 'Guest', - }), - getProfileInfo: async () => ({ - email: 'guest@example.com', - displayName: 'Guest', - }), - getBackstageIdentity: async () => ({ - type: 'user', - userEntityRef: 'user:default/guest', - ownershipEntityRefs: ['user:default/guest'], - }), - getCredentials: async () => ({}), - signOut: async () => {}, - }); + this.appIdentityProxy.setTarget( + { + getUserId: () => 'guest', + getIdToken: async () => undefined, + getProfile: () => ({ + email: 'guest@example.com', + displayName: 'Guest', + }), + getProfileInfo: async () => ({ + email: 'guest@example.com', + displayName: 'Guest', + }), + getBackstageIdentity: async () => ({ + type: 'user', + userEntityRef: 'user:default/guest', + ownershipEntityRefs: ['user:default/guest'], + }), + getCredentials: async () => ({}), + signOut: async () => {}, + }, + { signOutTargetUrl: basePath || '/' }, + ); return ( diff --git a/packages/core-components/CHANGELOG.md b/packages/core-components/CHANGELOG.md index 167076a74e..faec6d3d90 100644 --- a/packages/core-components/CHANGELOG.md +++ b/packages/core-components/CHANGELOG.md @@ -1,5 +1,60 @@ # @backstage/core-components +## 0.9.5 + +### Patch Changes + +- feb4e8de07: Fix EntityPage tab scrolling overflow bug on Firefox +- 65840b17be: Fix issue where right arrow icon was incorrectly added to side bar items without a sub-menu +- de593ec78f: Updated dependency `react-text-truncate` to `^0.19.0`. +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- 6968b65ba1: Updated dependency `@react-hookz/web` to `^14.0.0`. +- ee2cd642c5: Updated dependency `rc-progress` to `3.3.3`. +- 96d1e01641: Accessibility updates: + + - Added `aria-label` to the `Select` component + - Changed heading level used in the header of `Table` component + +- 7d355c4b3f: Fix the missing filter in the toolbar when passing a custom component in the core-components Table +- 1cf9caecd6: fix Sidebar Contexts deprecation message +- bff65e6958: The `SidebarPinStateContext` and `SidebarContext` have been deprecated and will be removed in a future release. Instead, use `` + `useSidebarPinState()` and/or `` + `useSidebarOpenState()`. + + This was done to ensure that sidebar state can be shared successfully across components exported by different packages, regardless of what version of this package is resolved and installed for each individual package. + +- Updated dependencies + - @backstage/core-plugin-api@1.0.3 + +## 0.9.5-next.2 + +### Patch Changes + +- ee2cd642c5: Updated dependency `rc-progress` to `3.3.3`. +- 1cf9caecd6: fix Sidebar Contexts deprecation message + +## 0.9.5-next.1 + +### Patch Changes + +- feb4e8de07: Fix EntityPage tab scrolling overflow bug on Firefox +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- bff65e6958: The `SidebarPinStateContext` and `SidebarContext` have been deprecated and will be removed in a future release. Instead, use `` + `useSidebarPinState()` and/or `` + `useSidebarOpenState()`. + + This was done to ensure that sidebar state can be shared successfully across components exported by different packages, regardless of what version of this package is resolved and installed for each individual package. + +- Updated dependencies + - @backstage/core-plugin-api@1.0.3-next.0 + +## 0.9.5-next.0 + +### Patch Changes + +- 65840b17be: Fix issue where right arrow icon was incorrectly added to side bar items without a sub-menu +- 6968b65ba1: Updated dependency `@react-hookz/web` to `^14.0.0`. +- 96d1e01641: Accessibility updates: + + - Added `aria-label` to the `Select` component + - Changed heading level used in the header of `Table` component + ## 0.9.4 ### Patch Changes diff --git a/packages/core-components/api-report.md b/packages/core-components/api-report.md index 727a50926c..911ef36867 100644 --- a/packages/core-components/api-report.md +++ b/packages/core-components/api-report.md @@ -16,7 +16,6 @@ import { CardHeaderProps } from '@material-ui/core/CardHeader'; import { Column } from '@material-table/core'; import { ComponentClass } from 'react'; import { ComponentProps } from 'react'; -import { Context } from 'react'; import { default as CSS_2 } from 'csstype'; import { CSSProperties } from 'react'; import { ElementType } from 'react'; @@ -903,14 +902,10 @@ export const sidebarConfig: { mobileSidebarHeight: number; }; -// Warning: (ae-missing-release-tag) "SidebarContext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public -export const SidebarContext: Context; +// @public @deprecated +export const SidebarContext: React_2.Context; -// Warning: (ae-missing-release-tag) "SidebarContextType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public +// @public @deprecated export type SidebarContextType = { isOpen: boolean; setOpen: (open: boolean) => void; @@ -983,6 +978,21 @@ export type SidebarItemClassKey = | 'arrows' | 'selected'; +// @public +export type SidebarOpenState = { + isOpen: boolean; + setOpen: (open: boolean) => void; +}; + +// @public +export const SidebarOpenStateProvider: ({ + children, + value, +}: { + children: ReactNode; + value: SidebarOpenState; +}) => JSX.Element; + // @public (undocumented) export type SidebarOptions = { drawerWidthClosed?: number; @@ -1005,15 +1015,31 @@ export type SidebarPageProps = { }; // @public +export type SidebarPinState = { + isPinned: boolean; + toggleSidebarPinState: () => any; + isMobile?: boolean; +}; + +// @public @deprecated export const SidebarPinStateContext: React_2.Context; -// @public +// @public @deprecated export type SidebarPinStateContextType = { isPinned: boolean; toggleSidebarPinState: () => any; isMobile?: boolean; }; +// @public +export const SidebarPinStateProvider: ({ + children, + value, +}: { + children: ReactNode; + value: SidebarPinStateContextType; +}) => JSX.Element; + // @public (undocumented) export type SidebarProps = { openDelayMs?: number; @@ -1447,6 +1473,12 @@ export class UserIdentity implements IdentityApi { signOut(): Promise; } +// @public +export const useSidebarOpenState: () => SidebarOpenState; + +// @public +export const useSidebarPinState: () => SidebarPinState; + // Warning: (ae-missing-release-tag) "useSupportConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) diff --git a/packages/core-components/package.json b/packages/core-components/package.json index a988d8213e..867909b3cf 100644 --- a/packages/core-components/package.json +++ b/packages/core-components/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/core-components", "description": "Core components used by Backstage plugins and apps", - "version": "0.9.4", + "version": "0.9.5", "private": false, "publishConfig": { "access": "public", @@ -34,9 +34,10 @@ }, "dependencies": { "@backstage/config": "^1.0.1", - "@backstage/core-plugin-api": "^1.0.2", + "@backstage/core-plugin-api": "^1.0.3", "@backstage/errors": "^1.0.0", "@backstage/theme": "^0.2.15", + "@backstage/version-bridge": "^1.0.1", "@material-table/core": "^3.1.0", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -55,7 +56,7 @@ "pluralize": "^8.0.0", "prop-types": "^15.7.2", "qs": "^6.9.4", - "rc-progress": "3.3.2", + "rc-progress": "3.3.3", "react-helmet": "6.1.0", "react-hook-form": "^7.12.2", "react-markdown": "^8.0.0", @@ -63,7 +64,7 @@ "react-router-dom": "6.0.0-beta.0", "react-sparklines": "^1.7.0", "react-syntax-highlighter": "^15.4.5", - "react-text-truncate": "^0.18.0", + "react-text-truncate": "^0.19.0", "react-use": "^17.3.2", "@react-hookz/web": "^14.0.0", "react-virtualized-auto-sizer": "^1.0.6", @@ -78,9 +79,9 @@ "react-dom": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/core-app-api": "^1.0.2", - "@backstage/cli": "^0.17.1", - "@backstage/test-utils": "^1.1.0", + "@backstage/core-app-api": "^1.0.3", + "@backstage/cli": "^0.17.2", + "@backstage/test-utils": "^1.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/react-hooks": "^8.0.0", @@ -100,7 +101,7 @@ "@types/react-window": "^1.8.5", "@types/zen-observable": "^0.8.0", "cross-fetch": "^3.1.5", - "msw": "^0.35.0" + "msw": "^0.42.0" }, "files": [ "dist" diff --git a/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx b/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx index 6db8e2dd02..9f447f80cc 100644 --- a/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx +++ b/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx @@ -40,7 +40,7 @@ import { ARROW_MARKER_ID } from './constants'; * * @public * @remarks - * and are useful when rendering custom or edge labels + * `` and `` are useful when rendering custom or edge labels */ export interface DependencyGraphProps extends React.SVGProps { diff --git a/packages/core-components/src/components/Table/Table.test.tsx b/packages/core-components/src/components/Table/Table.test.tsx index 12431dea59..ff17409502 100644 --- a/packages/core-components/src/components/Table/Table.test.tsx +++ b/packages/core-components/src/components/Table/Table.test.tsx @@ -160,4 +160,55 @@ describe('', () => { ); expect(rendered.getByText('EMPTY')).toBeInTheDocument(); }); + + describe('with custom components', () => { + const CustomRow = ({ data }: any) => { + return ( + + + + + ); + }; + + it('should not override the toolbar implementation', async () => { + const rendered = await renderInTestApp( +
customised cell {data.col1}customised cell {data.col2}
EMPTY} + columns={minProps.columns} + data={minProps.data} + filters={[ + { + column: column1.title, + type: 'select', + }, + ]} + components={{ + Row: CustomRow, + }} + />, + ); + + expect(rendered.getByText('Filters (0)')).toBeInTheDocument(); + }); + + it('should render the provided custom row component correctly', async () => { + const rendered = await renderInTestApp( +
EMPTY} + columns={minProps.columns} + data={minProps.data} + components={{ + Row: CustomRow, + }} + />, + ); + + expect( + rendered.getByText('customised cell first value, first row'), + ).toBeInTheDocument(); + }); + }); }); diff --git a/packages/core-components/src/components/Table/Table.tsx b/packages/core-components/src/components/Table/Table.tsx index 84e88ae01e..65edb5b69b 100644 --- a/packages/core-components/src/components/Table/Table.tsx +++ b/packages/core-components/src/components/Table/Table.tsx @@ -303,6 +303,7 @@ export function Table(props: TableProps) { initialState, emptyContent, onStateChange, + components, ...restProps } = props; const tableClasses = useTableStyles(); @@ -493,6 +494,7 @@ export function Table(props: TableProps) { Header: StyledMTableHeader, Toolbar, Body, + ...components, }} options={{ ...defaultOptions, ...options }} columns={MTColumns} diff --git a/packages/core-components/src/layout/HeaderTabs/HeaderTabs.tsx b/packages/core-components/src/layout/HeaderTabs/HeaderTabs.tsx index bee4bdfce2..0a937683a5 100644 --- a/packages/core-components/src/layout/HeaderTabs/HeaderTabs.tsx +++ b/packages/core-components/src/layout/HeaderTabs/HeaderTabs.tsx @@ -35,6 +35,7 @@ const useStyles = makeStyles( gridArea: 'pageSubheader', backgroundColor: theme.palette.background.paper, paddingLeft: theme.spacing(3), + minWidth: 0, }, defaultTab: { padding: theme.spacing(3, 3), diff --git a/packages/core-components/src/layout/ItemCard/ItemCardHeader.tsx b/packages/core-components/src/layout/ItemCard/ItemCardHeader.tsx index 3209848ecb..a34d773ce3 100644 --- a/packages/core-components/src/layout/ItemCard/ItemCardHeader.tsx +++ b/packages/core-components/src/layout/ItemCard/ItemCardHeader.tsx @@ -64,7 +64,7 @@ export type ItemCardHeaderProps = Partial> & { * A simple card header, rendering a default look for "item cards" - cards that * are arranged in a grid for users to select among several options. * - * This component expects to be placed within a MUI . + * This component expects to be placed within a MUI ``. * * Styles for the header can be overridden using the `classes` prop, e.g.: * diff --git a/packages/core-components/src/layout/Page/Page.tsx b/packages/core-components/src/layout/Page/Page.tsx index 9bfc16fea3..1abb8da93b 100644 --- a/packages/core-components/src/layout/Page/Page.tsx +++ b/packages/core-components/src/layout/Page/Page.tsx @@ -14,10 +14,10 @@ * limitations under the License. */ -import React, { useContext } from 'react'; +import React from 'react'; import { BackstageTheme } from '@backstage/theme'; import { makeStyles, ThemeProvider } from '@material-ui/core/styles'; -import { SidebarPinStateContext } from '../Sidebar/Page'; +import { useSidebarPinState } from '../Sidebar/SidebarPinStateContext'; export type PageClassKey = 'root'; @@ -43,7 +43,7 @@ type Props = { export function Page(props: Props) { const { themeId, children } = props; - const { isMobile } = useContext(SidebarPinStateContext); + const { isMobile } = useSidebarPinState(); const classes = useStyles({ isMobile }); return ( - , + , ); } diff --git a/packages/core-components/src/layout/Sidebar/Bar.tsx b/packages/core-components/src/layout/Sidebar/Bar.tsx index e78706bd62..c86f60a0a2 100644 --- a/packages/core-components/src/layout/Sidebar/Bar.tsx +++ b/packages/core-components/src/layout/Sidebar/Bar.tsx @@ -25,14 +25,15 @@ import { makeSidebarConfig, makeSidebarSubmenuConfig, SidebarConfig, - SidebarContext, SidebarConfigContext, SubmenuConfig, SidebarOptions, SubmenuOptions, } from './config'; import { BackstageTheme } from '@backstage/theme'; -import { SidebarPinStateContext, useContent } from './Page'; +import { useContent } from './Page'; +import { SidebarOpenStateProvider } from './SidebarOpenStateContext'; +import { useSidebarPinState } from './SidebarPinStateContext'; import { MobileSidebar } from './MobileSidebar'; /** @public */ @@ -133,9 +134,7 @@ const DesktopSidebar = (props: DesktopSidebarProps) => { ); const [state, setState] = useState(State.Closed); const hoverTimerRef = useRef(); - const { isPinned, toggleSidebarPinState } = useContext( - SidebarPinStateContext, - ); + const { isPinned, toggleSidebarPinState } = useSidebarPinState(); const handleOpen = () => { if (isPinned || disableExpandOnHover) { @@ -191,7 +190,7 @@ const DesktopSidebar = (props: DesktopSidebarProps) => { return ( ); }; @@ -226,7 +225,7 @@ export const Sidebar = (props: SidebarProps) => { props.submenuOptions ?? {}, ); const { children, disableExpandOnHover, openDelayMs, closeDelayMs } = props; - const { isMobile } = useContext(SidebarPinStateContext); + const { isMobile } = useSidebarPinState(); return isMobile ? ( {children} diff --git a/packages/core-components/src/layout/Sidebar/Intro.tsx b/packages/core-components/src/layout/Sidebar/Intro.tsx index d1d8f4b2a2..05d580c960 100644 --- a/packages/core-components/src/layout/Sidebar/Intro.tsx +++ b/packages/core-components/src/layout/Sidebar/Intro.tsx @@ -25,10 +25,10 @@ import { useLocalStorageValue } from '@react-hookz/web'; import { SidebarConfigContext, SidebarConfig, - SidebarContext, SIDEBAR_INTRO_LOCAL_STORAGE, } from './config'; import { SidebarDivider } from './Items'; +import { useSidebarOpenState } from './SidebarOpenStateContext'; /** @public */ export type SidebarIntroClassKey = @@ -151,7 +151,7 @@ const recentlyViewedIntroText = 'And your recently viewed plugins will pop up here!'; export function SidebarIntro(_props: {}) { - const { isOpen } = useContext(SidebarContext); + const { isOpen } = useSidebarOpenState(); const defaultValue = { starredItemsDismissed: false, recentlyViewedItemsDismissed: false, diff --git a/packages/core-components/src/layout/Sidebar/Items.tsx b/packages/core-components/src/layout/Sidebar/Items.tsx index c004f6904c..0413864084 100644 --- a/packages/core-components/src/layout/Sidebar/Items.tsx +++ b/packages/core-components/src/layout/Sidebar/Items.tsx @@ -48,7 +48,6 @@ import { useResolvedPath, } from 'react-router-dom'; import { - SidebarContext, SidebarConfigContext, SidebarItemWithSubmenuContext, SidebarConfig, @@ -62,6 +61,7 @@ import DoubleArrowLeft from './icons/DoubleArrowLeft'; import DoubleArrowRight from './icons/DoubleArrowRight'; import { isLocationMatch } from './utils'; import { Location } from 'history'; +import { useSidebarOpenState } from './SidebarOpenStateContext'; /** @public */ export type SidebarItemClassKey = @@ -259,6 +259,7 @@ type SidebarItemBaseProps = { icon: IconComponent; text?: string; hasNotifications?: boolean; + hasSubmenu?: boolean; disableHighlight?: boolean; className?: string; }; @@ -356,6 +357,7 @@ const SidebarItemBase = forwardRef((props, ref) => { icon: Icon, text, hasNotifications = false, + hasSubmenu = false, disableHighlight = false, onClick, children, @@ -367,15 +369,15 @@ const SidebarItemBase = forwardRef((props, ref) => { // XXX (@koroeskohr): unsure this is optimal. But I just really didn't want to have the item component // depend on the current location, and at least have it being optionally forced to selected. // Still waiting on a Q answered to fine tune the implementation - const { isOpen } = useContext(SidebarContext); + const { isOpen } = useSidebarOpenState(); const divStyle = - !isOpen && children ? { display: 'flex', marginLeft: '24px' } : {}; + !isOpen && hasSubmenu ? { display: 'flex', marginLeft: '24px' } : {}; const displayItemIcon = (
- {!isOpen && children ? : <>} + {!isOpen && hasSubmenu ? : <>}
); @@ -492,6 +494,7 @@ const SidebarItemWithSubmenu = ({ className={classnames(isHoveredOn && classes.highlighted)} > @@ -668,7 +671,7 @@ export const SidebarScrollWrapper = styled('div')(({ theme }) => { export const SidebarExpandButton = () => { const { sidebarConfig } = useContext(SidebarConfigContext); const classes = useMemoStyles(sidebarConfig); - const { isOpen, setOpen } = useContext(SidebarContext); + const { isOpen, setOpen } = useSidebarOpenState(); const isSmallScreen = useMediaQuery( theme => theme.breakpoints.down('md'), { noSsr: true }, diff --git a/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx b/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx index 111bf38314..b51e3cbc0d 100644 --- a/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx +++ b/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx @@ -27,8 +27,9 @@ import MenuIcon from '@material-ui/icons/Menu'; import { orderBy } from 'lodash'; import React, { createContext, useEffect, useState, useContext } from 'react'; import { useLocation } from 'react-router'; +import { SidebarOpenStateProvider } from './SidebarOpenStateContext'; import { SidebarGroup } from './SidebarGroup'; -import { SidebarConfigContext, SidebarContext, SidebarConfig } from './config'; +import { SidebarConfigContext, SidebarConfig } from './config'; /** * Type of `MobileSidebarContext` @@ -207,7 +208,7 @@ export const MobileSidebar = (props: MobileSidebarProps) => { !sidebarGroups[selectedMenuItemIndex].props.to; return ( - {} }}> + {} }}> @@ -231,6 +232,6 @@ export const MobileSidebar = (props: MobileSidebarProps) => { {sidebarGroups} - + ); }; diff --git a/packages/core-components/src/layout/Sidebar/Page.tsx b/packages/core-components/src/layout/Sidebar/Page.tsx index 9bf8105f81..bdf41b5dd7 100644 --- a/packages/core-components/src/layout/Sidebar/Page.tsx +++ b/packages/core-components/src/layout/Sidebar/Page.tsx @@ -29,6 +29,7 @@ import { SidebarConfigContext, SidebarConfig } from './config'; import { BackstageTheme } from '@backstage/theme'; import { LocalStorage } from './localStorage'; import useMediaQuery from '@material-ui/core/useMediaQuery'; +import { SidebarPinStateProvider } from './SidebarPinStateContext'; export type SidebarPageClassKey = 'root'; @@ -62,17 +63,6 @@ const useStyles = makeStyles< { name: 'BackstageSidebarPage' }, ); -/** - * Type of `SidebarPinStateContext` - * - * @public - */ -export type SidebarPinStateContextType = { - isPinned: boolean; - toggleSidebarPinState: () => any; - isMobile?: boolean; -}; - /** * Props for SidebarPage * @@ -82,19 +72,6 @@ export type SidebarPageProps = { children?: React.ReactNode; }; -/** - * Contains the state on how the `Sidebar` is rendered - * - * @public - */ -export const SidebarPinStateContext = createContext( - { - isPinned: true, - toggleSidebarPinState: () => {}, - isMobile: false, - }, -); - type PageContextType = { content: { contentRef?: React.MutableRefObject; @@ -137,7 +114,7 @@ export function SidebarPage(props: SidebarPageProps) { const classes = useStyles({ isPinned, sidebarConfig }); return ( -
{props.children}
-
+ ); } diff --git a/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx b/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx index 55b5e770b0..1158cc14df 100644 --- a/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx +++ b/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx @@ -22,7 +22,7 @@ import BottomNavigationAction, { import { makeStyles } from '@material-ui/core/styles'; import React, { useContext } from 'react'; import { useLocation } from 'react-router-dom'; -import { SidebarPinStateContext } from '.'; +import { useSidebarPinState } from '.'; import { Link } from '../../components'; import { SidebarConfigContext, SidebarConfig } from './config'; import { MobileSidebarContext } from './MobileSidebar'; @@ -122,7 +122,7 @@ const MobileSidebarGroup = (props: SidebarGroupProps) => { */ export const SidebarGroup = (props: SidebarGroupProps) => { const { children, to, label, icon, value } = props; - const { isMobile } = useContext(SidebarPinStateContext); + const { isMobile } = useSidebarPinState(); return isMobile ? ( diff --git a/packages/core-components/src/layout/Sidebar/SidebarOpenStateContext.test.tsx b/packages/core-components/src/layout/Sidebar/SidebarOpenStateContext.test.tsx new file mode 100644 index 0000000000..f1900f025c --- /dev/null +++ b/packages/core-components/src/layout/Sidebar/SidebarOpenStateContext.test.tsx @@ -0,0 +1,115 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React, { ReactNode, useContext } from 'react'; +import { renderWithEffects } from '@backstage/test-utils'; +import { waitFor } from '@testing-library/react'; +import { renderHook, act } from '@testing-library/react-hooks'; +import { + LegacySidebarContext, + SidebarOpenStateProvider, + useSidebarOpenState, +} from './SidebarOpenStateContext'; + +describe('SidebarOpenStateContext', () => { + describe('SidebarOpenStateProvider', () => { + it('should render children', async () => { + const { findByText } = await renderWithEffects( + {} }}> + Child + , + ); + expect(await findByText('Child')).toBeInTheDocument(); + }); + + it('should provide the legacy context as well, for now', async () => { + const LegacyContextSpy = () => { + const { isOpen } = useContext(LegacySidebarContext); + return <>{String(isOpen)}; + }; + + const { findByText } = await renderWithEffects( + {}, + }} + > + + , + ); + + expect(await findByText('true')).toBeInTheDocument(); + }); + }); + + describe('useSidebarOpenState', () => { + it('can be invoked within legacy context', () => { + const wrapper = ({ children }: { children: ReactNode }) => ( + {}, + }} + > + {children} + + ); + + const { result } = renderHook(() => useSidebarOpenState(), { + wrapper, + }); + + expect(result.current.isOpen).toBe(true); + expect(typeof result.current.setOpen).toBe('function'); + }); + + it('does not need to be invoked within provider', () => { + const { result } = renderHook(() => useSidebarOpenState()); + expect(result.current.isOpen).toBe(false); + expect(typeof result.current.setOpen).toBe('function'); + }); + + it('should read and update state', async () => { + let actualValue = true; + const wrapper = ({ children }: { children: ReactNode }) => ( + { + actualValue = value; + }, + }} + > + {children} + + ); + const { result, rerender } = renderHook(() => useSidebarOpenState(), { + wrapper, + }); + + expect(result.current.isOpen).toBe(true); + + act(() => { + result.current.setOpen(false); + rerender(); + }); + + await waitFor(() => { + expect(result.current.isOpen).toBe(false); + }); + }); + }); +}); diff --git a/packages/core-components/src/layout/Sidebar/SidebarOpenStateContext.tsx b/packages/core-components/src/layout/Sidebar/SidebarOpenStateContext.tsx new file mode 100644 index 0000000000..7cb0e5b2c3 --- /dev/null +++ b/packages/core-components/src/layout/Sidebar/SidebarOpenStateContext.tsx @@ -0,0 +1,115 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React, { createContext, ReactNode, useContext } from 'react'; +import { + createVersionedContext, + createVersionedValueMap, +} from '@backstage/version-bridge'; + +/** + * Types for the `SidebarContext` + * + * @public @deprecated + * Use `SidebarOpenState` instead. + */ +export type SidebarContextType = { + isOpen: boolean; + setOpen: (open: boolean) => void; +}; + +/** + * The open state of the sidebar. + * + * @public + */ +export type SidebarOpenState = { + /** + * Whether or not the sidebar is open and full-width. When `false`, the + * sidebar is "closed" and typically only shows icons with no text. + */ + isOpen: boolean; + + /** + * A function to set whether or not the sidebar is open. Pass `true` to open + * the sidebar. Pass `false` to close it. + */ + setOpen: (open: boolean) => void; +}; + +const defaultSidebarOpenStateContext = { + isOpen: false, + setOpen: () => {}, +}; + +/** + * Context whether the `Sidebar` is open + * + * @public @deprecated + * Use `` + `useSidebarOpenState()` instead. + */ +export const LegacySidebarContext = createContext( + defaultSidebarOpenStateContext, +); + +const VersionedSidebarContext = createVersionedContext<{ + 1: SidebarOpenState; +}>('sidebar-open-state-context'); + +/** + * Provides context for reading and updating sidebar state. + * + * @public + */ +export const SidebarOpenStateProvider = ({ + children, + value, +}: { + children: ReactNode; + value: SidebarOpenState; +}) => ( + + + {children} + + +); + +/** + * Hook to read and update the sidebar's open state, which controls whether or + * not the sidebar is open and full-width, or closed and only displaying icons. + * + * @public + */ +export const useSidebarOpenState = (): SidebarOpenState => { + const versionedOpenStateContext = useContext(VersionedSidebarContext); + const legacyOpenStateContext = useContext(LegacySidebarContext); + + // Invoked from outside a SidebarOpenStateProvider: check for the legacy + // context's value, but otherwise return the default. + if (versionedOpenStateContext === undefined) { + return legacyOpenStateContext || defaultSidebarOpenStateContext; + } + + const openStateContext = versionedOpenStateContext.atVersion(1); + if (openStateContext === undefined) { + throw new Error('No context found for version 1.'); + } + + return openStateContext; +}; diff --git a/packages/core-components/src/layout/Sidebar/SidebarPinStateContext.test.tsx b/packages/core-components/src/layout/Sidebar/SidebarPinStateContext.test.tsx new file mode 100644 index 0000000000..8bd1597725 --- /dev/null +++ b/packages/core-components/src/layout/Sidebar/SidebarPinStateContext.test.tsx @@ -0,0 +1,126 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React, { ReactNode, useContext } from 'react'; +import { renderWithEffects } from '@backstage/test-utils'; +import { waitFor } from '@testing-library/react'; +import { renderHook, act } from '@testing-library/react-hooks'; +import { + LegacySidebarPinStateContext, + SidebarPinStateProvider, + useSidebarPinState, +} from './SidebarPinStateContext'; + +describe('SidebarPinStateContext', () => { + describe('SidebarPinStateProvider', () => { + it('should render children', async () => { + const { findByText } = await renderWithEffects( + {}, + }} + > + Child + , + ); + expect(await findByText('Child')).toBeInTheDocument(); + }); + + it('should provide the legacy context as well, for now', async () => { + const LegacyContextSpy = () => { + const { isMobile } = useContext(LegacySidebarPinStateContext); + return <>{String(isMobile)}; + }; + + const { findByText } = await renderWithEffects( + {}, + }} + > + + , + ); + + expect(await findByText('true')).toBeInTheDocument(); + }); + }); + + describe('useSidebarPinState', () => { + it('can be invoked within legacy context', () => { + const wrapper = ({ children }: { children: ReactNode }) => ( + {}, + }} + > + {children} + + ); + + const { result } = renderHook(() => useSidebarPinState(), { + wrapper, + }); + + expect(result.current.isPinned).toBe(true); + expect(result.current.isMobile).toBe(true); + expect(typeof result.current.toggleSidebarPinState).toBe('function'); + }); + + it('does not need to be invoked within provider', () => { + const { result } = renderHook(() => useSidebarPinState()); + expect(result.current.isPinned).toBe(true); + expect(result.current.isMobile).toBe(false); + expect(typeof result.current.toggleSidebarPinState).toBe('function'); + }); + + it('should read and update state', async () => { + let actualValue = true; + const wrapper = ({ children }: { children: ReactNode }) => ( + { + actualValue = !actualValue; + }, + }} + > + {children} + + ); + const { result, rerender } = renderHook(() => useSidebarPinState(), { + wrapper, + }); + + expect(result.current.isPinned).toBe(true); + + act(() => { + result.current.toggleSidebarPinState(); + rerender(); + }); + + await waitFor(() => { + expect(result.current.isPinned).toBe(false); + }); + }); + }); +}); diff --git a/packages/core-components/src/layout/Sidebar/SidebarPinStateContext.tsx b/packages/core-components/src/layout/Sidebar/SidebarPinStateContext.tsx new file mode 100644 index 0000000000..e00ed259e9 --- /dev/null +++ b/packages/core-components/src/layout/Sidebar/SidebarPinStateContext.tsx @@ -0,0 +1,121 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { + createVersionedContext, + createVersionedValueMap, +} from '@backstage/version-bridge'; +import React, { createContext, ReactNode, useContext } from 'react'; + +/** + * Type of `SidebarPinStateContext` + * + * @public @deprecated + * Use `SidebarPinState` instead. + */ +export type SidebarPinStateContextType = { + isPinned: boolean; + toggleSidebarPinState: () => any; + isMobile?: boolean; +}; + +/** + * The pin state of the sidebar. + * + * @public + */ +export type SidebarPinState = { + /** + * Whether or not the sidebar is pinned to the `open` state. When `isPinned` + * is `false`, the sidebar opens and closes on hover. When `true`, the + * sidebar is permanently opened, regardless of user interaction. + */ + isPinned: boolean; + + /** + * A function to toggle the pin state of the sidebar. + */ + toggleSidebarPinState: () => any; + + /** + * Whether or not the sidebar is or should be rendered in a mobile-optimized + * way. + */ + isMobile?: boolean; +}; + +const defaultSidebarPinStateContext = { + isPinned: true, + toggleSidebarPinState: () => {}, + isMobile: false, +}; + +/** + * Contains the state on how the `Sidebar` is rendered + * + * @public @deprecated + * Use `` + `useSidebarPinState()` instead. + */ +export const LegacySidebarPinStateContext = + createContext(defaultSidebarPinStateContext); + +const VersionedSidebarPinStateContext = createVersionedContext<{ + 1: SidebarPinState; +}>('sidebar-pin-state-context'); + +/** + * Provides state for how the `Sidebar` is rendered + * + * @public + */ +export const SidebarPinStateProvider = ({ + children, + value, +}: { + children: ReactNode; + value: SidebarPinStateContextType; +}) => ( + + + {children} + + +); + +/** + * Hook to read and update sidebar pin state, which controls whether or not the + * sidebar is pinned open. + * + * @public + */ +export const useSidebarPinState = (): SidebarPinState => { + const versionedPinStateContext = useContext(VersionedSidebarPinStateContext); + const legacyPinStateContext = useContext(LegacySidebarPinStateContext); + + // Invoked from outside a SidebarPinStateProvider: check for the legacy + // context's value, but otherwise return the default. + if (versionedPinStateContext === undefined) { + return legacyPinStateContext || defaultSidebarPinStateContext; + } + + const pinStateContext = versionedPinStateContext.atVersion(1); + if (pinStateContext === undefined) { + throw new Error('No context found for version 1.'); + } + + return pinStateContext; +}; diff --git a/packages/core-components/src/layout/Sidebar/SidebarSubmenu.tsx b/packages/core-components/src/layout/Sidebar/SidebarSubmenu.tsx index d1b79263eb..438e580ef4 100644 --- a/packages/core-components/src/layout/Sidebar/SidebarSubmenu.tsx +++ b/packages/core-components/src/layout/Sidebar/SidebarSubmenu.tsx @@ -19,10 +19,10 @@ import classnames from 'classnames'; import React, { ReactNode, useContext, useEffect, useState } from 'react'; import { SidebarItemWithSubmenuContext, - SidebarContext, SidebarConfigContext, SubmenuConfig, } from './config'; +import { useSidebarOpenState } from './SidebarOpenStateContext'; import { BackstageTheme } from '@backstage/theme'; const useStyles = makeStyles< @@ -105,7 +105,7 @@ export type SidebarSubmenuProps = { * @public */ export const SidebarSubmenu = (props: SidebarSubmenuProps) => { - const { isOpen } = useContext(SidebarContext); + const { isOpen } = useSidebarOpenState(); const { sidebarConfig, submenuConfig } = useContext(SidebarConfigContext); const left = isOpen ? sidebarConfig.drawerWidthOpen diff --git a/packages/core-components/src/layout/Sidebar/config.ts b/packages/core-components/src/layout/Sidebar/config.ts index 08d469d5a3..bb0ff207fb 100644 --- a/packages/core-components/src/layout/Sidebar/config.ts +++ b/packages/core-components/src/layout/Sidebar/config.ts @@ -101,22 +101,6 @@ export const makeSidebarSubmenuConfig = ( export const SIDEBAR_INTRO_LOCAL_STORAGE = '@backstage/core/sidebar-intro-dismissed'; -/** - * Types for the `SidebarContext` - */ -export type SidebarContextType = { - isOpen: boolean; - setOpen: (open: boolean) => void; -}; - -/** - * Context whether the `Sidebar` is open - */ -export const SidebarContext = createContext({ - isOpen: false, - setOpen: () => {}, -}); - export type SidebarConfigContextType = { sidebarConfig: SidebarConfig; submenuConfig: SubmenuConfig; diff --git a/packages/core-components/src/layout/Sidebar/index.ts b/packages/core-components/src/layout/Sidebar/index.ts index ecc573fbda..4c91ea6a8f 100644 --- a/packages/core-components/src/layout/Sidebar/index.ts +++ b/packages/core-components/src/layout/Sidebar/index.ts @@ -27,16 +27,8 @@ export type { SidebarSubmenuItemDropdownItem, } from './SidebarSubmenuItem'; export type { SidebarClassKey, SidebarProps } from './Bar'; -export { - SidebarPage, - SidebarPinStateContext as SidebarPinStateContext, - useContent, -} from './Page'; -export type { - SidebarPinStateContextType as SidebarPinStateContextType, - SidebarPageClassKey, - SidebarPageProps, -} from './Page'; +export { SidebarPage, useContent } from './Page'; +export type { SidebarPageClassKey, SidebarPageProps } from './Page'; export { SidebarDivider, SidebarItem, @@ -54,13 +46,23 @@ export type { } from './Items'; export { IntroCard, SidebarIntro } from './Intro'; export type { SidebarIntroClassKey } from './Intro'; +export { SIDEBAR_INTRO_LOCAL_STORAGE, sidebarConfig } from './config'; +export type { SidebarOptions, SubmenuOptions } from './config'; export { - SIDEBAR_INTRO_LOCAL_STORAGE, - SidebarContext, - sidebarConfig, -} from './config'; + LegacySidebarContext as SidebarContext, + SidebarOpenStateProvider, + useSidebarOpenState, +} from './SidebarOpenStateContext'; export type { SidebarContextType, - SidebarOptions, - SubmenuOptions, -} from './config'; + SidebarOpenState, +} from './SidebarOpenStateContext'; +export { + LegacySidebarPinStateContext as SidebarPinStateContext, + SidebarPinStateProvider, + useSidebarPinState, +} from './SidebarPinStateContext'; +export type { + SidebarPinStateContextType, + SidebarPinState, +} from './SidebarPinStateContext'; diff --git a/packages/core-plugin-api/CHANGELOG.md b/packages/core-plugin-api/CHANGELOG.md index 44104208ef..8b363df966 100644 --- a/packages/core-plugin-api/CHANGELOG.md +++ b/packages/core-plugin-api/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/core-plugin-api +## 1.0.3 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. + +## 1.0.3-next.0 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. + ## 1.0.2 ### Patch Changes diff --git a/packages/core-plugin-api/package.json b/packages/core-plugin-api/package.json index 4377761d89..8d525b5f0f 100644 --- a/packages/core-plugin-api/package.json +++ b/packages/core-plugin-api/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/core-plugin-api", "description": "Core API used by Backstage plugins", - "version": "1.0.2", + "version": "1.0.3", "private": false, "publishConfig": { "access": "public", @@ -46,9 +46,9 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/core-app-api": "^1.0.2", - "@backstage/test-utils": "^1.1.0", + "@backstage/cli": "^0.17.2", + "@backstage/core-app-api": "^1.0.3", + "@backstage/test-utils": "^1.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/react-hooks": "^8.0.0", @@ -58,7 +58,7 @@ "@types/prop-types": "^15.7.3", "@types/zen-observable": "^0.8.0", "cross-fetch": "^3.1.5", - "msw": "^0.35.0" + "msw": "^0.42.0" }, "files": [ "dist" diff --git a/packages/create-app/CHANGELOG.md b/packages/create-app/CHANGELOG.md index 6993b4631e..775aed3faa 100644 --- a/packages/create-app/CHANGELOG.md +++ b/packages/create-app/CHANGELOG.md @@ -1,5 +1,294 @@ # @backstage/create-app +## 0.4.28 + +### Patch Changes + +- 881fbd7e8d: Register `TechDocs` addons on catalog entity pages, follow the steps below to add them manually: + + ```diff + // packages/app/src/components/catalog/EntityPage.tsx + + + import { TechDocsAddons } from '@backstage/plugin-techdocs-react'; + + import { + + ReportIssue, + + } from '@backstage/plugin-techdocs-module-addons-contrib'; + + + const techdocsContent = ( + + + + + + + + + + + + ); + + const defaultEntityPage = ( + ... + + + {techdocsContent} + + ... + ); + + const serviceEntityPage = ( + ... + + + {techdocsContent} + + ... + ); + + const websiteEntityPage = ( + ... + + + {techdocsContent} + + ... + ); + ``` + +- 0e870fe0ac: Removed peer dependencies, as they are no longer needed. +- bff65e6958: Use of `SidebarContext` has been deprecated and will be removed in a future release. Instead, `useSidebarOpenState()` should be used to consume the context and `` should be used to provide it. + + To prepare your app, update `packages/app/src/components/Root/Root.tsx` as follows: + + ```diff + import { + Sidebar, + sidebarConfig, + - SidebarContext + SidebarDivider, + // ... + SidebarSpace, + + useSidebarOpenState, + } from '@backstage/core-components'; + + // ... + + const SidebarLogo = () => { + const classes = useSidebarLogoStyles(); + - const { isOpen } = useContext(SidebarContext); + + const { isOpen } = useSidebarOpenState(); + + // ... + }; + ``` + +- 935d8515da: Updated the `--version` flag to output the version of the current backstage release instead of the version of create-app. +- 18d4c3e50a: Updated `app-config.production.yaml` to specify an empty list of catalog locations. This is done to prevent example locations stored in `app-config.yaml` from being loaded as these are examples. +- 1f70704580: Accessibility updates: + + - Added `aria-label` to the sidebar Logo link. To enable this for an existing app, please make the following changes: + + `packages/app/src/components/Root/Root.tsx` + + ```diff + const SidebarLogo = () => { + const classes = useSidebarLogoStyles(); + const { isOpen } = useContext(SidebarContext); + + return ( +
+ + {isOpen ? : } + +
+ ); + }; + ``` + +- 30f04d1497: Components ``, ``, ``, and `` are now deprecated in `@backstage/plugin-search` and should be imported from `@backstage/plugin-search-react` instead. + + To upgrade your App, update the following in `packages/app/src/components/search/SearchPage.tsx`: + + ```diff + import { + DefaultResultListItem + SearchBar + SearchFilter + SearchResult + - } from `@backstage/plugin-search`; + + } from `@backstage/plugin-search-react`; + ``` + +- f7f5a6c6a3: It's now possible to pass result item components a `rank`, which is captured by the analytics API when a user clicks on a search result. To apply this change, update your `/packages/app/src/components/search/SearchPage.tsx` in the following way: + + ```diff + // ... + + {({ results }) => ( + + - {results.map(({ type, document, highlight }) => { + + {results.map(({ type, document, highlight, rank }) => { + switch (type) { + case 'software-catalog': + return ( + + ); + case 'techdocs': + return ( + + ); + default: + return ( + + ); + } + })} + + )} + + // ... + ``` + + If you have implemented a custom Search Modal or other custom search experience, you will want to make similar changes in those components. + +- aaf7652084: Bump version of `cypress` in newly scaffolded Backstage Applications. To apply this change to your own instance, please make the following change to `packages/app/package.json` under `devDependencies`. + + ```diff + - "cypress": "^7.3.0", + + "cypress": "^9.7.0", + ``` + +- 141a1caebe: Updated the auth backend setup in the template to include a guest sign-in resolver in order to make it quicker to get up and running with a basic sign-in setup. There is no need to update existing apps to match this change, but in case you want to use the guest sign-in resolver you can find it at https://backstage.io/docs/auth/identity-resolver#guest-sign-in-resolver + +## 0.4.28-next.2 + +### Patch Changes + +- aaf7652084: Bump version of `cypress` in newly scaffolded Backstage Applications. To apply this change to your own instance, please make the following change to `packages/app/package.json` under `devDependencies`. + + ```diff + - "cypress": "^7.3.0", + + "cypress": "^9.7.0", + ``` + +## 0.4.28-next.1 + +### Patch Changes + +- bff65e6958: Use of `SidebarContext` has been deprecated and will be removed in a future release. Instead, `useSidebarOpenState()` should be used to consume the context and `` should be used to provide it. + + To prepare your app, update `packages/app/src/components/Root/Root.tsx` as follows: + + ```diff + import { + Sidebar, + sidebarConfig, + - SidebarContext + SidebarDivider, + // ... + SidebarSpace, + + useSidebarOpenState, + } from '@backstage/core-components'; + + // ... + + const SidebarLogo = () => { + const classes = useSidebarLogoStyles(); + - const { isOpen } = useContext(SidebarContext); + + const { isOpen } = useSidebarOpenState(); + + // ... + }; + ``` + +## 0.4.28-next.0 + +### Patch Changes + +- 881fbd7e8d: Register `TechDocs` addons on catalog entity pages, follow the steps below to add them manually: + + ```diff + // packages/app/src/components/catalog/EntityPage.tsx + + + import { TechDocsAddons } from '@backstage/plugin-techdocs-react'; + + import { + + ReportIssue, + + } from '@backstage/plugin-techdocs-module-addons-contrib'; + + + const techdocsContent = ( + + + + + + + + + + + + ); + + const defaultEntityPage = ( + ... + + + {techdocsContent} + + ... + ); + + const serviceEntityPage = ( + ... + + + {techdocsContent} + + ... + ); + + const websiteEntityPage = ( + ... + + + {techdocsContent} + + ... + ); + ``` + +- 935d8515da: Updated the `--version` flag to output the version of the current backstage release instead of the version of create-app. +- 1f70704580: Accessibility updates: + + - Added `aria-label` to the sidebar Logo link. To enable this for an existing app, please make the following changes: + + `packages/app/src/components/Root/Root.tsx` + + ```diff + const SidebarLogo = () => { + const classes = useSidebarLogoStyles(); + const { isOpen } = useContext(SidebarContext); + + return ( +
+ + {isOpen ? : } + +
+ ); + }; + ``` + ## 0.4.27 ### Patch Changes diff --git a/packages/create-app/package.json b/packages/create-app/package.json index 2fe8398309..bca9fe9b68 100644 --- a/packages/create-app/package.json +++ b/packages/create-app/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/create-app", "description": "A CLI that helps you create your own Backstage app", - "version": "0.4.27", + "version": "0.4.28", "private": false, "publishConfig": { "access": "public" @@ -50,54 +50,6 @@ "mock-fs": "^5.1.1", "ts-node": "^10.0.0" }, - "peerDependencies": { - "@backstage/app-defaults": "", - "@backstage/backend-common": "", - "@backstage/backend-tasks": "", - "@backstage/catalog-client": "", - "@backstage/catalog-model": "", - "@backstage/cli": "", - "@backstage/config": "", - "@backstage/core-app-api": "", - "@backstage/core-components": "", - "@backstage/core-plugin-api": "", - "@backstage/errors": "", - "@backstage/integration-react": "", - "@backstage/plugin-api-docs": "", - "@backstage/plugin-app-backend": "", - "@backstage/plugin-auth-backend": "", - "@backstage/plugin-catalog": "", - "@backstage/plugin-catalog-backend": "", - "@backstage/plugin-catalog-common": "", - "@backstage/plugin-catalog-graph": "", - "@backstage/plugin-catalog-import": "", - "@backstage/plugin-catalog-react": "", - "@backstage/plugin-circleci": "", - "@backstage/plugin-explore": "", - "@backstage/plugin-github-actions": "", - "@backstage/plugin-lighthouse": "", - "@backstage/plugin-org": "", - "@backstage/plugin-permission-common": "", - "@backstage/plugin-permission-node": "", - "@backstage/plugin-permission-react": "", - "@backstage/plugin-proxy-backend": "", - "@backstage/plugin-rollbar-backend": "", - "@backstage/plugin-scaffolder": "", - "@backstage/plugin-scaffolder-backend": "", - "@backstage/plugin-search": "", - "@backstage/plugin-search-react": "", - "@backstage/plugin-search-backend": "", - "@backstage/plugin-search-backend-module-pg": "", - "@backstage/plugin-search-backend-node": "", - "@backstage/plugin-tech-radar": "", - "@backstage/plugin-techdocs": "", - "@backstage/plugin-techdocs-react": "", - "@backstage/plugin-techdocs-module-addons-contrib": "", - "@backstage/plugin-techdocs-backend": "", - "@backstage/plugin-user-settings": "", - "@backstage/test-utils": "", - "@backstage/theme": "" - }, "nodemonConfig": { "watch": "./src", "exec": "bin/backstage-create-app", diff --git a/packages/create-app/src/lib/versions.ts b/packages/create-app/src/lib/versions.ts index 25007d7535..78b28b5811 100644 --- a/packages/create-app/src/lib/versions.ts +++ b/packages/create-app/src/lib/versions.ts @@ -20,9 +20,8 @@ This is a list of all packages used by the template. If dependencies are added or removed, this list should be updated as well. -The list, and the accompanying peerDependencies entries, are here to ensure correct versioning -and bumping of this package. Without this list the version would not be bumped unless we -manually trigger a release. +There is a release step that ensures that this package is always bumped with every +release, meaning these version will always be up to date. This does not create an actual dependency on these packages and does not bring in any code. Relative imports are used rather than package imports to make sure the packages aren't externalized. diff --git a/packages/create-app/templates/default-app/app-config.production.yaml b/packages/create-app/templates/default-app/app-config.production.yaml index b9dd00bb01..6535d967d5 100644 --- a/packages/create-app/templates/default-app/app-config.production.yaml +++ b/packages/create-app/templates/default-app/app-config.production.yaml @@ -32,3 +32,9 @@ backend: # ssl: # ca: # if you have a CA file and want to verify it you can uncomment this section # $file: /ca/server.crt + +catalog: + # Overrides the default list locations from app-config.yaml as these contain example data. + # See https://backstage.io/docs/features/software-catalog/software-catalog-overview#adding-components-to-the-catalog for more details + # on how to get entities into the catalog. + locations: [] diff --git a/packages/create-app/templates/default-app/package.json.hbs b/packages/create-app/templates/default-app/package.json.hbs index d4cf4fb5fe..47fb0d622f 100644 --- a/packages/create-app/templates/default-app/package.json.hbs +++ b/packages/create-app/templates/default-app/package.json.hbs @@ -35,7 +35,8 @@ "concurrently": "^6.0.0", "lerna": "^4.0.0", "prettier": "^2.3.2", - "typescript": "~4.6.4" + "typescript": "~4.6.4", + "node-gyp": "^9.0.0" }, "resolutions": { "@types/react": "^17", diff --git a/packages/create-app/templates/default-app/packages/app/cypress.json b/packages/create-app/templates/default-app/packages/app/cypress.json index 5de7ebffea..0cb845a86d 100644 --- a/packages/create-app/templates/default-app/packages/app/cypress.json +++ b/packages/create-app/templates/default-app/packages/app/cypress.json @@ -1,5 +1,6 @@ { "baseUrl": "http://localhost:3001", "fixturesFolder": false, - "pluginsFile": false + "pluginsFile": false, + "retries": 3 } diff --git a/packages/create-app/templates/default-app/packages/app/package.json.hbs b/packages/create-app/templates/default-app/packages/app/package.json.hbs index 9bf67f6e52..d3ba938960 100644 --- a/packages/create-app/templates/default-app/packages/app/package.json.hbs +++ b/packages/create-app/templates/default-app/packages/app/package.json.hbs @@ -50,7 +50,7 @@ "@types/node": "^14.14.32", "@types/react-dom": "*", "cross-env": "^7.0.0", - "cypress": "^7.3.0", + "cypress": "^9.7.0", "eslint-plugin-cypress": "^2.10.3", "start-server-and-test": "^1.10.11" }, @@ -63,7 +63,7 @@ "test:e2e": "cross-env PORT=3001 start-server-and-test start http://localhost:3001 cy:dev", "test:e2e:ci": "cross-env PORT=3001 start-server-and-test start http://localhost:3001 cy:run", "cy:dev": "cypress open", - "cy:run": "cypress run" + "cy:run": "cypress run --browser chrome" }, "browserslist": { "production": [ diff --git a/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx b/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx index d10eccf03a..b1164a32f0 100644 --- a/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx +++ b/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import React, { useContext, PropsWithChildren } from 'react'; +import React, { PropsWithChildren } from 'react'; import { Link, makeStyles } from '@material-ui/core'; import HomeIcon from '@material-ui/icons/Home'; import ExtensionIcon from '@material-ui/icons/Extension'; @@ -32,13 +32,13 @@ import { SidebarSearchModal } from '@backstage/plugin-search'; import { Sidebar, sidebarConfig, - SidebarContext, SidebarDivider, SidebarGroup, SidebarItem, SidebarPage, SidebarScrollWrapper, SidebarSpace, + useSidebarOpenState, } from '@backstage/core-components'; import MenuIcon from '@material-ui/icons/Menu'; import SearchIcon from '@material-ui/icons/Search'; @@ -60,7 +60,7 @@ const useSidebarLogoStyles = makeStyles({ const SidebarLogo = () => { const classes = useSidebarLogoStyles(); - const { isOpen } = useContext(SidebarContext); + const { isOpen } = useSidebarOpenState(); return (
diff --git a/packages/create-app/templates/default-app/packages/app/src/components/catalog/EntityPage.tsx b/packages/create-app/templates/default-app/packages/app/src/components/catalog/EntityPage.tsx index 84d094410b..6ec4da055d 100644 --- a/packages/create-app/templates/default-app/packages/app/src/components/catalog/EntityPage.tsx +++ b/packages/create-app/templates/default-app/packages/app/src/components/catalog/EntityPage.tsx @@ -68,6 +68,17 @@ import { RELATION_PROVIDES_API, } from '@backstage/catalog-model'; +import { TechDocsAddons } from '@backstage/plugin-techdocs-react'; +import { ReportIssue } from '@backstage/plugin-techdocs-module-addons-contrib'; + +const techdocsContent = ( + + + + + +); + const cicdContent = ( // This is an example of how you can implement your company's logic in entity page. // You can for example enforce that all components of type 'service' should use GitHubActions @@ -167,7 +178,7 @@ const serviceEntityPage = ( - + {techdocsContent} ); @@ -194,7 +205,7 @@ const websiteEntityPage = ( - + {techdocsContent} ); @@ -213,7 +224,7 @@ const defaultEntityPage = ( - + {techdocsContent} ); diff --git a/packages/create-app/templates/default-app/packages/app/src/components/search/SearchPage.tsx b/packages/create-app/templates/default-app/packages/app/src/components/search/SearchPage.tsx index d4c7c92346..928b8201ea 100644 --- a/packages/create-app/templates/default-app/packages/app/src/components/search/SearchPage.tsx +++ b/packages/create-app/templates/default-app/packages/app/src/components/search/SearchPage.tsx @@ -8,14 +8,14 @@ import { } from '@backstage/plugin-catalog-react'; import { TechDocsSearchResultListItem } from '@backstage/plugin-techdocs'; +import { SearchType } from '@backstage/plugin-search'; import { + DefaultResultListItem, SearchBar, SearchFilter, SearchResult, - SearchType, - DefaultResultListItem, -} from '@backstage/plugin-search'; -import { useSearch } from '@backstage/plugin-search-react'; + useSearch, +} from '@backstage/plugin-search-react'; import { CatalogIcon, Content, @@ -112,7 +112,7 @@ const SearchPage = () => { {({ results }) => ( - {results.map(({ type, document, highlight }) => { + {results.map(({ type, document, highlight, rank }) => { switch (type) { case 'software-catalog': return ( @@ -120,6 +120,7 @@ const SearchPage = () => { key={document.location} result={document} highlight={highlight} + rank={rank} /> ); case 'techdocs': @@ -128,6 +129,7 @@ const SearchPage = () => { key={document.location} result={document} highlight={highlight} + rank={rank} /> ); default: @@ -136,6 +138,7 @@ const SearchPage = () => { key={document.location} result={document} highlight={highlight} + rank={rank} /> ); } diff --git a/packages/create-app/templates/default-app/packages/backend/src/plugins/auth.ts b/packages/create-app/templates/default-app/packages/backend/src/plugins/auth.ts index 13f18c55e9..159116d7b8 100644 --- a/packages/create-app/templates/default-app/packages/backend/src/plugins/auth.ts +++ b/packages/create-app/templates/default-app/packages/backend/src/plugins/auth.ts @@ -18,18 +18,36 @@ export default async function createPlugin( providerFactories: { ...defaultAuthProviderFactories, - // This overrides the default GitHub auth provider with a custom one. - // Since the options are empty it will behave just like the default - // provider, but if you uncomment the `signIn` section you will enable - // sign-in via GitHub. This particular configuration uses a resolver - // that matches the username to the user entity name. See the auth - // documentation for more details on how to enable and customize sign-in: + // This replaces the default GitHub auth provider with a customized one. + // The `signIn` option enables sign-in for this provider, using the + // identity resolution logic that's provided in the `resolver` callback. + // + // This particular resolver makes all users share a single "guest" identity. + // It should only be used for testing and trying out Backstage. + // + // If you want to use a production ready resolver you can switch to the + // the one that is commented out below, it looks up a user entity in the + // catalog using the GitHub username of the authenticated user. + // That resolver requires you to have user entities populated in the catalog, + // for example using https://backstage.io/docs/integrations/github/org + // + // There are other resolvers to choose from, and you can also create + // your own, see the auth documentation for more details: // // https://backstage.io/docs/auth/identity-resolver github: providers.github.create({ - // signIn: { - // resolver: providers.github.resolvers.usernameMatchingUserEntityName(), - // }, + signIn: { + resolver(_, ctx) { + const userRef = 'user:default/guest'; // Must be a full entity reference + return ctx.issueToken({ + claims: { + sub: userRef, // The user's own identity + ent: [userRef], // A list of identities that the user claims ownership through + }, + }); + }, + // resolver: providers.github.resolvers.usernameMatchingUserEntityName(), + }, }), }, }); diff --git a/packages/dev-utils/CHANGELOG.md b/packages/dev-utils/CHANGELOG.md index 3463c378fe..563de9fc44 100644 --- a/packages/dev-utils/CHANGELOG.md +++ b/packages/dev-utils/CHANGELOG.md @@ -1,5 +1,43 @@ # @backstage/dev-utils +## 1.0.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/core-app-api@1.0.3 + - @backstage/core-plugin-api@1.0.3 + - @backstage/integration-react@1.1.1 + - @backstage/test-utils@1.1.1 + - @backstage/catalog-model@1.0.3 + - @backstage/app-defaults@1.0.3 + +## 1.0.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-app-api@1.0.3-next.0 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/integration-react@1.1.1-next.1 + - @backstage/test-utils@1.1.1-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + - @backstage/app-defaults@1.0.3-next.1 + +## 1.0.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + - @backstage/app-defaults@1.0.3-next.0 + - @backstage/integration-react@1.1.1-next.0 + ## 1.0.2 ### Patch Changes diff --git a/packages/dev-utils/package.json b/packages/dev-utils/package.json index 8ced35c07c..2da041d9b7 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.2", + "version": "1.0.3", "private": false, "publishConfig": { "access": "public", @@ -33,14 +33,14 @@ "start": "backstage-cli package start" }, "dependencies": { - "@backstage/app-defaults": "^1.0.2", - "@backstage/core-app-api": "^1.0.2", - "@backstage/core-components": "^0.9.4", - "@backstage/core-plugin-api": "^1.0.2", - "@backstage/catalog-model": "^1.0.2", - "@backstage/integration-react": "^1.1.0", - "@backstage/plugin-catalog-react": "^1.1.0", - "@backstage/test-utils": "^1.1.0", + "@backstage/app-defaults": "^1.0.3", + "@backstage/core-app-api": "^1.0.3", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", + "@backstage/catalog-model": "^1.0.3", + "@backstage/integration-react": "^1.1.1", + "@backstage/plugin-catalog-react": "^1.1.1", + "@backstage/test-utils": "^1.1.1", "@backstage/theme": "^0.2.15", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -59,7 +59,7 @@ "react-dom": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", + "@backstage/cli": "^0.17.2", "@types/jest": "^26.0.7", "@types/node": "^16.0.0" }, diff --git a/packages/e2e-test/src/commands/run.ts b/packages/e2e-test/src/commands/run.ts index e1477d98d7..d324aeafaf 100644 --- a/packages/e2e-test/src/commands/run.ts +++ b/packages/e2e-test/src/commands/run.ts @@ -235,6 +235,18 @@ async function createApp( print('Rewriting module resolutions of app to use workspace packages'); await overrideModuleResolutions(appDir, workspaceDir); + // Yarn does not clean up node_module folders in the linked in dependencies by itself + print('Cleaning up node_modules in workspace'); + await fs.remove(resolvePath(workspaceDir, 'node_modules')); + for (const wsDir of ['packages', 'plugins']) { + for (const dir of await fs.readdir(resolvePath(workspaceDir, wsDir))) { + const moduleDir = resolvePath(workspaceDir, wsDir, dir, 'node_modules'); + if (await fs.pathExists(moduleDir)) { + await fs.remove(moduleDir); + } + } + } + print('Pinning yarn version and registry in app'); await pinYarnVersion(appDir); await fs.writeFile( diff --git a/packages/errors/package.json b/packages/errors/package.json index f37c103f37..8327246224 100644 --- a/packages/errors/package.json +++ b/packages/errors/package.json @@ -38,7 +38,7 @@ "serialize-error": "^8.0.1" }, "devDependencies": { - "@backstage/cli": "^0.17.1-next.0", + "@backstage/cli": "^0.17.2-next.0", "@types/jest": "^26.0.7" }, "files": [ diff --git a/packages/integration-react/CHANGELOG.md b/packages/integration-react/CHANGELOG.md index baa0318529..d03553f50d 100644 --- a/packages/integration-react/CHANGELOG.md +++ b/packages/integration-react/CHANGELOG.md @@ -1,5 +1,33 @@ # @backstage/integration-react +## 1.1.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5 + - @backstage/integration@1.2.1 + - @backstage/core-plugin-api@1.0.3 + +## 1.1.1-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/integration@1.2.1-next.1 + +## 1.1.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.9.5-next.0 + - @backstage/integration@1.2.1-next.0 + ## 1.1.0 ### Minor Changes diff --git a/packages/integration-react/package.json b/packages/integration-react/package.json index 34790174d0..580b91509f 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.0", + "version": "1.1.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -25,9 +25,9 @@ }, "dependencies": { "@backstage/config": "^1.0.1", - "@backstage/core-components": "^0.9.4", - "@backstage/core-plugin-api": "^1.0.2", - "@backstage/integration": "^1.2.0", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", + "@backstage/integration": "^1.2.1", "@backstage/theme": "^0.2.15", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -38,15 +38,15 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/dev-utils": "^1.0.2", - "@backstage/test-utils": "^1.1.0", + "@backstage/cli": "^0.17.2", + "@backstage/dev-utils": "^1.0.3", + "@backstage/test-utils": "^1.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", "@types/jest": "^26.0.7", "@types/node": "^16.11.26", - "msw": "^0.35.0", + "msw": "^0.42.0", "cross-fetch": "^3.1.5" }, "files": [ diff --git a/packages/integration/CHANGELOG.md b/packages/integration/CHANGELOG.md index fa4999f57c..736d9ff2a1 100644 --- a/packages/integration/CHANGELOG.md +++ b/packages/integration/CHANGELOG.md @@ -1,5 +1,31 @@ # @backstage/integration +## 1.2.1 + +### Patch Changes + +- 72dfcbc8bf: Gerrit Integration: Handle absolute paths in `resolveUrl` properly. +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- e37c71b5a4: Updated to support deployments of Azure DevOps Server under TFS or similar sub path + +## 1.2.1-next.2 + +### Patch Changes + +- e37c71b5a4: Updated to support deployments of Azure DevOps Server under TFS or similar sub path + +## 1.2.1-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. + +## 1.2.1-next.0 + +### Patch Changes + +- 72dfcbc8bf: Gerrit Integration: Handle absolute paths in `resolveUrl` properly. + ## 1.2.0 ### Minor Changes diff --git a/packages/integration/config.d.ts b/packages/integration/config.d.ts index c14f5c76cd..22acc5cd70 100644 --- a/packages/integration/config.d.ts +++ b/packages/integration/config.d.ts @@ -15,7 +15,10 @@ */ export interface Config { - /** Configuration for integrations towards various external repository provider systems */ + /** + * Configuration for integrations towards various external repository provider systems + * @visibility frontend + */ integrations?: { /** Integration configuration for Azure */ azure?: Array<{ diff --git a/packages/integration/package.json b/packages/integration/package.json index ae91c4ca2f..06faef890d 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.2.0", + "version": "1.2.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -43,12 +43,12 @@ "lodash": "^4.17.21" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/config-loader": "^1.1.1", - "@backstage/test-utils": "^1.1.0", + "@backstage/cli": "^0.17.2", + "@backstage/config-loader": "^1.1.2", + "@backstage/test-utils": "^1.1.1", "@types/jest": "^26.0.7", "@types/luxon": "^2.0.4", - "msw": "^0.35.0" + "msw": "^0.42.0" }, "files": [ "dist", diff --git a/packages/integration/src/azure/AzureUrl.test.ts b/packages/integration/src/azure/AzureUrl.test.ts index 2f22dd13da..a56f800133 100644 --- a/packages/integration/src/azure/AzureUrl.test.ts +++ b/packages/integration/src/azure/AzureUrl.test.ts @@ -142,6 +142,30 @@ describe('AzureUrl', () => { ); }); + it('should work with the old tfs long URL', () => { + const url = AzureUrl.fromRepoUrl( + 'http://my-host/tfs/projects/my-project/_git/my-repo', + ); + + expect(url.getOwner()).toBe('tfs/projects'); + expect(url.getProject()).toBe('my-project'); + expect(url.getRepo()).toBe('my-repo'); + expect(url.getRef()).toBeUndefined(); + expect(url.getPath()).toBeUndefined(); + }); + + it('should work with the old tfs long URL form with a path and ref', () => { + const url = AzureUrl.fromRepoUrl( + 'http://my-host/tfs/projects/my-project/_git/my-repo?path=%2Ffolder&version=GBtest-branch', + ); + + expect(url.getOwner()).toBe('tfs/projects'); + expect(url.getProject()).toBe('my-project'); + expect(url.getRepo()).toBe('my-repo'); + expect(url.getRef()).toBe('test-branch'); + expect(url.getPath()).toBe('/folder'); + }); + it('should reject non-branch refs', () => { expect(() => AzureUrl.fromRepoUrl( diff --git a/packages/integration/src/azure/AzureUrl.ts b/packages/integration/src/azure/AzureUrl.ts index 76de8550ed..1161ab7a1b 100644 --- a/packages/integration/src/azure/AzureUrl.ts +++ b/packages/integration/src/azure/AzureUrl.ts @@ -37,6 +37,10 @@ export class AzureUrl { owner = parts[1]; project = parts[2]; repo = parts[4]; + } else if (parts[4] === '_git') { + owner = `${parts[1]}/${parts[2]}`; + project = parts[3]; + repo = parts[5]; } if (!owner || !project || !repo) { diff --git a/packages/integration/src/gerrit/GerritIntegration.test.ts b/packages/integration/src/gerrit/GerritIntegration.test.ts index 6b5e228bb6..1a7bac5be6 100644 --- a/packages/integration/src/gerrit/GerritIntegration.test.ts +++ b/packages/integration/src/gerrit/GerritIntegration.test.ts @@ -97,6 +97,24 @@ describe('GerritIntegration', () => { }); }); + describe('resolves with an absolute url', () => { + it('works for valid urls', () => { + const integration = new GerritIntegration({ + host: 'gerrit-review.example.com', + gitilesBaseUrl: 'https://gerrit-review.example.com/gitiles', + } as any); + + expect( + integration.resolveUrl({ + url: '/catalog-info.yaml', + base: 'https://gerrit-review.example.com/gitiles/repo/+/refs/heads/master/', + }), + ).toBe( + 'https://gerrit-review.example.com/gitiles/repo/+/refs/heads/master/catalog-info.yaml', + ); + }); + }); + it('resolve edit URL', () => { const integration = new GerritIntegration({ host: 'gerrit-review.example.com', diff --git a/packages/integration/src/gerrit/GerritIntegration.ts b/packages/integration/src/gerrit/GerritIntegration.ts index 97eb9372c0..d57b54df8e 100644 --- a/packages/integration/src/gerrit/GerritIntegration.ts +++ b/packages/integration/src/gerrit/GerritIntegration.ts @@ -20,6 +20,7 @@ import { GerritIntegrationConfig, readGerritIntegrationConfigs, } from './config'; +import { parseGerritGitilesUrl, builldGerritGitilesUrl } from './core'; /** * A Gerrit based integration. @@ -58,6 +59,10 @@ export class GerritIntegration implements ScmIntegration { }): string { const { url, base, lineNumber } = options; let updated; + if (url.startsWith('/')) { + const { branch, project } = parseGerritGitilesUrl(this.config, base); + return builldGerritGitilesUrl(this.config, project, branch, url); + } if (url) { updated = new URL(url, base); } else { diff --git a/packages/integration/src/gerrit/core.test.ts b/packages/integration/src/gerrit/core.test.ts index 84bc1c0c19..351e7ce58e 100644 --- a/packages/integration/src/gerrit/core.test.ts +++ b/packages/integration/src/gerrit/core.test.ts @@ -20,6 +20,7 @@ import fetch from 'cross-fetch'; import { setupRequestMockHandlers } from '@backstage/test-utils'; import { GerritIntegrationConfig } from './config'; import { + builldGerritGitilesUrl, getGerritBranchApiUrl, getGerritCloneRepoUrl, getGerritRequestOptions, @@ -32,6 +33,20 @@ describe('gerrit core', () => { const worker = setupServer(); setupRequestMockHandlers(worker); + describe('builldGerritGitilesUrl', () => { + it('can create an url from arguments', () => { + const config: GerritIntegrationConfig = { + host: 'gerrit.com', + gitilesBaseUrl: 'https://gerrit.com/gitiles', + }; + expect( + builldGerritGitilesUrl(config, 'repo', 'dev', 'catalog-info.yaml'), + ).toEqual( + 'https://gerrit.com/gitiles/repo/+/refs/heads/dev/catalog-info.yaml', + ); + }); + }); + describe('getGerritRequestOptions', () => { it('adds headers when a password is specified', () => { const authRequest: GerritIntegrationConfig = { diff --git a/packages/integration/src/gerrit/core.ts b/packages/integration/src/gerrit/core.ts index eb413a2608..cd2b919e09 100644 --- a/packages/integration/src/gerrit/core.ts +++ b/packages/integration/src/gerrit/core.ts @@ -70,6 +70,26 @@ export function parseGerritGitilesUrl( }; } +/** + * Build a Gerrit Gitiles url that targets a specific path. + * + * @param config - A Gerrit provider config. + * @param project - The name of the git project + * @param branch - The branch we will target. + * @param filePath - The absolute file path. + * @public + */ +export function builldGerritGitilesUrl( + config: GerritIntegrationConfig, + project: string, + branch: string, + filePath: string, +): string { + return `${ + config.gitilesBaseUrl + }/${project}/+/refs/heads/${branch}/${trimStart(filePath, '/')}`; +} + /** * Return the authentication prefix. * diff --git a/packages/release-manifests/CHANGELOG.md b/packages/release-manifests/CHANGELOG.md index 017a50bd6c..279128576c 100644 --- a/packages/release-manifests/CHANGELOG.md +++ b/packages/release-manifests/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/release-manifests +## 0.0.4 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. + +## 0.0.4-next.0 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. + ## 0.0.3 ### Patch Changes diff --git a/packages/release-manifests/package.json b/packages/release-manifests/package.json index ef968e7a2d..f0627a9c95 100644 --- a/packages/release-manifests/package.json +++ b/packages/release-manifests/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/release-manifests", "description": "Helper library for receiving release manifests", - "version": "0.0.3", + "version": "0.0.4", "private": false, "main": "src/index.ts", "types": "src/index.ts", @@ -36,8 +36,8 @@ "cross-fetch": "^3.1.5" }, "devDependencies": { - "@backstage/test-utils": "^1.1.0", - "msw": "^0.35.0", + "@backstage/test-utils": "^1.1.1", + "msw": "^0.42.0", "@types/jest": "^26.0.7", "@types/node": "^16.0.0" }, diff --git a/packages/release-manifests/src/manifest.test.ts b/packages/release-manifests/src/manifest.test.ts index 70894e5431..272555df70 100644 --- a/packages/release-manifests/src/manifest.test.ts +++ b/packages/release-manifests/src/manifest.test.ts @@ -14,73 +14,75 @@ * limitations under the License. */ -import { setupRequestMockHandlers } from '@backstage/test-utils'; -import { getManifestByReleaseLine, getManifestByVersion } from './manifest'; import { setupServer } from 'msw/node'; import { rest } from 'msw'; +import { setupRequestMockHandlers } from '@backstage/test-utils'; +import { getManifestByReleaseLine, getManifestByVersion } from './manifest'; -describe('getManifestByVersion', () => { +describe('Release Manifests', () => { const worker = setupServer(); setupRequestMockHandlers(worker); - it('should return a list of packages in a release', async () => { - worker.use( - rest.get('*/v1/releases/0.0.0/manifest.json', (_, res, ctx) => - res( - ctx.status(200), - ctx.json({ - packages: [{ name: '@backstage/core', version: '1.2.3' }], - }), + describe('getManifestByVersion', () => { + it('should return a list of packages in a release', async () => { + worker.use( + rest.get('*/v1/releases/0.0.0/manifest.json', (_, res, ctx) => + res( + ctx.status(200), + ctx.json({ + packages: [{ name: '@backstage/core', version: '1.2.3' }], + }), + ), ), - ), - rest.get('*/v1/releases/999.0.1/manifest.json', (_, res, ctx) => - res(ctx.status(404), ctx.json({})), - ), - ); + rest.get('*/v1/releases/999.0.1/manifest.json', (_, res, ctx) => + res(ctx.status(404), ctx.json({})), + ), + ); - const pkgs = await getManifestByVersion({ version: '0.0.0' }); - expect(pkgs.packages).toEqual([ - { - name: '@backstage/core', - version: '1.2.3', - }, - ]); + const pkgs = await getManifestByVersion({ version: '0.0.0' }); + expect(pkgs.packages).toEqual([ + { + name: '@backstage/core', + version: '1.2.3', + }, + ]); - await expect(getManifestByVersion({ version: '999.0.1' })).rejects.toThrow( - 'No release found for 999.0.1 version', - ); - }); -}); - -describe('getManifestByReleaseLine', () => { - const worker = setupServer(); - setupRequestMockHandlers(worker); - - it('should return a list of packages in a release', async () => { - worker.use( - rest.get('*/v1/tags/main/manifest.json', (_, res, ctx) => - res( - ctx.status(200), - ctx.json({ - packages: [{ name: '@backstage/core', version: '1.2.3' }], - }), - ), - ), - rest.get('*/v1/tags/foo/manifest.json', (_, res, ctx) => - res(ctx.status(404), ctx.json({})), - ), - ); - - const pkgs = await getManifestByReleaseLine({ releaseLine: 'main' }); - expect(pkgs.packages).toEqual([ - { - name: '@backstage/core', - version: '1.2.3', - }, - ]); - - await expect( - getManifestByReleaseLine({ releaseLine: 'foo' }), - ).rejects.toThrow("No 'foo' release line found"); + await expect( + getManifestByVersion({ version: '999.0.1' }), + ).rejects.toThrow('No release found for 999.0.1 version'); + }); + }); + + describe('getManifestByReleaseLine', () => { + it('should return a list of packages in a release', async () => { + worker.use( + rest.get( + 'https://versions.backstage.io/v1/tags/main/manifest.json', + (_, res, ctx) => + res( + ctx.status(200), + ctx.json({ + packages: [{ name: '@backstage/core', version: '1.2.3' }], + }), + ), + ), + rest.get( + 'https://versions.backstage.io/v1/tags/foo/manifest.json', + (_, res, ctx) => res(ctx.status(404), ctx.json({})), + ), + ); + + const pkgs = await getManifestByReleaseLine({ releaseLine: 'main' }); + expect(pkgs.packages).toEqual([ + { + name: '@backstage/core', + version: '1.2.3', + }, + ]); + + await expect( + getManifestByReleaseLine({ releaseLine: 'foo' }), + ).rejects.toThrow("No 'foo' release line found"); + }); }); }); diff --git a/packages/search-common/CHANGELOG.md b/packages/search-common/CHANGELOG.md deleted file mode 100644 index 2a757c1909..0000000000 --- a/packages/search-common/CHANGELOG.md +++ /dev/null @@ -1,213 +0,0 @@ -# @backstage/search-common - -## 0.3.4 - -### Patch Changes - -- Updated dependencies - - @backstage/plugin-search-common@0.3.4 - -## 0.3.4-next.0 - -### Patch Changes - -- Updated dependencies - - @backstage/plugin-search-common@0.3.4-next.0 - -## 0.3.3 - -### Patch Changes - -- Updated dependencies - - @backstage/plugin-search-common@0.3.3 - -## 0.3.3-next.1 - -### Patch Changes - -- Updated dependencies - - @backstage/plugin-search-common@0.3.3-next.1 - -## 0.3.3-next.0 - -### Patch Changes - -- Updated dependencies - - @backstage/plugin-search-common@0.3.3-next.0 - -## 0.3.2 - -### Patch Changes - -- 82b0190155: This package is no longer maintained. Use `@backstage/plugin-search-common`, going forward. -- Updated dependencies - - @backstage/plugin-search-common@0.3.2 - -## 0.3.1 - -### Patch Changes - -- d52155466a: **DEPRECATION** - - The `@backstage/search-common` package is being renamed `@backstage/plugin-search-common`. We may continue to publish changes to `@backstage/search-common` for a time, but will stop doing so in the near future. If you depend on this package, you should update your dependencies to point at the renamed package. - -- Updated dependencies - - @backstage/plugin-search-common@0.3.1 - -## 0.3.1-next.0 - -### Patch Changes - -- d52155466a: **DEPRECATION** - - The `@backstage/search-common` package is being renamed `@backstage/plugin-search-common`. We may continue to publish changes to `@backstage/search-common` for a time, but will stop doing so in the near future. If you depend on this package, you should update your dependencies to point at the renamed package. - -- Updated dependencies - - @backstage/plugin-search-common@0.3.1-next.0 - -## 0.3.0 - -### Minor Changes - -- 022507c860: **BREAKING** - - The Backstage Search Platform's indexing process has been rewritten as a stream - pipeline in order to improve efficiency and performance on large document sets. - - The concepts of `Collator` and `Decorator` have been replaced with readable and - transform object streams (respectively), as well as factory classes to - instantiate them. Accordingly, the `SearchEngine.index()` method has also been - replaced with a `getIndexer()` factory method that resolves to a writable - object stream. - - Check [this upgrade guide](https://backstage.io/docs/features/search/how-to-guides#how-to-migrate-from-search-alpha-to-beta) - for further details. - -### Patch Changes - -- Updated dependencies - - @backstage/plugin-permission-common@0.5.2 - -## 0.2.4 - -### Patch Changes - -- Fix for the previous release with missing type declarations. -- Updated dependencies - - @backstage/types@0.1.3 - - @backstage/plugin-permission-common@0.5.1 - -## 0.2.3 - -### Patch Changes - -- c77c5c7eb6: Added `backstage.role` to `package.json` -- Updated dependencies - - @backstage/plugin-permission-common@0.5.0 - - @backstage/types@0.1.2 - -## 0.2.2 - -### Patch Changes - -- 9a511968b1: - Add optional visibilityPermission property to DocumentCollator type - - Add new DocumentTypeInfo type for housing information about the document types stored in a search engine. -- b2e918fa0b: Add optional resourceRef field to the IndexableDocument type for use when authorizing access to documents. -- 96cbebc629: Add optional query request options containing authorization token to SearchEngine#query. - -## 0.2.1 - -### Patch Changes - -- 10615525f3: Switch to use the json and observable types from `@backstage/types` - -## 0.2.0 - -### Minor Changes - -- a13f21cdc: Implement optional `pageCursor` based paging in search. - - To use paging in your app, add a `` to your - `SearchPage.tsx`. - -## 0.1.3 - -### Patch Changes - -- d9c13d535: Implements configuration and indexing functionality for ElasticSearch search engine. Adds indexing, searching and default translator for ElasticSearch and modifies default backend example-app to use ES if it is configured. - - ## Example configurations: - - ### AWS - - Using AWS hosted ElasticSearch the only configuration options needed is the URL to the ElasticSearch service. The implementation assumes - that environment variables for AWS access key id and secret access key are defined in accordance to the [default AWS credential chain.](https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/setting-credentials-node.html). - - ```yaml - search: - elasticsearch: - provider: aws - node: https://my-backstage-search-asdfqwerty.eu-west-1.es.amazonaws.com - ``` - - ### Elastic.co - - Elastic Cloud hosted ElasticSearch uses a Cloud ID to determine the instance of hosted ElasticSearch to connect to. Additionally, username and password needs to be provided either directly or using environment variables like defined in [Backstage documentation.](https://backstage.io/docs/conf/writing#includes-and-dynamic-data) - - ```yaml - search: - elasticsearch: - provider: elastic - cloudId: backstage-elastic:asdfqwertyasdfqwertyasdfqwertyasdfqwerty== - auth: - username: elastic - password: changeme - ``` - - ### Others - - Other ElasticSearch instances can be connected to by using standard ElasticSearch authentication methods and exposed URL, provided that the cluster supports that. The configuration options needed are the URL to the node and authentication information. Authentication can be handled by either providing username/password or and API key or a bearer token. In case both username/password combination and one of the tokens are provided, token takes precedence. For more information how to create an API key, see [Elastic documentation on API keys](https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-create-api-key.html) and how to create a bearer token, see [Elastic documentation on tokens.](https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-create-service-token.html) - - #### Configuration examples - - ##### With username and password - - ```yaml - search: - elasticsearch: - node: http://localhost:9200 - auth: - username: elastic - password: changeme - ``` - - ##### With bearer token - - ```yaml - search: - elasticsearch: - node: http://localhost:9200 - auth: - bearer: token - ``` - - ##### With API key - - ```yaml - search: - elasticsearch: - node: http://localhost:9200 - auth: - apiKey: base64EncodedKey - ``` - -- Updated dependencies - - @backstage/config@0.1.6 - -## 0.1.2 - -### Patch Changes - -- db1c8f93b: The ` set of components exported by the Search Plugin are now updated to use the Search Backend API. These will be made available as the default non-"next" versions in a follow-up release. - - The interfaces for decorators and collators in the Search Backend have also seen minor, breaking revisions ahead of a general release. If you happen to be building on top of these interfaces, check and update your implementations accordingly. The APIs will be considered more stable in a follow-up release. diff --git a/packages/search-common/README.md b/packages/search-common/README.md deleted file mode 100644 index 80b0c09c40..0000000000 --- a/packages/search-common/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# @backstage/search-common - -This package has been moved to `@backstage/plugin-search-common` and is no -longer maintained. Please update any dependencies you have on this package -accordingly. diff --git a/packages/search-common/api-report.md b/packages/search-common/api-report.md deleted file mode 100644 index 03965ace66..0000000000 --- a/packages/search-common/api-report.md +++ /dev/null @@ -1,9 +0,0 @@ -## API Report File for "@backstage/search-common" - -> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). - -```ts -export * from '@backstage/plugin-search-common'; - -// (No @packageDocumentation comment for this package) -``` diff --git a/packages/search-common/package.json b/packages/search-common/package.json deleted file mode 100644 index 35e20d57ca..0000000000 --- a/packages/search-common/package.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": "@backstage/search-common", - "description": "No longer maintained. Use @backstage/plugin-search-common instead.", - "version": "0.3.4", - "main": "src/index.ts", - "types": "src/index.ts", - "private": false, - "publishConfig": { - "access": "public", - "main": "dist/index.cjs.js", - "types": "dist/index.d.ts" - }, - "backstage": { - "role": "common-library" - }, - "homepage": "https://backstage.io", - "repository": { - "type": "git", - "url": "https://github.com/backstage/backstage", - "directory": "packages/search-common" - }, - "license": "Apache-2.0", - "files": [ - "dist" - ], - "scripts": { - "build": "backstage-cli package build", - "lint": "backstage-cli package lint", - "test": "backstage-cli package test", - "prepack": "backstage-cli package prepack", - "postpack": "backstage-cli package postpack", - "clean": "backstage-cli package clean" - }, - "bugs": { - "url": "https://github.com/backstage/backstage/issues" - }, - "dependencies": { - "@backstage/plugin-search-common": "0.3.4" - }, - "devDependencies": {}, - "jest": { - "roots": [ - ".." - ] - } -} diff --git a/packages/techdocs-cli-embedded-app/CHANGELOG.md b/packages/techdocs-cli-embedded-app/CHANGELOG.md index 9f749dc2ab..58102369ed 100644 --- a/packages/techdocs-cli-embedded-app/CHANGELOG.md +++ b/packages/techdocs-cli-embedded-app/CHANGELOG.md @@ -1,5 +1,52 @@ # techdocs-cli-embedded-app +## 0.2.71 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@1.3.0 + - @backstage/plugin-techdocs@1.2.0 + - @backstage/cli@0.17.2 + - @backstage/core-components@0.9.5 + - @backstage/core-app-api@1.0.3 + - @backstage/core-plugin-api@1.0.3 + - @backstage/integration-react@1.1.1 + - @backstage/test-utils@1.1.1 + - @backstage/catalog-model@1.0.3 + - @backstage/plugin-techdocs-react@1.0.1 + - @backstage/app-defaults@1.0.3 + +## 0.2.71-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/cli@0.17.2-next.1 + - @backstage/core-app-api@1.0.3-next.0 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/integration-react@1.1.1-next.1 + - @backstage/test-utils@1.1.1-next.0 + - @backstage/plugin-techdocs@1.1.2-next.1 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog@1.2.1-next.1 + - @backstage/app-defaults@1.0.3-next.1 + - @backstage/plugin-techdocs-react@1.0.1-next.1 + +## 0.2.71-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@1.2.1-next.0 + - @backstage/cli@0.17.2-next.0 + - @backstage/core-components@0.9.5-next.0 + - @backstage/plugin-techdocs@1.1.2-next.0 + - @backstage/plugin-techdocs-react@1.0.1-next.0 + - @backstage/app-defaults@1.0.3-next.0 + - @backstage/integration-react@1.1.1-next.0 + ## 0.2.70 ### Patch Changes diff --git a/packages/techdocs-cli-embedded-app/package.json b/packages/techdocs-cli-embedded-app/package.json index d174cd0525..8029d604db 100644 --- a/packages/techdocs-cli-embedded-app/package.json +++ b/packages/techdocs-cli-embedded-app/package.json @@ -1,24 +1,24 @@ { "name": "techdocs-cli-embedded-app", - "version": "0.2.70", + "version": "0.2.71", "private": true, "backstage": { "role": "frontend" }, "bundled": true, "dependencies": { - "@backstage/app-defaults": "^1.0.2", - "@backstage/catalog-model": "^1.0.2", - "@backstage/cli": "^0.17.1", + "@backstage/app-defaults": "^1.0.3", + "@backstage/catalog-model": "^1.0.3", + "@backstage/cli": "^0.17.2", "@backstage/config": "^1.0.1", - "@backstage/core-app-api": "^1.0.2", - "@backstage/core-components": "^0.9.4", - "@backstage/core-plugin-api": "^1.0.2", - "@backstage/integration-react": "^1.1.0", - "@backstage/plugin-catalog": "^1.2.0", - "@backstage/plugin-techdocs": "^1.1.1", - "@backstage/plugin-techdocs-react": "^1.0.0", - "@backstage/test-utils": "^1.1.0", + "@backstage/core-app-api": "^1.0.3", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", + "@backstage/integration-react": "^1.1.1", + "@backstage/plugin-catalog": "^1.3.0", + "@backstage/plugin-techdocs": "^1.2.0", + "@backstage/plugin-techdocs-react": "^1.0.1", + "@backstage/test-utils": "^1.1.1", "@backstage/theme": "^0.2.15", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -30,7 +30,7 @@ "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.17.1", + "@backstage/cli": "^0.17.2", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", @@ -38,7 +38,7 @@ "@types/node": "^16.11.26", "@types/react-dom": "*", "cross-env": "^7.0.0", - "cypress": "^9.5.0", + "cypress": "^10.0.0", "eslint-plugin-cypress": "^2.10.3", "start-server-and-test": "^1.10.11" }, diff --git a/packages/techdocs-cli-embedded-app/src/components/Root/Root.tsx b/packages/techdocs-cli-embedded-app/src/components/Root/Root.tsx index 249394fdc4..cd8b5d35ed 100644 --- a/packages/techdocs-cli-embedded-app/src/components/Root/Root.tsx +++ b/packages/techdocs-cli-embedded-app/src/components/Root/Root.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import React, { PropsWithChildren, useContext } from 'react'; +import React, { PropsWithChildren } from 'react'; import { Link, makeStyles } from '@material-ui/core'; import LibraryBooks from '@material-ui/icons/LibraryBooks'; @@ -27,7 +27,7 @@ import { SidebarPage, sidebarConfig, SidebarDivider, - SidebarContext, + useSidebarOpenState, } from '@backstage/core-components'; import { NavLink } from 'react-router-dom'; @@ -48,7 +48,7 @@ const useSidebarLogoStyles = makeStyles({ const SidebarLogo = () => { const classes = useSidebarLogoStyles(); - const { isOpen } = useContext(SidebarContext); + const { isOpen } = useSidebarOpenState(); return (
diff --git a/packages/techdocs-cli/CHANGELOG.md b/packages/techdocs-cli/CHANGELOG.md index 44c8b9a1d1..21eb2393b7 100644 --- a/packages/techdocs-cli/CHANGELOG.md +++ b/packages/techdocs-cli/CHANGELOG.md @@ -1,5 +1,43 @@ # @techdocs/cli +## 1.1.2 + +### Patch Changes + +- f96e98f4cd: Updated dependency `cypress` to `^10.0.0`. +- bff65e6958: Updated sidebar-related logic to use `` + `useSidebarPinState()` and/or `` + `useSidebarOpenState()` from `@backstage/core-components`. +- Updated dependencies + - @backstage/backend-common@0.14.0 + - @backstage/plugin-techdocs-node@1.1.2 + - @backstage/catalog-model@1.0.3 + +## 1.1.2-next.2 + +### Patch Changes + +- f96e98f4cd: Updated dependency `cypress` to `^10.0.0`. +- Updated dependencies + - @backstage/backend-common@0.14.0-next.2 + - @backstage/plugin-techdocs-node@1.1.2-next.2 + +## 1.1.2-next.1 + +### Patch Changes + +- bff65e6958: Updated sidebar-related logic to use `` + `useSidebarPinState()` and/or `` + `useSidebarOpenState()` from `@backstage/core-components`. +- Updated dependencies + - @backstage/backend-common@0.13.6-next.1 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-techdocs-node@1.1.2-next.1 + +## 1.1.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + - @backstage/plugin-techdocs-node@1.1.2-next.0 + ## 1.1.1 ### Patch Changes diff --git a/packages/techdocs-cli/package.json b/packages/techdocs-cli/package.json index 2e8107aa35..d1db470572 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.1.1", + "version": "1.1.2", "private": false, "publishConfig": { "access": "public" @@ -37,7 +37,7 @@ "techdocs-cli": "bin/techdocs-cli" }, "devDependencies": { - "@backstage/cli": "^0.17.1", + "@backstage/cli": "^0.17.2", "@types/commander": "^2.12.2", "@types/fs-extra": "^9.0.6", "@types/http-proxy": "^1.17.4", @@ -45,7 +45,7 @@ "@types/node": "^16.11.26", "@types/serve-handler": "^6.1.0", "@types/webpack-env": "^1.15.3", - "cypress": "^9.5.0", + "cypress": "^10.0.0", "cypress-plugin-snapshots": "^1.4.4", "find-process": "^1.4.5", "nodemon": "^2.0.2", @@ -62,11 +62,11 @@ "ext": "ts" }, "dependencies": { - "@backstage/backend-common": "^0.13.3", - "@backstage/catalog-model": "^1.0.2", + "@backstage/backend-common": "^0.14.0", + "@backstage/catalog-model": "^1.0.3", "@backstage/cli-common": "^0.1.9", "@backstage/config": "^1.0.1", - "@backstage/plugin-techdocs-node": "^1.1.1", + "@backstage/plugin-techdocs-node": "^1.1.2", "@types/dockerode": "^3.3.0", "commander": "^9.1.0", "dockerode": "^3.3.1", diff --git a/packages/techdocs-cli/src/commands/index.ts b/packages/techdocs-cli/src/commands/index.ts index 85a218eb54..68e8f88c0b 100644 --- a/packages/techdocs-cli/src/commands/index.ts +++ b/packages/techdocs-cli/src/commands/index.ts @@ -38,7 +38,7 @@ export function registerCommands(program: Command) { 'The mkdocs docker container to use', defaultDockerImage, ) - .option('--no-pull', 'Do not pull the latest docker image', false) + .option('--no-pull', 'Do not pull the latest docker image') .option( '--no-docker', 'Do not use Docker, use MkDocs executable and plugins in current user environment.', diff --git a/packages/techdocs-common/CHANGELOG.md b/packages/techdocs-common/CHANGELOG.md deleted file mode 100644 index 5d930469fa..0000000000 --- a/packages/techdocs-common/CHANGELOG.md +++ /dev/null @@ -1,1144 +0,0 @@ -# @backstage/techdocs-common - -## 0.11.15 - -### Patch Changes - -- Updated dependencies - - @backstage/plugin-techdocs-node@1.1.1 - -## 0.11.15-next.1 - -### Patch Changes - -- Updated dependencies - - @backstage/plugin-techdocs-node@1.1.1-next.1 - -## 0.11.15-next.0 - -### Patch Changes - -- Updated dependencies - - @backstage/plugin-techdocs-node@1.1.1-next.0 - -## 0.11.14 - -### Patch Changes - -- Updated dependencies - - @backstage/plugin-techdocs-node@1.1.0 - -## 0.11.14-next.2 - -### Patch Changes - -- Updated dependencies - - @backstage/plugin-techdocs-node@1.1.0-next.2 - -## 0.11.14-next.1 - -### Patch Changes - -- Updated dependencies - - @backstage/plugin-techdocs-node@1.0.1-next.1 - -## 0.11.14-next.0 - -### Patch Changes - -- Updated dependencies - - @backstage/plugin-techdocs-node@1.0.1-next.0 - -## 0.11.13 - -### Patch Changes - -- 82b0190155: This package is no longer maintained. Use `@backstage/plugin-techdocs-node`, going forward. -- Updated dependencies - - @backstage/plugin-techdocs-node@1.0.0 - -## 0.11.12 - -### Patch Changes - -- cea6f10b97: **DEPRECATION** - - The `@backstage/techdocs-common` package is being renamed `@backstage/plugin-techdocs-node`. We may continue to publish changes to `@backstage/techdocs-common` for a time, but will stop doing so in the near future. If you depend on this package, you should update your dependencies to point at the renamed package. - -- Updated dependencies - - @backstage/plugin-techdocs-node@0.11.12 - -## 0.11.12-next.0 - -### Patch Changes - -- cea6f10b97: **DEPRECATION** - - The `@backstage/techdocs-common` package is being renamed `@backstage/plugin-techdocs-node`. We may continue to publish changes to `@backstage/techdocs-common` for a time, but will stop doing so in the near future. If you depend on this package, you should update your dependencies to point at the renamed package. - -- Updated dependencies - - @backstage/plugin-techdocs-node@0.11.12-next.0 - -## 0.11.11 - -### Patch Changes - -- 955be6bc7d: adds passing projectID to the Storage client -- ff0a16fb1a: Modify techdocs builder to automatically append techdocs-core plugin to mkdocs.yaml file if it is missing. Adds an optional configuration item if this plugin needs to be omitted. -- Updated dependencies - - @backstage/catalog-model@0.12.0 - - @backstage/backend-common@0.12.0 - - @backstage/integration@0.8.0 - - @backstage/search-common@0.3.0 - -## 0.11.10 - -### Patch Changes - -- 209fd128e6: Updated usage of `github:` location types in docs to use `url:` instead. -- 13ef228d03: Clean up the API interface for TechDocs common library. -- Updated dependencies - - @backstage/backend-common@0.11.0 - - @backstage/catalog-model@0.11.0 - - @backstage/integration@0.7.5 - -## 0.11.9 - -### Patch Changes - -- Fix for the previous release with missing type declarations. -- Updated dependencies - - @backstage/backend-common@0.10.9 - - @backstage/catalog-model@0.10.1 - - @backstage/config@0.1.15 - - @backstage/errors@0.2.2 - - @backstage/integration@0.7.4 - - @backstage/search-common@0.2.4 - -## 0.11.8 - -### Patch Changes - -- c77c5c7eb6: Added `backstage.role` to `package.json` -- 216725b434: Updated to use new names for `parseLocationRef` and `stringifyLocationRef` -- 7aeb491394: Replace use of deprecated `ENTITY_DEFAULT_NAMESPACE` constant with `DEFAULT_NAMESPACE`. -- Updated dependencies - - @backstage/backend-common@0.10.8 - - @backstage/errors@0.2.1 - - @backstage/integration@0.7.3 - - @backstage/catalog-model@0.10.0 - - @backstage/config@0.1.14 - - @backstage/search-common@0.2.3 - -## 0.11.7 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.10.7 - -## 0.11.7-next.0 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.10.7-next.0 - -## 0.11.6 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.10.6 - -## 0.11.6-next.0 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.10.6-next.0 - -## 0.11.5 - -### Patch Changes - -- ff93fbeeec: Fix interpolated string for "Failed to generate docs from ..." -- Updated dependencies - - @backstage/search-common@0.2.2 - - @backstage/backend-common@0.10.5 - -## 0.11.4 - -### Patch Changes - -- 47277c0d8c: Updated the default version of the `@spotify/techdocs` container used when `techdocs.generator.runIn` is `docker` to `v0.3.6`, which includes an update to `mkdocs-monorepo-plugin` that allows glob-based wildcard includes. -- Updated dependencies - - @backstage/integration@0.7.2 - - @backstage/backend-common@0.10.4 - - @backstage/config@0.1.13 - - @backstage/catalog-model@0.9.10 - -## 0.11.4-next.0 - -### Patch Changes - -- 47277c0d8c: Updated the default version of the `@spotify/techdocs` container used when `techdocs.generator.runIn` is `docker` to `v0.3.6`, which includes an update to `mkdocs-monorepo-plugin` that allows glob-based wildcard includes. -- Updated dependencies - - @backstage/backend-common@0.10.4-next.0 - - @backstage/config@0.1.13-next.0 - - @backstage/catalog-model@0.9.10-next.0 - - @backstage/integration@0.7.2-next.0 - -## 0.11.3 - -### Patch Changes - -- 5333451def: Cleaned up API exports -- Updated dependencies - - @backstage/config@0.1.12 - - @backstage/integration@0.7.1 - - @backstage/backend-common@0.10.3 - - @backstage/errors@0.2.0 - - @backstage/catalog-model@0.9.9 - -## 0.11.2 - -### Patch Changes - -- c2c8768771: Bump `@azure/identity` from `^1.5.0` to `^2.0.1`. -- Updated dependencies - - @backstage/backend-common@0.10.1 - - @backstage/integration@0.7.0 - -## 0.11.1 - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.10.0 - -## 0.11.0 - -### Minor Changes - -- 1bada775a9: Added the ability for the TechDocs Backend to (optionally) leverage a cache - store to improve performance when reading files from a cloud storage provider. - -### Patch Changes - -- dcd1a0c3f4: Minor improvement to the API reports, by not unpacking arguments directly -- Updated dependencies - - @backstage/backend-common@0.9.13 - -## 0.10.8 - -### Patch Changes - -- bab752e2b3: Change default port of backend from 7000 to 7007. - - This is due to the AirPlay Receiver process occupying port 7000 and preventing local Backstage instances on MacOS to start. - - You can change the port back to 7000 or any other value by providing an `app-config.yaml` with the following values: - - ``` - backend: - listen: 0.0.0.0:7123 - baseUrl: http://localhost:7123 - ``` - - More information can be found here: https://backstage.io/docs/conf/writing - -- Updated dependencies - - @backstage/errors@0.1.5 - - @backstage/backend-common@0.9.11 - -## 0.10.7 - -### Patch Changes - -- 0b60a051c9: Added OpenStack Swift case migration support. -- 9e64a7ac1e: Allow amazon web services s3 buckets to pass an server side encryption configuration so they can publish to encrypted buckets -- Updated dependencies - - @backstage/catalog-model@0.9.7 - - @backstage/backend-common@0.9.10 - -## 0.10.6 - -### Patch Changes - -- a2d4389587: 1. Techdocs publisher constructors now use parameter objects when being - instantiated - - 2. Internal refactor of `LocalPublish` publisher to use `fromConfig` for - creation to be aligned with other publishers; this does not impact - `LocalPublish` usage. - - ```diff - - const publisher = new LocalPublish(config, logger, discovery); - + const publisher = LocalPublish.fromConfig(config, logger, discovery); - ``` - -- 6129c89a47: Default TechDocs container used at docs generation-time is now [v0.3.5](https://github.com/backstage/techdocs-container/releases/tag/v0.3.5). -- f3c7eec64b: Updated to properly join URL segments under any OS for both AWS S3 and GCP -- Updated dependencies - - @backstage/backend-common@0.9.9 - -## 0.10.5 - -### Patch Changes - -- d207f6ee9e: Support optional bucketRootPath configuration parameter in S3 and GCS publishers -- Updated dependencies - - @backstage/config@0.1.11 - - @backstage/errors@0.1.4 - - @backstage/integration@0.6.9 - - @backstage/backend-common@0.9.8 - - @backstage/catalog-model@0.9.6 - - @backstage/search-common@0.2.1 - -## 0.10.4 - -### Patch Changes - -- 87f5b9db13: Use docs/README.md or README.md as fallback if docs/index.md is missing -- 36e67d2f24: Internal updates to apply more strict checks to throw errors. -- Updated dependencies - - @backstage/backend-common@0.9.7 - - @backstage/errors@0.1.3 - - @backstage/catalog-model@0.9.5 - -## 0.10.3 - -### Patch Changes - -- 156421c59a: Sets the default techdocs docker image to the [latest released version - v0.3.3](https://github.com/backstage/techdocs-container/releases/tag/v0.3.3). -- Updated dependencies - - @backstage/catalog-model@0.9.4 - - @backstage/backend-common@0.9.6 - - @backstage/integration@0.6.7 - -## 0.10.2 - -### Patch Changes - -- 1c75e8bf98: Add more context to techdocs log lines when files are not found along with - ensuring that the routers return 404 with a descriptive message. -- e92f0f728b: Locks the version of the default docker image used to generate TechDocs. As of - this changelog entry, it is v0.3.2! -- Updated dependencies - - @backstage/backend-common@0.9.5 - - @backstage/integration@0.6.6 - -## 0.10.1 - -### Patch Changes - -- 96fef17a18: Upgrade git-parse-url to v11.6.0 -- Updated dependencies - - @backstage/backend-common@0.9.3 - - @backstage/integration@0.6.4 - -## 0.10.0 - -### Minor Changes - -- 8b0f6f860: Set the correct `edit_uri` or `repo_url` for documentation pages that are hosted on GitHub and GitLab. - - The constructor of the `TechDocsGenerator` changed. - Prefer the use of `TechdocsGenerator.fromConfig(โ€ฆ)` instead: - - ```diff - - const techdocsGenerator = new TechdocsGenerator({ - + const techdocsGenerator = TechdocsGenerator.fromConfig(config, { - logger, - containerRunner, - - config, - }); - ``` - -### Patch Changes - -- 30ed662a3: Adding in-context search to TechDocs Reader component. Using existing search-backend to query for indexed search results scoped into a specific entity's techdocs. Needs TechDocsCollator enabled on the backend to work. - - Adding extra information to indexed tech docs documents for search. - -- 3624616e7: "Local" (out-of-the-box) publisher explicitly follows lower-case entity triplet - logic. -- 67ba7e088: Only write the updated `mkdocs.yml` file if the content was updated. - - This keeps local files unchanged if the `dir` annotation is used in combination with the `file` location. - -- 8eab6be6a: Force using `posix` path for cloud storage -- Updated dependencies - - @backstage/integration@0.6.3 - - @backstage/search-common@0.2.0 - - @backstage/catalog-model@0.9.1 - - @backstage/backend-common@0.9.1 - -## 0.9.0 - -### Minor Changes - -- 58452cdb7: OpenStack Swift Client changed with Trendyol's OpenStack Swift SDK. - - ## Migration from old OpenStack Swift Configuration - - Let's assume we have the old OpenStack Swift configuration here. - - ```yaml - techdocs: - publisher: - type: 'openStackSwift' - openStackSwift: - containerName: 'name-of-techdocs-storage-bucket' - credentials: - username: ${OPENSTACK_SWIFT_STORAGE_USERNAME} - password: ${OPENSTACK_SWIFT_STORAGE_PASSWORD} - authUrl: ${OPENSTACK_SWIFT_STORAGE_AUTH_URL} - keystoneAuthVersion: ${OPENSTACK_SWIFT_STORAGE_AUTH_VERSION} - domainId: ${OPENSTACK_SWIFT_STORAGE_DOMAIN_ID} - domainName: ${OPENSTACK_SWIFT_STORAGE_DOMAIN_NAME} - region: ${OPENSTACK_SWIFT_STORAGE_REGION} - ``` - - ##### Step 1: Change the credential keys - - Since the new SDK uses _Application Credentials_ to authenticate OpenStack, we - need to change the keys `credentials.username` to `credentials.id`, - `credentials.password` to `credentials.secret` and use Application Credential ID - and secret here. For more detail about credentials look - [here](https://docs.openstack.org/api-ref/identity/v3/?expanded=password-authentication-with-unscoped-authorization-detail,authenticating-with-an-application-credential-detail#authenticating-with-an-application-credential). - - ##### Step 2: Remove the unused keys - - Since the new SDK doesn't use the old way authentication, we don't need the keys - `openStackSwift.keystoneAuthVersion`, `openStackSwift.domainId`, - `openStackSwift.domainName` and `openStackSwift.region`. So you can remove them. - - ##### Step 3: Add Swift URL - - The new SDK needs the OpenStack Swift connection URL for connecting the Swift. - So you need to add a new key called `openStackSwift.swiftUrl` and give the - OpenStack Swift url here. Example url should look like that: - `https://example.com:6780/swift/v1` - - ##### That's it! - - Your new configuration should look like that! - - ```yaml - techdocs: - publisher: - type: 'openStackSwift' - openStackSwift: - containerName: 'name-of-techdocs-storage-bucket' - credentials: - id: ${OPENSTACK_SWIFT_STORAGE_APPLICATION_CREDENTIALS_ID} - secret: ${OPENSTACK_SWIFT_STORAGE_APPLICATION_CREDENTIALS_SECRET} - authUrl: ${OPENSTACK_SWIFT_STORAGE_AUTH_URL} - swiftUrl: ${OPENSTACK_SWIFT_STORAGE_SWIFT_URL} - ``` - -- c772d9a84: TechDocs sites can now be accessed using paths containing entity triplets of - any case (e.g. `/docs/namespace/KIND/name` or `/docs/namespace/kind/name`). - - If you do not use an external storage provider for serving TechDocs, this is a - transparent change and no action is required from you. - - If you _do_ use an external storage provider for serving TechDocs (one of\* GCS, - AWS S3, or Azure Blob Storage), you must run a migration command against your - storage provider before updating. - - [A migration guide is available here](https://backstage.io/docs/features/techdocs/how-to-guides#how-to-migrate-from-techdocs-alpha-to-beta). - - - (\*) We're seeking help from the community to bring OpenStack Swift support - [to feature parity](https://github.com/backstage/backstage/issues/6763) with the above. - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.9.0 - - @backstage/integration@0.6.2 - - @backstage/config@0.1.8 - -## 0.8.1 - -### Patch Changes - -- bc405be6e: Stale TechDocs content (files that had previously been published but which have - since been removed) is now removed from storage at publish-time. This is now - supported by the following publishers: - - - Google GCS - - AWS S3 - - Azure Blob Storage - - You may need to apply a greater level of permissions (e.g. the ability to - delete objects in your storage provider) to any credentials/accounts used by - the TechDocs CLI or TechDocs backend in order for this change to take effect. - - For more details, see [#6132][issue-ref]. - - [issue-ref]: https://github.com/backstage/backstage/issues/6132 - -- Updated dependencies - - @backstage/integration@0.6.0 - - @backstage/backend-common@0.8.9 - -## 0.8.0 - -### Minor Changes - -- 48ea3d25b: TechDocs has dropped all support for the long-ago deprecated git-based common - prepares as well as all corresponding values in `backstage.io/techdocs-ref` - annotations. - - Entities whose `backstage.io/techdocs-ref` annotation values still begin with - `github:`, `gitlab:`, `bitbucket:`, or `azure/api:` will no longer be generated - by TechDocs. Be sure to update these values so that they align with their - expected format and your usage of TechDocs. - - For details, see [this explainer on TechDocs ref annotation values][how]. - - [how]: https://backstage.io/docs/features/techdocs/how-to-guides#how-to-understand-techdocs-ref-annotation-values - -### Patch Changes - -- Updated dependencies - - @backstage/backend-common@0.8.8 - - @backstage/config@0.1.6 - - @backstage/integration@0.5.9 - -## 0.7.1 - -### Patch Changes - -- 59a5fa319: Migrated files are now printed when `techdocs-cli migrate` is run with the - `--verbose` flag set. -- 54356336e: TechDocs generator stage now supports `mkdocs.yaml` file, in addition to `.yml` - depending on whichever is present at the time of generation. (Assumes the - latest `spotify/techdocs` container, running mkdocs `v1.2.2` or greater). - -## 0.7.0 - -### Minor Changes - -- d32d01e5b: Improve the annotation `backstage.io/techdocs-ref: dir:` that links to a path that is relative to the source of the annotated entity. - This annotation works with the basic and the recommended flow, however, it will be most useful with the basic approach. - - This change remove the deprecation of the `dir` reference and provides first-class support for it. - In addition, this change removes the support of the deprecated `github`, `gitlab`, and `azure/api` locations from the `dir` reference preparer. - - #### Example Usage - - The annotation is convenient if the documentation is stored in the same location, i.e. the same git repository, as the `catalog-info.yaml`. - While it is still supported to add full URLs such as `backstage.io/techdocs-ref: url:https://...` for custom setups, documentation is mostly stored in the same repository as the entity definition. - By automatically resolving the target relative to the registration location of the entity, the configuration overhead for this default setup is minimized. - Since it leverages the `@backstage/integrations` package for the URL resolution, this is compatible with every supported source. - - Consider the following examples: - - 1. "I have a repository with a single `catalog-info.yaml` and a TechDocs page in the root folder!" - - ``` - https://github.com/backstage/example/tree/main/ - |- catalog-info.yaml - | > apiVersion: backstage.io/v1alpha1 - | > kind: Component - | > metadata: - | > name: example - | > annotations: - | > backstage.io/techdocs-ref: dir:. # -> same folder - | > spec: {} - |- docs/ - |- mkdocs.yml - ``` - - 2. "I have a repository with a single `catalog-info.yaml` and my TechDocs page in located in a folder!" - - ``` - https://bitbucket.org/my-owner/my-project/src/master/ - |- catalog-info.yaml - | > apiVersion: backstage.io/v1alpha1 - | > kind: Component - | > metadata: - | > name: example - | > annotations: - | > backstage.io/techdocs-ref: dir:./some-folder # -> subfolder - | > spec: {} - |- some-folder/ - |- docs/ - |- mkdocs.yml - ``` - - 3. "I have a mono repository that hosts multiple components!" - - ``` - https://dev.azure.com/organization/project/_git/repository - |- my-1st-module/ - |- catalog-info.yaml - | > apiVersion: backstage.io/v1alpha1 - | > kind: Component - | > metadata: - | > name: my-1st-module - | > annotations: - | > backstage.io/techdocs-ref: dir:. # -> same folder - | > spec: {} - |- docs/ - |- mkdocs.yml - |- my-2nd-module/ - |- catalog-info.yaml - | > apiVersion: backstage.io/v1alpha1 - | > kind: Component - | > metadata: - | > name: my-2nd-module - | > annotations: - | > backstage.io/techdocs-ref: dir:. # -> same folder - | > spec: {} - |- docs/ - |- mkdocs.yml - |- catalog-info.yaml - | > apiVersion: backstage.io/v1alpha1 - | > kind: Location - | > metadata: - | > name: example - | > spec: - | > targets: - | > - ./*/catalog-info.yaml - ``` - -### Patch Changes - -- 6e5aed1c9: Fix validation of mkdocs.yml docs_dir -- 250984333: Add link to https://backstage.io/docs/features/techdocs/configuration in the log warning message about updating techdocs.generate key. -- Updated dependencies - - @backstage/backend-common@0.8.7 - -## 0.6.8 - -### Patch Changes - -- d5eaab91d: Adds custom docker image support to the techdocs generator. This change adds a new `techdocs.generator` configuration key and deprecates the existing `techdocs.generators.techdocs` key. - - ```yaml - techdocs: - # recommended, going forward: - generator: - runIn: 'docker' # or 'local' - # New optional settings - dockerImage: my-org/techdocs # use a custom docker image - pullImage: false # disable automatic pulling of image (e.g. if custom docker login is required) - # legacy (deprecated): - generators: - techdocs: 'docker' # or 'local' - ``` - -- c18e8eb91: Provide optional `logger: Logger` and `logStream: Writable` arguments to the `GeneratorBase#run(...)` command. - They receive all log messages that are emitted during the generator run. -- ae84b20cf: Revert the upgrade to `fs-extra@10.0.0` as that seemed to have broken all installs inexplicably. -- Updated dependencies - - @backstage/backend-common@0.8.6 - -## 0.6.7 - -### Patch Changes - -- 683308ecf: Fix openStack swift publisher encoding issue. Remove utf8 forced encoding on binary files -- 6841e0113: fix minor version of git-url-parse as 11.5.x introduced a bug for Bitbucket Server -- Updated dependencies - - @backstage/integration@0.5.8 - - @backstage/catalog-model@0.9.0 - - @backstage/backend-common@0.8.5 - -## 0.6.6 - -### Patch Changes - -- ab5cc376f: Use new utilities from `@backstage/backend-common` for safely resolving child paths -- b47fc34bc: Update "service catalog" references to "software catalog" -- Updated dependencies - - @backstage/backend-common@0.8.4 - - @backstage/integration@0.5.7 - -## 0.6.5 - -### Patch Changes - -- c17c0fcf9: Adding additional checks on tech docs to prevent folder traversal via mkdocs.yml docs_dir value. -- Updated dependencies - - @backstage/catalog-model@0.8.4 - -## 0.6.4 - -### Patch Changes - -- aad98c544: Fixes multiple XSS and sanitization bypass vulnerabilities in TechDocs. -- 090594755: Support parsing `mkdocs.yml` files that are using custom yaml tags like - `!!python/name:materialx.emoji.twemoji`. -- Updated dependencies [ebe802bc4] -- Updated dependencies [49d7ec169] - - @backstage/catalog-model@0.8.1 - - @backstage/integration@0.5.5 - -## 0.6.3 - -### Patch Changes - -- 8cefadca0: Adding validation to mkdocs.yml parsing to prevent directory tree traversing -- Updated dependencies [0fd4ea443] -- Updated dependencies [add62a455] -- Updated dependencies [704875e26] - - @backstage/integration@0.5.4 - - @backstage/catalog-model@0.8.0 - -## 0.6.2 - -### Patch Changes - -- 65e6c4541: Remove circular dependencies -- Updated dependencies [f7f7783a3] -- Updated dependencies [c7dad9218] -- Updated dependencies [65e6c4541] -- Updated dependencies [68fdbf014] -- Updated dependencies [5001de908] - - @backstage/catalog-model@0.7.10 - - @backstage/backend-common@0.8.1 - - @backstage/integration@0.5.3 - -## 0.6.1 - -### Patch Changes - -- e04f1ccfb: Fixed a bug that prevented loading static assets from GCS, S3, Azure, and OpenStackSwift whose keys contain spaces or other special characters. -- Updated dependencies [22fd8ce2a] -- Updated dependencies [10c008a3a] -- Updated dependencies [f9fb4a205] -- Updated dependencies [16be1d093] - - @backstage/backend-common@0.8.0 - - @backstage/catalog-model@0.7.9 - -## 0.6.0 - -### Minor Changes - -- e0bfd3d44: Migrate the package to use the `ContainerRunner` interface instead of `runDockerContainer(โ€ฆ)`. - It also no longer provides the `ContainerRunner` as an input to the `GeneratorBase#run(โ€ฆ)` function, but expects it as a constructor parameter instead. - - If you use the `TechdocsGenerator` you need to update the usage: - - ```diff - + const containerRunner = new DockerContainerRunner({ dockerClient }); - - - const generator = new TechdocsGenerator(logger, config); - + const techdocsGenerator = new TechdocsGenerator({ - + logger, - + containerRunner, - + config, - + }); - - await this.generator.run({ - inputDir: preparedDir, - outputDir, - - dockerClient: this.dockerClient, - parsedLocationAnnotation, - etag: newEtag, - }); - ``` - -### Patch Changes - -- e9e56b01a: Adding optional config to enable S3-like API for tech-docs using s3ForcePathStyle option. - This allows providers like LocalStack, Minio and Wasabi (+possibly others) to be used to host tech docs. -- Updated dependencies [e0bfd3d44] -- Updated dependencies [38ca05168] -- Updated dependencies [d8b81fd28] - - @backstage/backend-common@0.7.0 - - @backstage/integration@0.5.2 - - @backstage/catalog-model@0.7.8 - - @backstage/config@0.1.5 - -## 0.5.1 - -### Patch Changes - -- f4af06ebe: Gracefully handle HTTP request failures in download method of AzureBlobStorage publisher. - -## 0.5.0 - -### Minor Changes - -- bc9d62f4f: Move the sanity checks of the publisher configurations to a dedicated `PublisherBase#getReadiness()` method instead of throwing an error when doing `Publisher.fromConfig(...)`. - You should include the check when your backend to get early feedback about a potential misconfiguration: - - ```diff - // packages/backend/src/plugins/techdocs.ts - - export default async function createPlugin({ - logger, - config, - discovery, - reader, - }: PluginEnvironment): Promise { - // ... - - const publisher = await Publisher.fromConfig(config, { - logger, - discovery, - }) - - + // checks if the publisher is working and logs the result - + await publisher.getReadiness(); - - // Docker client (conditionally) used by the generators, based on techdocs.generators config. - const dockerClient = new Docker(); - - // ... - } - ``` - - If you want to crash your application on invalid configurations, you can throw an `Error` to preserve the old behavior. - Please be aware that this is not the recommended for the use in a Backstage backend but might be helpful in CLI tools such as the `techdocs-cli`. - - ```ts - const publisher = await Publisher.fromConfig(config, { - logger, - discovery, - }); - - const ready = await publisher.getReadiness(); - if (!ready.isAvailable) { - throw new Error('Invalid TechDocs publisher configuration'); - } - ``` - -### Patch Changes - -- Updated dependencies [bb5055aee] -- Updated dependencies [5d0740563] - - @backstage/catalog-model@0.7.7 - -## 0.4.5 - -### Patch Changes - -- 8686eb38c: Use errors from `@backstage/errors` -- 424742dc1: Applies only if you use TechDocs local builder instead of building on CI/CD i.e. if `techdocs.builder` in your `app-config.yaml` is set to `'local'` - - Improvements - - 1. Do not check for updates in the repository if a check has been made in the last 60 seconds. This is to prevent the annoying check for update on every page switch or load. - 2. No need to maintain an in-memory etag storage, and use the one stored in `techdocs_metadata.json` file alongside generated docs. - - New feature - - 1. You can now use a mix of basic and recommended setup i.e. `techdocs.builder` is `'local'` but using an external cloud storage instead of local storage. Previously, in this setup, the docs would never get updated. - -- Updated dependencies [8686eb38c] -- Updated dependencies [0434853a5] -- Updated dependencies [8686eb38c] - - @backstage/backend-common@0.6.0 - - @backstage/config@0.1.4 - -## 0.4.4 - -### Patch Changes - -- d7245b733: Remove runDockerContainer, and start using the utility function provided by @backstage/backend-common -- 0b42fff22: Make use of parseLocationReference/stringifyLocationReference -- 2ef5bc7ea: Implement proper AWS Credentials precedence with assume-role and explicit credentials -- aa095e469: OpenStack Swift publisher added for tech-docs. -- bc46435f5: - Improve deprecation warning messaging in logs. - - Replace temp folder path from git provider domain(`source`) to full git host name (`resource`). (See: https://github.com/IonicaBizau/git-url-parse#giturlparseurl) -- a501128db: Refactor log messaging to improve clarity -- ca4a904f6: Add an optional configuration option for setting the url endpoint for AWS S3 publisher: `techdocs.publisher.awsS3.endpoint` -- Updated dependencies [277644e09] -- Updated dependencies [52f613030] -- Updated dependencies [d7245b733] -- Updated dependencies [0b42fff22] -- Updated dependencies [905cbfc96] -- Updated dependencies [761698831] -- Updated dependencies [d4e77ec5f] - - @backstage/integration@0.5.1 - - @backstage/backend-common@0.5.6 - - @backstage/catalog-model@0.7.4 - -## 0.4.3 - -### Patch Changes - -- f43192207: remove usage of res.send() for res.json() and res.end() to ensure content types are more consistently application/json on backend responses and error cases -- 61299519f: Remove read-store-upload loop when uploading S3 objects for TechDocs -- Updated dependencies [12d8f27a6] -- Updated dependencies [497859088] -- Updated dependencies [8adb48df4] - - @backstage/catalog-model@0.7.3 - - @backstage/backend-common@0.5.5 - -## 0.4.2 - -### Patch Changes - -- 2499f6cde: Add support for assuming role in AWS integrations -- 1e4ddd71d: Fix AWS, GCS and Azure publisher to work on Windows. -- Updated dependencies [bad21a085] -- Updated dependencies [a1f5e6545] - - @backstage/catalog-model@0.7.2 - - @backstage/config@0.1.3 - -## 0.4.1 - -### Patch Changes - -- fb28da212: Switched to using `'x-access-token'` for authenticating Git over HTTPS towards GitHub. -- 26e143e60: After TechDocs generate step, insert build timestamp to techdocs_metadata.json -- c6655413d: Improved error reporting in AzureBlobStorage to surface errors when fetching metadata and uploading files fails. -- 44414239f: Pass user and group ID when invoking docker container. When TechDocs invokes Docker, docker could be run as a `root` user which results in generation of files by applications run by non-root user (e.g. TechDocs) will not have access to modify. This PR passes in current user and group ID to docker so that the file permissions of the generated files and folders are correct. -- b0a41c707: Add etag of the prepared file tree to techdocs_metadata.json in the storage -- Updated dependencies [16fb1d03a] -- Updated dependencies [491f3a0ec] -- Updated dependencies [491f3a0ec] -- Updated dependencies [434b4e81a] -- Updated dependencies [fb28da212] - - @backstage/backend-common@0.5.4 - - @backstage/integration@0.5.0 - -## 0.4.0 - -### Minor Changes - -- 08142b256: URL Preparer will now use proper etag based caching introduced in https://github.com/backstage/backstage/pull/4120. Previously, builds used to be cached for 30 minutes. - -### Patch Changes - -- 77ad0003a: Revert AWS SDK version to v2 -- 08142b256: TechDocs will throw warning in backend logs when legacy git preparer or dir preparer is used to preparer docs. Migrate to URL Preparer by updating `backstage.io/techdocs-ref` annotation to be prefixed with `url:`. - Detailed docs are here https://backstage.io/docs/features/techdocs/how-to-guides#how-to-use-url-reader-in-techdocs-prepare-step - See benefits and reason for doing so https://github.com/backstage/backstage/issues/4409 -- Updated dependencies [ffffea8e6] -- Updated dependencies [82b2c11b6] -- Updated dependencies [965e200c6] -- Updated dependencies [ffffea8e6] -- Updated dependencies [5a5163519] - - @backstage/backend-common@0.5.3 - - @backstage/integration@0.4.0 - -## 0.3.7 - -### Patch Changes - -- c777df180: 1. Added option to use Azure Blob Storage as a choice to store the static generated files for TechDocs. -- e44925723: `techdocs.requestUrl` and `techdocs.storageUrl` are now optional configs and the discovery API will be used to get the URL where techdocs plugin is hosted. -- f0320190d: dir preparer will use URL Reader in its implementation. -- Updated dependencies [c4abcdb60] -- Updated dependencies [2430ee7c2] -- Updated dependencies [6e612ce25] -- Updated dependencies [025e122c3] -- Updated dependencies [064c513e1] -- Updated dependencies [7881f2117] -- Updated dependencies [3149bfe63] -- Updated dependencies [2e62aea6f] -- Updated dependencies [11cb5ef94] - - @backstage/integration@0.3.2 - - @backstage/backend-common@0.5.2 - - @backstage/catalog-model@0.7.1 - -## 0.3.6 - -### Patch Changes - -- 9dd057662: Upgrade [git-url-parse](https://www.npmjs.com/package/git-url-parse) to [v11.4.4](https://github.com/IonicaBizau/git-url-parse/pull/125) which fixes parsing an Azure DevOps branch ref. -- db2328c88: Add rate limiter for concurrent execution of file uploads in AWS and Google publishers -- Updated dependencies [26a3a6cf0] -- Updated dependencies [664dd08c9] -- Updated dependencies [6800da78d] -- Updated dependencies [9dd057662] - - @backstage/backend-common@0.5.1 - - @backstage/integration@0.3.1 - -## 0.3.5 - -### Patch Changes - -- 53c9c51f2: TechDocs backend now streams files through from Google Cloud Storage to the browser, improving memory usage. -- a5e27d5c1: Create type for TechDocsMetadata (#3716) - - This change introduces a new type (TechDocsMetadata) in packages/techdocs-common. This type is then introduced in the endpoint response in techdocs-backend and in the api interface in techdocs (frontend). - -- Updated dependencies [def2307f3] -- Updated dependencies [0b135e7e0] -- Updated dependencies [294a70cab] -- Updated dependencies [fa8ba330a] -- Updated dependencies [0ea032763] -- Updated dependencies [5345a1f98] -- Updated dependencies [ed6baab66] -- Updated dependencies [09a370426] -- Updated dependencies [a93f42213] - - @backstage/catalog-model@0.7.0 - - @backstage/backend-common@0.5.0 - - @backstage/integration@0.3.0 - -## 0.3.4 - -### Patch Changes - -- a594a7257: @backstage/techdocs-common can now be imported in an environment without @backstage/plugin-techdocs-backend being installed. - -## 0.3.3 - -### Patch Changes - -- 68ad5af51: Improve techdocs-common Generator API for it to be used by techdocs-cli. TechDocs generator.run function now takes - an input AND an output directory. Most probably you use techdocs-common via plugin-techdocs-backend, and so there - is no breaking change for you. - But if you use techdocs-common separately, you need to create an output directory and pass into the generator. -- 371f67ecd: fix to-string breakage of binary files -- f1e74777a: Fix bug where binary files (`png`, etc.) could not load when using AWS or GCS publisher. -- dbe4450c3: Google Cloud authentication in TechDocs has been improved. - - 1. `techdocs.publisher.googleGcs.credentials` is now optional. If it is missing, `GOOGLE_APPLICATION_CREDENTIALS` - environment variable (and some other methods) will be used to authenticate. - Read more here https://cloud.google.com/docs/authentication/production - - 2. `techdocs.publisher.googleGcs.projectId` is no longer used. You can remove it from your `app-config.yaml`. - -- 5826d0973: AWS SDK version bump for TechDocs. -- b3b9445df: AWS S3 authentication in TechDocs has been improved. - - 1. `techdocs.publisher.awsS3.bucketName` is now the only required config. `techdocs.publisher.awsS3.credentials` and `techdocs.publisher.awsS3.region` are optional. - - 2. If `techdocs.publisher.awsS3.credentials` and `techdocs.publisher.awsS3.region` are missing, the AWS environment variables `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY` and `AWS_REGION` will be used. There are more better ways of setting up AWS authentication. Read the guide at https://backstage.io/docs/features/techdocs/using-cloud-storage - -- Updated dependencies [466354aaa] -- Updated dependencies [f3b064e1c] -- Updated dependencies [abbee6fff] -- Updated dependencies [147fadcb9] - - @backstage/integration@0.2.0 - - @backstage/catalog-model@0.6.1 - - @backstage/backend-common@0.4.3 - -## 0.3.2 - -### Patch Changes - -- 7ec525481: 1. Added option to use AWS S3 as a choice to store the static generated files for TechDocs. -- f8ba88ded: Fix for `integration.github.apiBaseUrl` configuration not properly overriding apiBaseUrl used by techdocs -- 00042e73c: Moving the Git actions to isomorphic-git instead of the node binding version of nodegit -- Updated dependencies [5ecd50f8a] -- Updated dependencies [00042e73c] -- Updated dependencies [0829ff126] -- Updated dependencies [036a84373] - - @backstage/backend-common@0.4.2 - - @backstage/integration@0.1.5 - -## 0.3.1 - -### Patch Changes - -- 8804e8981: Using @backstage/integration package for GitHub/GitLab/Azure tokens and request options. - - Most probably you do not have to make any changes in the app because of this change. - However, if you are using the `DirectoryPreparer` or `CommonGitPreparer` exported by - `@backstage/techdocs-common` package, you now need to add pass in a `config` (from `@backstage/config`) - instance as argument. - - ``` - - const directoryPreparer = new DirectoryPreparer(logger); - const commonGitPreparer = new CommonGitPreparer(logger); - - const directoryPreparer = new DirectoryPreparer(config, logger); - const commonGitPreparer = new CommonGitPreparer(config, logger); - ``` - -## 0.3.0 - -### Minor Changes - -- a8573e53b: techdocs-backend: Simplified file, removing individual preparers and generators. - techdocs-backend: UrlReader is now available to use in preparers. - - In your Backstage app, `packages/backend/plugins/techdocs.ts` file has now been simplified, - to remove registering individual preparers and generators. - - Please update the file when upgrading the version of `@backstage/plugin-techdocs-backend` package. - - ```typescript - const preparers = await Preparers.fromConfig(config, { - logger, - reader, - }); - - const generators = await Generators.fromConfig(config, { - logger, - }); - - const publisher = await Publisher.fromConfig(config, { - logger, - discovery, - }); - ``` - - You should be able to remove unnecessary imports, and just do - - ```typescript - import { - createRouter, - Preparers, - Generators, - Publisher, - } from '@backstage/plugin-techdocs-backend'; - ``` - -## 0.2.0 - -### Minor Changes - -- dae4f3983: _Breaking changes_ - - 1. Added option to use Google Cloud Storage as a choice to store the static generated files for TechDocs. - It can be configured using `techdocs.publisher.type` option in `app-config.yaml`. - Step-by-step guide to configure GCS is available here https://backstage.io/docs/features/techdocs/using-cloud-storage - Set `techdocs.publisher.type` to `'local'` if you want to continue using local filesystem to store TechDocs files. - - 2. `techdocs.builder` is now required and can be set to `'local'` or `'external'`. (Set it to `'local'` for now, since CI/CD build - workflow for TechDocs will be available soon (in few weeks)). - If builder is set to 'local' and you open a TechDocs page, `techdocs-backend` will try to generate the docs, publish to storage and - show the generated docs afterwords. - If builder is set to `'external'`, `techdocs-backend` will only fetch the docs and will NOT try to generate and publish. In this case of `'external'`, - we assume that docs are being built in the CI/CD pipeline of the repository. - TechDocs will not assume a default value for `techdocs.builder`. It is better to explicitly define it in the `app-config.yaml`. - - 3. When configuring TechDocs in your backend, there is a difference in how a new publisher is created. - - ``` - --- const publisher = new LocalPublish(logger, discovery); - +++ const publisher = Publisher.fromConfig(config, logger, discovery); - ``` - - Based on the config `techdocs.publisher.type`, the publisher could be either Local publisher or Google Cloud Storage publisher. - - 4. `techdocs.storageUrl` is now a required config. Should be `http://localhost:7007/api/techdocs/static/docs` in most setups. - - 5. Parts of `@backstage/plugin-techdocs-backend` have been moved to a new package `@backstage/techdocs-common` to generate docs. Also to publish docs - to-and-fro between TechDocs and a storage (either local or external). However, a Backstage app does NOT need to import the `techdocs-common` package - - app should only import `@backstage/plugin-techdocs` and `@backstage/plugin-techdocs-backend`. - - _Patch changes_ - - 1. See all of TechDocs config options and its documentation https://backstage.io/docs/features/techdocs/configuration - - 2. Logic about serving static files and metadata retrieval have been abstracted away from the router in `techdocs-backend` to the instance of publisher. - - 3. Removed Material UI Spinner from TechDocs header. Spinners cause unnecessary UX distraction. - Case 1 (when docs are built and are to be served): Spinners appear for a split second before the name of site shows up. This unnecessarily distracts eyes because spinners increase the size of the Header. A dot (.) would do fine. Definitely more can be done. - Case 2 (when docs are being generated): There is already a linear progress bar (which is recommended in Storybook). - -### Patch Changes - -- Updated dependencies [c911061b7] -- Updated dependencies [1d1c2860f] -- Updated dependencies [0e6298f7e] -- Updated dependencies [4eafdec4a] -- Updated dependencies [ac3560b42] - - @backstage/catalog-model@0.6.0 - - @backstage/backend-common@0.4.1 diff --git a/packages/techdocs-common/README.md b/packages/techdocs-common/README.md deleted file mode 100644 index 052eae7ab2..0000000000 --- a/packages/techdocs-common/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# @backstage/techdocs-common - -This package has been moved to `@backstage/plugin-techdocs-node` and is no -longer maintained. Please update any dependencies you have on this package -accordingly. diff --git a/packages/techdocs-common/api-report.md b/packages/techdocs-common/api-report.md deleted file mode 100644 index 62851bbd68..0000000000 --- a/packages/techdocs-common/api-report.md +++ /dev/null @@ -1,9 +0,0 @@ -## API Report File for "@backstage/techdocs-common" - -> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). - -```ts -export * from '@backstage/plugin-techdocs-node'; - -// (No @packageDocumentation comment for this package) -``` diff --git a/packages/techdocs-common/package.json b/packages/techdocs-common/package.json deleted file mode 100644 index fbc7052f9c..0000000000 --- a/packages/techdocs-common/package.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "name": "@backstage/techdocs-common", - "description": "No longer maintained. Use @backstage/plugin-techdocs-node instead.", - "version": "0.11.15", - "main": "src/index.ts", - "types": "src/index.ts", - "private": false, - "publishConfig": { - "access": "public", - "main": "dist/index.cjs.js", - "types": "dist/index.d.ts" - }, - "backstage": { - "role": "node-library" - }, - "homepage": "https://backstage.io", - "repository": { - "type": "git", - "url": "https://github.com/backstage/backstage", - "directory": "packages/techdocs-common" - }, - "license": "Apache-2.0", - "files": [ - "dist" - ], - "scripts": { - "build": "backstage-cli package build", - "lint": "backstage-cli package lint", - "test": "backstage-cli package test", - "prepack": "backstage-cli package prepack", - "postpack": "backstage-cli package postpack", - "clean": "backstage-cli package clean", - "start": "backstage-cli package start" - }, - "bugs": { - "url": "https://github.com/backstage/backstage/issues" - }, - "dependencies": { - "@backstage/plugin-techdocs-node": "1.1.1" - }, - "devDependencies": {}, - "jest": { - "roots": [ - ".." - ] - } -} diff --git a/packages/test-utils/CHANGELOG.md b/packages/test-utils/CHANGELOG.md index 1fd8aa82c7..d8e9a8dd11 100644 --- a/packages/test-utils/CHANGELOG.md +++ b/packages/test-utils/CHANGELOG.md @@ -1,5 +1,27 @@ # @backstage/test-utils +## 1.1.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-app-api@1.0.3 + - @backstage/core-plugin-api@1.0.3 + - @backstage/plugin-permission-common@0.6.2 + - @backstage/plugin-permission-react@0.4.2 + +## 1.1.1-next.0 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-app-api@1.0.3-next.0 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/plugin-permission-common@0.6.2-next.0 + - @backstage/plugin-permission-react@0.4.2-next.0 + ## 1.1.0 ### Minor Changes diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index 870882463f..0c52f47325 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.1.0", + "version": "1.1.1", "private": false, "publishConfig": { "access": "public", @@ -34,10 +34,10 @@ }, "dependencies": { "@backstage/config": "^1.0.1", - "@backstage/core-app-api": "^1.0.2", - "@backstage/core-plugin-api": "^1.0.2", - "@backstage/plugin-permission-common": "^0.6.1", - "@backstage/plugin-permission-react": "^0.4.1", + "@backstage/core-app-api": "^1.0.3", + "@backstage/core-plugin-api": "^1.0.3", + "@backstage/plugin-permission-common": "^0.6.2", + "@backstage/plugin-permission-react": "^0.4.2", "@backstage/theme": "^0.2.15", "@backstage/types": "^1.0.0", "@material-ui/core": "^4.12.2", @@ -55,10 +55,10 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", + "@backstage/cli": "^0.17.2", "@types/jest": "^26.0.7", "@types/node": "^16.11.26", - "msw": "^0.35.0" + "msw": "^0.42.0" }, "files": [ "dist" diff --git a/packages/theme/package.json b/packages/theme/package.json index 537b6bab96..d10999fefb 100644 --- a/packages/theme/package.json +++ b/packages/theme/package.json @@ -36,7 +36,7 @@ "@material-ui/core": "^4.12.2" }, "devDependencies": { - "@backstage/cli": "^0.17.1-next.0" + "@backstage/cli": "^0.17.2-next.0" }, "files": [ "dist" diff --git a/packages/types/package.json b/packages/types/package.json index 112247fefe..5ea10709ec 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -34,7 +34,7 @@ }, "dependencies": {}, "devDependencies": { - "@backstage/cli": "^0.17.1-next.0", + "@backstage/cli": "^0.17.2-next.0", "@types/zen-observable": "^0.8.0", "zen-observable": "^0.8.15" }, diff --git a/packages/version-bridge/package.json b/packages/version-bridge/package.json index c7fdf46951..81ec8017a1 100644 --- a/packages/version-bridge/package.json +++ b/packages/version-bridge/package.json @@ -37,7 +37,7 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1-next.0", + "@backstage/cli": "^0.17.2-next.0", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/react-hooks": "^8.0.0" diff --git a/plugins/adr-backend/CHANGELOG.md b/plugins/adr-backend/CHANGELOG.md index 20b2cf507c..946df67282 100644 --- a/plugins/adr-backend/CHANGELOG.md +++ b/plugins/adr-backend/CHANGELOG.md @@ -1,5 +1,49 @@ # @backstage/plugin-adr-backend +## 0.1.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-search-common@0.3.5 + - @backstage/backend-common@0.14.0 + - @backstage/integration@1.2.1 + - @backstage/catalog-client@1.0.3 + - @backstage/catalog-model@1.0.3 + - @backstage/plugin-adr-common@0.1.1 + +## 0.1.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-common@0.3.5-next.1 + - @backstage/backend-common@0.14.0-next.2 + - @backstage/integration@1.2.1-next.2 + +## 0.1.1-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/backend-common@0.13.6-next.1 + - @backstage/catalog-client@1.0.3-next.0 + - @backstage/integration@1.2.1-next.1 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-search-common@0.3.5-next.0 + - @backstage/plugin-adr-common@0.1.1-next.1 + +## 0.1.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + - @backstage/integration@1.2.1-next.0 + - @backstage/plugin-adr-common@0.1.1-next.0 + ## 0.1.0 ### Minor Changes diff --git a/plugins/adr-backend/package.json b/plugins/adr-backend/package.json index bc7526754e..c192e0cd85 100644 --- a/plugins/adr-backend/package.json +++ b/plugins/adr-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-adr-backend", - "version": "0.1.0", + "version": "0.1.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -29,14 +29,14 @@ "postpack": "backstage-cli package postpack" }, "dependencies": { - "@backstage/backend-common": "^0.13.3", - "@backstage/catalog-client": "^1.0.2", - "@backstage/catalog-model": "^1.0.2", + "@backstage/backend-common": "^0.14.0", + "@backstage/catalog-client": "^1.0.3", + "@backstage/catalog-model": "^1.0.3", "@backstage/config": "^1.0.1", "@backstage/errors": "^1.0.0", - "@backstage/integration": "^1.2.0", - "@backstage/plugin-adr-common": "^0.1.0", - "@backstage/plugin-search-common": "^0.3.4", + "@backstage/integration": "^1.2.1", + "@backstage/plugin-adr-common": "^0.1.1", + "@backstage/plugin-search-common": "^0.3.5", "luxon": "^2.0.2", "marked": "^4.0.14", "winston": "^3.2.1", @@ -44,11 +44,11 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", + "@backstage/cli": "^0.17.2", "@types/marked": "^4.0.0", "@types/supertest": "^2.0.8", "supertest": "^6.1.3", - "msw": "^0.35.0" + "msw": "^0.42.0" }, "files": [ "dist" diff --git a/plugins/adr-common/CHANGELOG.md b/plugins/adr-common/CHANGELOG.md index 506d2b3d3d..d9a0207248 100644 --- a/plugins/adr-common/CHANGELOG.md +++ b/plugins/adr-common/CHANGELOG.md @@ -1,5 +1,30 @@ # @backstage/plugin-adr-common +## 0.1.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-common@0.3.5 + - @backstage/integration@1.2.1 + - @backstage/catalog-model@1.0.3 + +## 0.1.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.2.1-next.1 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-search-common@0.3.5-next.0 + +## 0.1.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.2.1-next.0 + ## 0.1.0 ### Minor Changes diff --git a/plugins/adr-common/package.json b/plugins/adr-common/package.json index 69774b4f9a..d3607f3a99 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.1.0", + "version": "0.1.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -30,12 +30,12 @@ "postpack": "backstage-cli package postpack" }, "dependencies": { - "@backstage/catalog-model": "^1.0.2", - "@backstage/integration": "^1.2.0", - "@backstage/plugin-search-common": "^0.3.4" + "@backstage/catalog-model": "^1.0.3", + "@backstage/integration": "^1.2.1", + "@backstage/plugin-search-common": "^0.3.5" }, "devDependencies": { - "@backstage/cli": "^0.17.1" + "@backstage/cli": "^0.17.2" }, "files": [ "dist" diff --git a/plugins/adr/CHANGELOG.md b/plugins/adr/CHANGELOG.md index 735f333b2f..61e28f3834 100644 --- a/plugins/adr/CHANGELOG.md +++ b/plugins/adr/CHANGELOG.md @@ -1,5 +1,45 @@ # @backstage/plugin-adr +## 0.1.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- a6458a120b: Adding term highlighting support to `AdrSearchResultListItem` +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/plugin-search-common@0.3.5 + - @backstage/plugin-search-react@0.2.1 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/integration-react@1.1.1 + - @backstage/plugin-adr-common@0.1.1 + +## 0.1.1-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/integration-react@1.1.1-next.1 + - @backstage/plugin-catalog-react@1.1.1-next.1 + - @backstage/plugin-search-react@0.2.1-next.0 + - @backstage/plugin-search-common@0.3.5-next.0 + - @backstage/plugin-adr-common@0.1.1-next.1 + +## 0.1.1-next.0 + +### Patch Changes + +- a6458a120b: Adding term highlighting support to `AdrSearchResultListItem` +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + - @backstage/integration-react@1.1.1-next.0 + - @backstage/plugin-adr-common@0.1.1-next.0 + ## 0.1.0 ### Minor Changes diff --git a/plugins/adr/api-report.md b/plugins/adr/api-report.md index 61cf231784..fbe04b1dc4 100644 --- a/plugins/adr/api-report.md +++ b/plugins/adr/api-report.md @@ -47,10 +47,12 @@ export const AdrReader: { export const AdrSearchResultListItem: ({ lineClamp, highlight, + rank, result, }: { lineClamp?: number | undefined; highlight?: ResultHighlight | undefined; + rank?: number | undefined; result: AdrDocument; }) => JSX.Element; diff --git a/plugins/adr/package.json b/plugins/adr/package.json index 2274bdebd4..194cc44ae4 100644 --- a/plugins/adr/package.json +++ b/plugins/adr/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-adr", - "version": "0.1.0", + "version": "0.1.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -22,13 +22,13 @@ "postpack": "backstage-cli package postpack" }, "dependencies": { - "@backstage/core-components": "^0.9.4", - "@backstage/core-plugin-api": "^1.0.2", - "@backstage/integration-react": "^1.1.0", - "@backstage/plugin-adr-common": "^0.1.0", - "@backstage/plugin-catalog-react": "^1.1.0", - "@backstage/plugin-search-common": "^0.3.4", - "@backstage/plugin-search-react": "^0.2.0", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", + "@backstage/integration-react": "^1.1.1", + "@backstage/plugin-adr-common": "^0.1.1", + "@backstage/plugin-catalog-react": "^1.1.1", + "@backstage/plugin-search-common": "^0.3.5", + "@backstage/plugin-search-react": "^0.2.1", "@backstage/theme": "^0.2.15", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -44,10 +44,10 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/core-app-api": "^1.0.2", - "@backstage/dev-utils": "^1.0.2", - "@backstage/test-utils": "^1.1.0", + "@backstage/cli": "^0.17.2", + "@backstage/core-app-api": "^1.0.3", + "@backstage/dev-utils": "^1.0.3", + "@backstage/test-utils": "^1.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", @@ -55,7 +55,7 @@ "@types/jest": "*", "@types/node": "*", "cross-fetch": "^3.1.5", - "msw": "^0.35.0" + "msw": "^0.42.0" }, "files": [ "dist" diff --git a/plugins/adr/src/search/AdrSearchResultListItem.tsx b/plugins/adr/src/search/AdrSearchResultListItem.tsx index 484bac336e..677e39fc11 100644 --- a/plugins/adr/src/search/AdrSearchResultListItem.tsx +++ b/plugins/adr/src/search/AdrSearchResultListItem.tsx @@ -24,6 +24,7 @@ import { makeStyles, } from '@material-ui/core'; import { Link } from '@backstage/core-components'; +import { useAnalytics } from '@backstage/core-plugin-api'; import { AdrDocument } from '@backstage/plugin-adr-common'; import { ResultHighlight } from '@backstage/plugin-search-common'; import { HighlightedSearchResultText } from '@backstage/plugin-search-react'; @@ -46,16 +47,26 @@ const useStyles = makeStyles({ export const AdrSearchResultListItem = ({ lineClamp = 5, highlight, + rank, result, }: { lineClamp?: number; highlight?: ResultHighlight; + rank?: number; result: AdrDocument; }) => { const classes = useStyles(); + const analytics = useAnalytics(); + + const handleClick = () => { + analytics.captureEvent('discover', result.title, { + attributes: { to: result.location }, + value: rank, + }); + }; return ( - + { + // load the default widgets + const definitionWidgets = defaultDefinitionWidgets(); + // add the grpc-docs api widget to the definition widgets + definitionWidgets.push(grpcDocsApiWidget); + return { + getApiDefinitionWidget: (apiEntity: ApiEntity) => { + // find the widget for the type of api entity + return definitionWidgets.find(d => d.type === apiEntity.spec.type); + }, + }; + }, + }), +]; +``` + +### Set the type in your api entities + +This widget will render the generated `protoc-gen-doc` descriptors with the `grpc-docs` package. For this make sure you use the `grpc-docs` as the type in the API catalog file. + +```yaml +apiVersion: backstage.io/v1alpha1 +kind: API +metadata: + name: hello-world + description: Hello World example for gRPC +spec: + type: grpc-docs + lifecycle: deprecated + owner: foo + definition: + $text: https://fetch-my-json.com/awesome-resource.json +``` diff --git a/plugins/api-docs-module-protoc-gen-doc/api-report.md b/plugins/api-docs-module-protoc-gen-doc/api-report.md new file mode 100644 index 0000000000..696fee2a20 --- /dev/null +++ b/plugins/api-docs-module-protoc-gen-doc/api-report.md @@ -0,0 +1,14 @@ +## API Report File for "@backstage/plugin-api-docs-module-protoc-gen-doc" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +/// + +// @public (undocumented) +export const grpcDocsApiWidget: { + type: string; + title: string; + component: (definition: string) => JSX.Element; +}; +``` diff --git a/plugins/api-docs-module-protoc-gen-doc/package.json b/plugins/api-docs-module-protoc-gen-doc/package.json new file mode 100644 index 0000000000..81e7b7ea45 --- /dev/null +++ b/plugins/api-docs-module-protoc-gen-doc/package.json @@ -0,0 +1,47 @@ +{ + "name": "@backstage/plugin-api-docs-module-protoc-gen-doc", + "description": "Additional functionalities for the api-docs plugin that renders the output of the protoc-gen-doc", + "version": "0.0.0", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "publishConfig": { + "access": "public", + "main": "dist/index.esm.js", + "types": "dist/index.d.ts" + }, + "backstage": { + "role": "frontend-plugin-module" + }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/api-docs-module-protoc-gen-doc" + }, + "keywords": [ + "backstage" + ], + "scripts": { + "build": "backstage-cli package build", + "lint": "backstage-cli package lint", + "test": "backstage-cli package test", + "clean": "backstage-cli package clean", + "prepack": "backstage-cli package prepack", + "postpack": "backstage-cli package postpack" + }, + "dependencies": { + "grpc-docs": "^1.1.2" + }, + "peerDependencies": { + "react": "^16.13.1 || ^17.0.0" + }, + "devDependencies": { + "@backstage/cli": "^0.17.2-next.0", + "@testing-library/jest-dom": "^5.16.4", + "@types/react": "^16.13.1 || ^17.0.0" + }, + "files": [ + "dist" + ] +} diff --git a/plugins/api-docs-module-protoc-gen-doc/src/index.ts b/plugins/api-docs-module-protoc-gen-doc/src/index.ts new file mode 100644 index 0000000000..8cca60ac60 --- /dev/null +++ b/plugins/api-docs-module-protoc-gen-doc/src/index.ts @@ -0,0 +1,23 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Package encapsulating utilities to be shared by frontend TechDocs plugins. + * + * @packageDocumentation + */ + +export { grpcDocsApiWidget } from './widgets'; diff --git a/plugins/search/src/components/SearchResult/index.tsx b/plugins/api-docs-module-protoc-gen-doc/src/setupTests.ts similarity index 86% rename from plugins/search/src/components/SearchResult/index.tsx rename to plugins/api-docs-module-protoc-gen-doc/src/setupTests.ts index e7d088f286..7a459ed24e 100644 --- a/plugins/search/src/components/SearchResult/index.tsx +++ b/plugins/api-docs-module-protoc-gen-doc/src/setupTests.ts @@ -1,5 +1,5 @@ /* - * Copyright 2021 The Backstage Authors + * Copyright 2022 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,4 +14,4 @@ * limitations under the License. */ -export { SearchResult } from './SearchResult'; +import '@testing-library/jest-dom'; diff --git a/plugins/api-docs-module-protoc-gen-doc/src/widgets.tsx b/plugins/api-docs-module-protoc-gen-doc/src/widgets.tsx new file mode 100644 index 0000000000..5f35c8d9b3 --- /dev/null +++ b/plugins/api-docs-module-protoc-gen-doc/src/widgets.tsx @@ -0,0 +1,29 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import { GRPCSelfGeneratedAPIReference } from 'grpc-docs'; + +/** + * @public + */ +export const grpcDocsApiWidget = { + type: 'grpc-docs', + title: 'gRPC', + component: (definition: string) => ( + + ), +}; diff --git a/plugins/api-docs/CHANGELOG.md b/plugins/api-docs/CHANGELOG.md index 053c7d2421..2d61c19e62 100644 --- a/plugins/api-docs/CHANGELOG.md +++ b/plugins/api-docs/CHANGELOG.md @@ -1,5 +1,40 @@ # @backstage/plugin-api-docs +## 0.8.6 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- 811ff4bcf4: Updated `swagger-ui-react` to 4.11.1 in order to address a [XSS + vulnerability](https://github.com/advisories/GHSA-hqq7-2q2v-82xq) in `@braintree/sanitize-url` +- Updated dependencies + - @backstage/plugin-catalog@1.3.0 + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## 0.8.6-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog@1.2.1-next.1 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## 0.8.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/plugin-catalog@1.2.1-next.0 + - @backstage/core-components@0.9.5-next.0 + ## 0.8.5 ### Patch Changes diff --git a/plugins/api-docs/package.json b/plugins/api-docs/package.json index 5ac5949179..9cd1e3aef0 100644 --- a/plugins/api-docs/package.json +++ b/plugins/api-docs/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-api-docs", "description": "A Backstage plugin that helps represent API entities in the frontend", - "version": "0.8.5", + "version": "0.8.6", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -34,33 +34,33 @@ }, "dependencies": { "@asyncapi/react-component": "1.0.0-next.38", - "@backstage/catalog-model": "^1.0.2", - "@backstage/core-components": "^0.9.4", - "@backstage/core-plugin-api": "^1.0.2", - "@backstage/plugin-catalog": "^1.2.0", - "@backstage/plugin-catalog-react": "^1.1.0", + "@backstage/catalog-model": "^1.0.3", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", + "@backstage/plugin-catalog": "^1.3.0", + "@backstage/plugin-catalog-react": "^1.1.1", "@backstage/theme": "^0.2.15", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", "graphiql": "^1.8.8", - "graphql-ws": "^5.4.1", "graphql": "^16.0.0", + "graphql-ws": "^5.4.1", "isomorphic-form-data": "^2.0.0", "react-router": "6.0.0-beta.0", "react-router-dom": "6.0.0-beta.0", "react-use": "^17.2.4", - "swagger-ui-react": "^4.1.3" + "swagger-ui-react": "^4.11.1" }, "peerDependencies": { "@types/react": "^16.13.1 || ^17.0.0", "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/core-app-api": "^1.0.2", - "@backstage/dev-utils": "^1.0.2", - "@backstage/test-utils": "^1.1.0", + "@backstage/cli": "^0.17.2", + "@backstage/core-app-api": "^1.0.3", + "@backstage/dev-utils": "^1.0.3", + "@backstage/test-utils": "^1.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", @@ -68,7 +68,7 @@ "@types/node": "^16.11.26", "@types/swagger-ui-react": "^4.1.1", "cross-fetch": "^3.1.5", - "msw": "^0.35.0" + "msw": "^0.42.0" }, "files": [ "dist" diff --git a/plugins/app-backend/CHANGELOG.md b/plugins/app-backend/CHANGELOG.md index 464ad88629..6e86ece20d 100644 --- a/plugins/app-backend/CHANGELOG.md +++ b/plugins/app-backend/CHANGELOG.md @@ -1,5 +1,37 @@ # @backstage/plugin-app-backend +## 0.3.33 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/backend-common@0.14.0 + - @backstage/config-loader@1.1.2 + +## 0.3.33-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0-next.2 + +## 0.3.33-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/backend-common@0.13.6-next.1 + - @backstage/config-loader@1.1.2-next.0 + +## 0.3.33-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + ## 0.3.32 ### Patch Changes diff --git a/plugins/app-backend/package.json b/plugins/app-backend/package.json index 3d98c1ba5a..5efb839730 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.32", + "version": "0.3.33", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -33,8 +33,8 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/backend-common": "^0.13.3", - "@backstage/config-loader": "^1.1.1", + "@backstage/backend-common": "^0.14.0", + "@backstage/config-loader": "^1.1.2", "@backstage/config": "^1.0.1", "@backstage/types": "^1.0.0", "@types/express": "^4.17.6", @@ -50,12 +50,12 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/backend-test-utils": "^0.1.24", - "@backstage/cli": "^0.17.1", + "@backstage/backend-test-utils": "^0.1.25", + "@backstage/cli": "^0.17.2", "@backstage/types": "^1.0.0", "@types/supertest": "^2.0.8", "mock-fs": "^5.1.0", - "msw": "^0.35.0", + "msw": "^0.42.0", "supertest": "^6.1.3" }, "files": [ diff --git a/plugins/auth-backend/CHANGELOG.md b/plugins/auth-backend/CHANGELOG.md index 6c1ea681a1..0f69289de5 100644 --- a/plugins/auth-backend/CHANGELOG.md +++ b/plugins/auth-backend/CHANGELOG.md @@ -1,5 +1,51 @@ # @backstage/plugin-auth-backend +## 0.14.1 + +### Patch Changes + +- 5e055079f0: Increased key field size for signing_keys table to account for larger signature keys +- f6aae90e4e: Added configurable algorithm field for TokenFactory +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- bc6fb57094: Updated dependency `passport` to `^0.6.0`. +- 467facc6ea: Fix improper binding of 'this' in ALB Auth provider +- Updated dependencies + - @backstage/backend-common@0.14.0 + - @backstage/plugin-auth-node@0.2.2 + - @backstage/catalog-client@1.0.3 + - @backstage/catalog-model@1.0.3 + +## 0.14.1-next.2 + +### Patch Changes + +- bc6fb57094: Updated dependency `passport` to `^0.6.0`. +- Updated dependencies + - @backstage/backend-common@0.14.0-next.2 + - @backstage/plugin-auth-node@0.2.2-next.2 + +## 0.14.1-next.1 + +### Patch Changes + +- 5e055079f0: Increased key field size for signing_keys table to account for larger signature keys +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- 467facc6ea: Fix improper binding of 'this' in ALB Auth provider +- Updated dependencies + - @backstage/backend-common@0.13.6-next.1 + - @backstage/catalog-client@1.0.3-next.0 + - @backstage/plugin-auth-node@0.2.2-next.1 + - @backstage/catalog-model@1.0.3-next.0 + +## 0.14.1-next.0 + +### Patch Changes + +- f6aae90e4e: Added configurable algorithm field for TokenFactory +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + - @backstage/plugin-auth-node@0.2.2-next.0 + ## 0.14.0 ### Minor Changes diff --git a/plugins/auth-backend/migrations/20220522100910_key_field_size.js b/plugins/auth-backend/migrations/20220522100910_key_field_size.js new file mode 100644 index 0000000000..c27d038223 --- /dev/null +++ b/plugins/auth-backend/migrations/20220522100910_key_field_size.js @@ -0,0 +1,49 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// @ts-check + +/** + * @param {import('knex').Knex} knex + */ +exports.up = async function up(knex) { + // Sqlite does not support alter column. + if (!knex.client.config.client.includes('sqlite3')) { + await knex.schema.alterTable('signing_keys', table => { + table + .text('key') + .notNullable() + .comment('The serialized signing key') + .alter({ alterType: true }); + }); + } +}; + +/** + * @param {import('knex').Knex} knex + */ +exports.down = async function down(knex) { + // Sqlite does not support alter column. + if (!knex.client.config.client.includes('sqlite3')) { + await knex.schema.alterTable('signing_keys', table => { + table + .string('key') + .notNullable() + .comment('The serialized signing key') + .alter({ alterType: true }); + }); + } +}; diff --git a/plugins/auth-backend/package.json b/plugins/auth-backend/package.json index fbb5729843..d0a4438afa 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.14.0", + "version": "0.14.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -33,10 +33,10 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/plugin-auth-node": "^0.2.1", - "@backstage/backend-common": "^0.13.3", - "@backstage/catalog-client": "^1.0.2", - "@backstage/catalog-model": "^1.0.2", + "@backstage/plugin-auth-node": "^0.2.2", + "@backstage/backend-common": "^0.14.0", + "@backstage/catalog-client": "^1.0.3", + "@backstage/catalog-model": "^1.0.3", "@backstage/config": "^1.0.1", "@backstage/errors": "^1.0.0", "@backstage/types": "^1.0.0", @@ -50,7 +50,7 @@ "express-promise-router": "^4.1.0", "express-session": "^1.17.1", "fs-extra": "10.1.0", - "google-auth-library": "^7.6.1", + "google-auth-library": "^8.0.0", "jose": "^4.6.0", "jwt-decode": "^3.1.0", "knex": "^1.0.2", @@ -61,7 +61,7 @@ "node-fetch": "^2.6.7", "node-cache": "^5.1.2", "openid-client": "^5.1.3", - "passport": "^0.5.2", + "passport": "^0.6.0", "passport-bitbucket-oauth2": "^0.1.2", "passport-github2": "^0.1.12", "passport-gitlab2": "^5.0.0", @@ -76,8 +76,8 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/backend-test-utils": "^0.1.24", - "@backstage/cli": "^0.17.1", + "@backstage/backend-test-utils": "^0.1.25", + "@backstage/cli": "^0.17.2", "@types/body-parser": "^1.19.0", "@types/cookie-parser": "^1.4.2", "@types/express-session": "^1.17.2", @@ -88,7 +88,7 @@ "@types/passport-saml": "^1.1.3", "@types/passport-strategy": "^0.2.35", "@types/xml2js": "^0.4.7", - "msw": "^0.35.0", + "msw": "^0.42.0", "supertest": "^6.1.3" }, "files": [ diff --git a/plugins/auth-backend/src/identity/TokenFactory.ts b/plugins/auth-backend/src/identity/TokenFactory.ts index fe9d2a1b4f..44cfebfb71 100644 --- a/plugins/auth-backend/src/identity/TokenFactory.ts +++ b/plugins/auth-backend/src/identity/TokenFactory.ts @@ -34,6 +34,9 @@ type Options = { keyDurationSeconds: number; /** JWS "alg" (Algorithm) Header Parameter value. Defaults to ES256. * Must match one of the algorithms defined for IdentityClient. + * When setting a different algorithm, check if the `key` field + * of the `signing_keys` table can fit the length of the generated keys. + * If not, add a knex migration file in the migrations folder. * More info on supported algorithms: https://github.com/panva/jose */ algorithm?: string; }; diff --git a/plugins/auth-backend/src/providers/aws-alb/provider.ts b/plugins/auth-backend/src/providers/aws-alb/provider.ts index 78bfb9f0ec..b8d43a11e6 100644 --- a/plugins/auth-backend/src/providers/aws-alb/provider.ts +++ b/plugins/auth-backend/src/providers/aws-alb/provider.ts @@ -189,7 +189,7 @@ export class AwsAlbAuthProvider implements AuthProviderRouteHandlers { }; } - async getKey(header: JWTHeaderParameters): Promise { + getKey = async (header: JWTHeaderParameters): Promise => { if (!header.kid) { throw new AuthenticationError('No key id was specified in header'); } @@ -208,7 +208,7 @@ export class AwsAlbAuthProvider implements AuthProviderRouteHandlers { keyValue.export({ format: 'pem', type: 'spki' }), ); return keyValue; - } + }; } /** diff --git a/plugins/auth-node/CHANGELOG.md b/plugins/auth-node/CHANGELOG.md index 34deb4db30..4381303915 100644 --- a/plugins/auth-node/CHANGELOG.md +++ b/plugins/auth-node/CHANGELOG.md @@ -1,5 +1,38 @@ # @backstage/plugin-auth-node +## 0.2.2 + +### Patch Changes + +- 5ca0b86b88: Address corner cases where the key store was not being created at startup +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- 9079a78078: Added configurable algorithms array for IdentityClient +- Updated dependencies + - @backstage/backend-common@0.14.0 + +## 0.2.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0-next.2 + +## 0.2.2-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/backend-common@0.13.6-next.1 + +## 0.2.2-next.0 + +### Patch Changes + +- 9079a78078: Added configurable algorithms array for IdentityClient +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + ## 0.2.1 ### Patch Changes diff --git a/plugins/auth-node/package.json b/plugins/auth-node/package.json index 301843239b..2281d2d35e 100644 --- a/plugins/auth-node/package.json +++ b/plugins/auth-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-node", - "version": "0.2.1", + "version": "0.2.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -23,7 +23,7 @@ "start": "backstage-cli package start" }, "dependencies": { - "@backstage/backend-common": "^0.13.3", + "@backstage/backend-common": "^0.14.0", "@backstage/config": "^1.0.1", "@backstage/errors": "^1.0.0", "jose": "^4.6.0", @@ -31,9 +31,9 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/cli": "^0.17.1", + "@backstage/cli": "^0.17.2", "lodash": "^4.17.21", - "msw": "^0.35.0", + "msw": "^0.42.0", "uuid": "^8.0.0" }, "files": [ diff --git a/plugins/auth-node/src/IdentityClient.ts b/plugins/auth-node/src/IdentityClient.ts index 3799222447..0773d22c26 100644 --- a/plugins/auth-node/src/IdentityClient.ts +++ b/plugins/auth-node/src/IdentityClient.ts @@ -141,7 +141,7 @@ export class IdentityClient { // Add a small margin in case clocks are out of sync const issuedAfterLastRefresh = payload?.iat && payload.iat > this.keyStoreUpdated - CLOCK_MARGIN_S; - if (!keyStoreHasKey && issuedAfterLastRefresh) { + if (!this.keyStore || (!keyStoreHasKey && issuedAfterLastRefresh)) { const url = await this.discovery.getBaseUrl('auth'); const endpoint = new URL(`${url}/.well-known/jwks.json`); this.keyStore = createRemoteJWKSet(endpoint); diff --git a/plugins/azure-devops-backend/CHANGELOG.md b/plugins/azure-devops-backend/CHANGELOG.md index 2755a273a5..f9d414bef9 100644 --- a/plugins/azure-devops-backend/CHANGELOG.md +++ b/plugins/azure-devops-backend/CHANGELOG.md @@ -1,5 +1,35 @@ # @backstage/plugin-azure-devops-backend +## 0.3.12 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/backend-common@0.14.0 + +## 0.3.12-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0-next.2 + +## 0.3.12-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/backend-common@0.13.6-next.1 + +## 0.3.12-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + ## 0.3.11 ### Patch Changes diff --git a/plugins/azure-devops-backend/README.md b/plugins/azure-devops-backend/README.md index e71021471b..5dc0570bd2 100644 --- a/plugins/azure-devops-backend/README.md +++ b/plugins/azure-devops-backend/README.md @@ -60,7 +60,10 @@ Here's how to get the backend up and running: // ... async function main() { // ... + // Add this line under the other lines that follow the useHotMemoize pattern const azureDevOpsEnv = useHotMemoize(module, () => createEnv('azure-devops')); + // ... + // Insert this line under the other lines that add their routers to apiRouter in the same way apiRouter.use('/azure-devops', await azureDevOps(azureDevOpsEnv)); ``` diff --git a/plugins/azure-devops-backend/package.json b/plugins/azure-devops-backend/package.json index 852e9bf03a..e85ba907c6 100644 --- a/plugins/azure-devops-backend/package.json +++ b/plugins/azure-devops-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-azure-devops-backend", - "version": "0.3.11", + "version": "0.3.12", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -23,7 +23,7 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/backend-common": "^0.13.3", + "@backstage/backend-common": "^0.14.0", "@backstage/config": "^1.0.1", "@backstage/plugin-azure-devops-common": "^0.2.3", "@types/express": "^4.17.6", @@ -35,10 +35,10 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", + "@backstage/cli": "^0.17.2", "@types/supertest": "^2.0.8", "supertest": "^6.1.6", - "msw": "^0.35.0" + "msw": "^0.42.0" }, "files": [ "dist", diff --git a/plugins/azure-devops-common/package.json b/plugins/azure-devops-common/package.json index 50fe55aa34..0f52082db6 100644 --- a/plugins/azure-devops-common/package.json +++ b/plugins/azure-devops-common/package.json @@ -32,7 +32,7 @@ "clean": "backstage-cli package clean" }, "devDependencies": { - "@backstage/cli": "^0.17.1" + "@backstage/cli": "^0.17.2-next.0" }, "files": [ "dist" diff --git a/plugins/azure-devops/CHANGELOG.md b/plugins/azure-devops/CHANGELOG.md index 3c5466611e..8de9bd00a8 100644 --- a/plugins/azure-devops/CHANGELOG.md +++ b/plugins/azure-devops/CHANGELOG.md @@ -1,5 +1,35 @@ # @backstage/plugin-azure-devops +## 0.1.22 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## 0.1.22-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## 0.1.22-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + ## 0.1.21 ### Patch Changes diff --git a/plugins/azure-devops/api-report.md b/plugins/azure-devops/api-report.md index 9760340771..b366166d95 100644 --- a/plugins/azure-devops/api-report.md +++ b/plugins/azure-devops/api-report.md @@ -5,10 +5,21 @@ ```ts /// +import { ApiRef } from '@backstage/core-plugin-api'; import { BackstagePlugin } from '@backstage/core-plugin-api'; +import { BuildRun } from '@backstage/plugin-azure-devops-common'; +import { BuildRunOptions } from '@backstage/plugin-azure-devops-common'; import { DashboardPullRequest } from '@backstage/plugin-azure-devops-common'; +import { DiscoveryApi } from '@backstage/core-plugin-api'; import { Entity } from '@backstage/catalog-model'; +import { GitTag } from '@backstage/plugin-azure-devops-common'; +import { IdentityApi } from '@backstage/core-plugin-api'; +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 { RepoBuildOptions } from '@backstage/plugin-azure-devops-common'; import { SvgIconProps } from '@material-ui/core'; +import { Team } from '@backstage/plugin-azure-devops-common'; // Warning: (ae-missing-release-tag) "AllFilter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -55,6 +66,107 @@ export type AssignedToUserFilter = BaseFilter & } ); +// Warning: (ae-missing-release-tag) "AzureDevOpsApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface AzureDevOpsApi { + // (undocumented) + getAllTeams(): Promise; + // (undocumented) + getBuildRuns( + projectName: string, + repoName?: string, + definitionName?: string, + options?: BuildRunOptions, + ): Promise<{ + items: BuildRun[]; + }>; + // (undocumented) + getDashboardPullRequests( + projectName: string, + ): Promise; + // (undocumented) + getGitTags( + projectName: string, + repoName: string, + ): Promise<{ + items: GitTag[]; + }>; + // (undocumented) + getPullRequests( + projectName: string, + repoName: string, + options?: PullRequestOptions, + ): Promise<{ + items: PullRequest[]; + }>; + // (undocumented) + getRepoBuilds( + projectName: string, + repoName: string, + options?: RepoBuildOptions, + ): Promise<{ + items: RepoBuild[]; + }>; + // (undocumented) + getUserTeamIds(userId: string): Promise; +} + +// Warning: (ae-missing-release-tag) "azureDevOpsApiRef" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const azureDevOpsApiRef: ApiRef; + +// Warning: (ae-missing-release-tag) "AzureDevOpsClient" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export class AzureDevOpsClient implements AzureDevOpsApi { + constructor(options: { + discoveryApi: DiscoveryApi; + identityApi: IdentityApi; + }); + // (undocumented) + getAllTeams(): Promise; + // (undocumented) + getBuildRuns( + projectName: string, + repoName?: string, + definitionName?: string, + options?: BuildRunOptions, + ): Promise<{ + items: BuildRun[]; + }>; + // (undocumented) + getDashboardPullRequests( + projectName: string, + ): Promise; + // (undocumented) + getGitTags( + projectName: string, + repoName: string, + ): Promise<{ + items: GitTag[]; + }>; + // (undocumented) + getPullRequests( + projectName: string, + repoName: string, + options?: PullRequestOptions, + ): Promise<{ + items: PullRequest[]; + }>; + // (undocumented) + getRepoBuilds( + projectName: string, + repoName: string, + options?: RepoBuildOptions, + ): Promise<{ + items: RepoBuild[]; + }>; + // (undocumented) + getUserTeamIds(userId: string): Promise; +} + // Warning: (ae-missing-release-tag) "azureDevOpsPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) diff --git a/plugins/azure-devops/package.json b/plugins/azure-devops/package.json index 9ddf73f5e4..8fad3a8697 100644 --- a/plugins/azure-devops/package.json +++ b/plugins/azure-devops/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-azure-devops", - "version": "0.1.21", + "version": "0.1.22", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -30,12 +30,12 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-model": "^1.0.2", - "@backstage/core-components": "^0.9.4", - "@backstage/core-plugin-api": "^1.0.2", + "@backstage/catalog-model": "^1.0.3", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", "@backstage/errors": "^1.0.0", "@backstage/plugin-azure-devops-common": "^0.2.3", - "@backstage/plugin-catalog-react": "^1.1.0", + "@backstage/plugin-catalog-react": "^1.1.1", "@backstage/theme": "^0.2.15", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -49,17 +49,17 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/core-app-api": "^1.0.2", - "@backstage/dev-utils": "^1.0.2", - "@backstage/test-utils": "^1.1.0", + "@backstage/cli": "^0.17.2", + "@backstage/core-app-api": "^1.0.3", + "@backstage/dev-utils": "^1.0.3", + "@backstage/test-utils": "^1.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", "@types/jest": "^26.0.7", "@types/node": "^16.11.26", "cross-fetch": "^3.1.5", - "msw": "^0.35.0" + "msw": "^0.42.0" }, "files": [ "dist" diff --git a/plugins/azure-devops/src/index.ts b/plugins/azure-devops/src/index.ts index 48177ee830..e65c1d02a3 100644 --- a/plugins/azure-devops/src/index.ts +++ b/plugins/azure-devops/src/index.ts @@ -26,6 +26,8 @@ export { export { AzurePullRequestsIcon } from './components/AzurePullRequestsIcon'; +export * from './api'; + export { FilterType } from './components/PullRequestsPage'; export type { PullRequestColumnConfig, diff --git a/plugins/badges-backend/CHANGELOG.md b/plugins/badges-backend/CHANGELOG.md index 0a40812f64..3b80f26ca1 100644 --- a/plugins/badges-backend/CHANGELOG.md +++ b/plugins/badges-backend/CHANGELOG.md @@ -1,5 +1,37 @@ # @backstage/plugin-badges-backend +## 0.1.27 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0 + - @backstage/catalog-client@1.0.3 + - @backstage/catalog-model@1.0.3 + +## 0.1.27-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0-next.2 + +## 0.1.27-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.1 + - @backstage/catalog-client@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + +## 0.1.27-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + ## 0.1.26 ### Patch Changes diff --git a/plugins/badges-backend/package.json b/plugins/badges-backend/package.json index cae6a418fd..d1b5d094d9 100644 --- a/plugins/badges-backend/package.json +++ b/plugins/badges-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-badges-backend", "description": "A Backstage backend plugin that generates README badges for your entities", - "version": "0.1.26", + "version": "0.1.27", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -34,9 +34,9 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/backend-common": "^0.13.3", - "@backstage/catalog-client": "^1.0.2", - "@backstage/catalog-model": "^1.0.2", + "@backstage/backend-common": "^0.14.0", + "@backstage/catalog-client": "^1.0.3", + "@backstage/catalog-model": "^1.0.3", "@backstage/config": "^1.0.1", "@backstage/errors": "^1.0.0", "@types/express": "^4.17.6", @@ -48,7 +48,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", + "@backstage/cli": "^0.17.2", "@types/supertest": "^2.0.8", "supertest": "^6.1.3" }, diff --git a/plugins/badges/CHANGELOG.md b/plugins/badges/CHANGELOG.md index 33a7b76a73..ff7b290b68 100644 --- a/plugins/badges/CHANGELOG.md +++ b/plugins/badges/CHANGELOG.md @@ -1,5 +1,35 @@ # @backstage/plugin-badges +## 0.2.30 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## 0.2.30-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## 0.2.30-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + ## 0.2.29 ### Patch Changes diff --git a/plugins/badges/package.json b/plugins/badges/package.json index 618e6ae7f4..aeade2c50a 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.29", + "version": "0.2.30", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -30,11 +30,11 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-model": "^1.0.2", - "@backstage/core-components": "^0.9.4", - "@backstage/core-plugin-api": "^1.0.2", + "@backstage/catalog-model": "^1.0.3", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", "@backstage/errors": "^1.0.0", - "@backstage/plugin-catalog-react": "^1.1.0", + "@backstage/plugin-catalog-react": "^1.1.1", "@backstage/theme": "^0.2.15", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -46,17 +46,17 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/core-app-api": "^1.0.2", - "@backstage/dev-utils": "^1.0.2", - "@backstage/test-utils": "^1.1.0", + "@backstage/cli": "^0.17.2", + "@backstage/core-app-api": "^1.0.3", + "@backstage/dev-utils": "^1.0.3", + "@backstage/test-utils": "^1.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", "@types/jest": "^26.0.7", "@types/node": "^16.11.26", "cross-fetch": "^3.1.5", - "msw": "^0.35.0" + "msw": "^0.42.0" }, "files": [ "dist" diff --git a/plugins/bazaar-backend/CHANGELOG.md b/plugins/bazaar-backend/CHANGELOG.md index 31fd97df3d..4b0cb04530 100644 --- a/plugins/bazaar-backend/CHANGELOG.md +++ b/plugins/bazaar-backend/CHANGELOG.md @@ -1,5 +1,29 @@ # @backstage/plugin-bazaar-backend +## 0.1.17 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0 + - @backstage/backend-test-utils@0.1.25 + +## 0.1.17-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0-next.2 + - @backstage/backend-test-utils@0.1.25-next.2 + +## 0.1.17-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + - @backstage/backend-test-utils@0.1.25-next.0 + ## 0.1.16 ### Patch Changes diff --git a/plugins/bazaar-backend/package.json b/plugins/bazaar-backend/package.json index 8a940e5045..5f723a9dfb 100644 --- a/plugins/bazaar-backend/package.json +++ b/plugins/bazaar-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-bazaar-backend", - "version": "0.1.16", + "version": "0.1.17", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -23,8 +23,8 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/backend-common": "^0.13.3", - "@backstage/backend-test-utils": "^0.1.24", + "@backstage/backend-common": "^0.14.0", + "@backstage/backend-test-utils": "^0.1.25", "@backstage/config": "^1.0.1", "@types/express": "^4.17.6", "express": "^4.17.1", @@ -34,7 +34,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1" + "@backstage/cli": "^0.17.2" }, "files": [ "dist", diff --git a/plugins/bazaar/CHANGELOG.md b/plugins/bazaar/CHANGELOG.md index 3ae149ecfb..911390ee28 100644 --- a/plugins/bazaar/CHANGELOG.md +++ b/plugins/bazaar/CHANGELOG.md @@ -1,5 +1,41 @@ # @backstage/plugin-bazaar +## 0.1.21 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@1.3.0 + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/cli@0.17.2 + - @backstage/core-components@0.9.5 + - @backstage/catalog-client@1.0.3 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## 0.1.21-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/cli@0.17.2-next.1 + - @backstage/catalog-client@1.0.3-next.0 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog@1.2.1-next.1 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## 0.1.21-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/plugin-catalog@1.2.1-next.0 + - @backstage/cli@0.17.2-next.0 + - @backstage/core-components@0.9.5-next.0 + ## 0.1.20 ### Patch Changes diff --git a/plugins/bazaar/package.json b/plugins/bazaar/package.json index ca26e3c800..cb3318b4e9 100644 --- a/plugins/bazaar/package.json +++ b/plugins/bazaar/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-bazaar", - "version": "0.1.20", + "version": "0.1.21", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -24,13 +24,13 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-client": "^1.0.2", - "@backstage/catalog-model": "^1.0.2", - "@backstage/cli": "^0.17.1", - "@backstage/core-components": "^0.9.4", - "@backstage/core-plugin-api": "^1.0.2", - "@backstage/plugin-catalog": "^1.2.0", - "@backstage/plugin-catalog-react": "^1.1.0", + "@backstage/catalog-client": "^1.0.3", + "@backstage/catalog-model": "^1.0.3", + "@backstage/cli": "^0.17.2", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", + "@backstage/plugin-catalog": "^1.3.0", + "@backstage/plugin-catalog-react": "^1.1.1", "@date-io/luxon": "1.x", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -47,8 +47,8 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/dev-utils": "^1.0.2", + "@backstage/cli": "^0.17.2", + "@backstage/dev-utils": "^1.0.3", "@testing-library/jest-dom": "^5.10.1", "cross-fetch": "^3.1.5" }, diff --git a/packages/techdocs-common/.eslintrc.js b/plugins/bitbucket-cloud-common/.eslintrc.js similarity index 100% rename from packages/techdocs-common/.eslintrc.js rename to plugins/bitbucket-cloud-common/.eslintrc.js diff --git a/plugins/bitbucket-cloud-common/CHANGELOG.md b/plugins/bitbucket-cloud-common/CHANGELOG.md new file mode 100644 index 0000000000..62100f2e0b --- /dev/null +++ b/plugins/bitbucket-cloud-common/CHANGELOG.md @@ -0,0 +1,34 @@ +# @backstage/plugin-bitbucket-cloud-common + +## 0.1.0 + +### Minor Changes + +- 1dffa7dd4d: Add new common library `bitbucket-cloud-common` with a client for Bitbucket Cloud. + + This client can be reused across all packages and might be the future place for additional + features like managing the rate limits, etc. + + The client itself was generated in parts using the `@openapitools/openapi-generator-cli`. + +### Patch Changes + +- 9122060776: Updated dependency `msw` to `^0.42.0`. +- Updated dependencies + - @backstage/integration@1.2.1 + +## 0.1.0-next.0 + +### Minor Changes + +- 1dffa7dd4d: Add new common library `bitbucket-cloud-common` with a client for Bitbucket Cloud. + + This client can be reused across all packages and might be the future place for additional + features like managing the rate limits, etc. + + The client itself was generated in parts using the `@openapitools/openapi-generator-cli`. + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.2.1-next.1 diff --git a/plugins/bitbucket-cloud-common/README.md b/plugins/bitbucket-cloud-common/README.md new file mode 100644 index 0000000000..f4c3bef1e2 --- /dev/null +++ b/plugins/bitbucket-cloud-common/README.md @@ -0,0 +1,64 @@ +# @backstage/plugin-bitbucket-cloud-common + +Welcome to the common package for bitbucket-cloud plugins! + +This common package provides a reusable API client for the Bitbucket Cloud API +which can be reused in catalog-backend-module plugins, scaffolder modules, etc. + +Using a shared client allows to control all traffic going from Backstage to +the Bitbucket Cloud API compared to separate clients or inline API calls. + +We may want to leverage this later to add rate limiting, etc. + +## How to maintain the generated code + +### Update the Models + +This command will + +1. [refresh the schema/OpenAPI Specification](#refresh-the-schema) +2. [re-generate the models](#generate-models) +3. [reduce the models to the minimal needed](#reduce-models) + +### Refresh the schema + +This command will download the latest version of the Bitbucket Cloud OpenAPI Specification +and apply some mutations to fix bugs or improve the schema for a better code generation output. + +```sh +yarn refresh-schema +``` + +### Generate Models + +The models used are created based on the [local OpenAPI Specification file](bitbucket-cloud.oas.json) +using a code generator. +Some post-cleanup is applied to improve the generated output. + +The client itself using the models is not generated. + +```sh +yarn generate-models +``` + +### Reduce Models + +In order to keep the API surface minimal, this command helps to only keep the minimal part of the +generated models by considering all `Models` module members directly or transitively used by the +client implementation. + +```sh +yarn reduce-models +``` + +## Adding a New Client Method + +If you want to add a new method to the client implementation which may use a new endpoint or "new" models +you can + +1. optionally [refresh the schema](#refresh-the-schema) to get the latest version +2. and [generate the models](#generate-models). + +At this point, you have **all** models usable for adding a new method using any of them. + +If you are ready with your addition to the client, you can [reduce the models to the minimal needed](#reduce-models). diff --git a/plugins/bitbucket-cloud-common/api-report.md b/plugins/bitbucket-cloud-common/api-report.md new file mode 100644 index 0000000000..367a47b9e3 --- /dev/null +++ b/plugins/bitbucket-cloud-common/api-report.md @@ -0,0 +1,386 @@ +## API Report File for "@backstage/plugin-bitbucket-cloud-common" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import { BitbucketCloudIntegrationConfig } from '@backstage/integration'; + +// @public (undocumented) +export class BitbucketCloudClient { + // (undocumented) + static fromConfig( + config: BitbucketCloudIntegrationConfig, + ): BitbucketCloudClient; + // (undocumented) + listRepositoriesByWorkspace( + workspace: string, + options?: FilterAndSortOptions & PartialResponseOptions, + ): WithPagination; + // (undocumented) + searchCode( + workspace: string, + query: string, + options?: FilterAndSortOptions & PartialResponseOptions, + ): WithPagination; +} + +// @public (undocumented) +export type FilterAndSortOptions = { + q?: string; + sort?: string; +}; + +// @public (undocumented) +export namespace Models { + export interface Account extends ModelObject { + account_status?: string; + // (undocumented) + created_on?: string; + // (undocumented) + display_name?: string; + // (undocumented) + has_2fa_enabled?: boolean; + // (undocumented) + links?: AccountLinks; + nickname?: string; + // (undocumented) + username?: string; + // (undocumented) + uuid?: string; + // (undocumented) + website?: string; + } + // (undocumented) + export interface AccountLinks { + // (undocumented) + avatar?: Link; + // (undocumented) + followers?: Link; + // (undocumented) + following?: Link; + // (undocumented) + html?: Link; + // (undocumented) + repositories?: Link; + // (undocumented) + self?: Link; + } + export interface Author extends ModelObject { + raw?: string; + // (undocumented) + user?: Account; + } + export interface BaseCommit extends ModelObject { + // (undocumented) + author?: Author; + // (undocumented) + date?: string; + // (undocumented) + hash?: string; + // (undocumented) + message?: string; + // (undocumented) + parents?: Array; + // (undocumented) + summary?: BaseCommitSummary; + } + // (undocumented) + export interface BaseCommitSummary { + html?: string; + markup?: BaseCommitSummaryMarkupEnum; + raw?: string; + } + const BaseCommitSummaryMarkupEnum: { + readonly Markdown: 'markdown'; + readonly Creole: 'creole'; + readonly Plaintext: 'plaintext'; + }; + export type BaseCommitSummaryMarkupEnum = + typeof BaseCommitSummaryMarkupEnum[keyof typeof BaseCommitSummaryMarkupEnum]; + export interface Branch { + default_merge_strategy?: string; + // (undocumented) + links?: RefLinks; + merge_strategies?: Array; + name?: string; + // (undocumented) + target?: Commit; + // (undocumented) + type: string; + } + const BranchMergeStrategiesEnum: { + readonly MergeCommit: 'merge_commit'; + readonly Squash: 'squash'; + readonly FastForward: 'fast_forward'; + }; + export type BranchMergeStrategiesEnum = + typeof BranchMergeStrategiesEnum[keyof typeof BranchMergeStrategiesEnum]; + export interface Commit extends BaseCommit { + // (undocumented) + participants?: Array; + // (undocumented) + repository?: Repository; + } + export interface CommitFile { + // (undocumented) + [key: string]: unknown; + // (undocumented) + attributes?: CommitFileAttributesEnum; + // (undocumented) + commit?: Commit; + escaped_path?: string; + path?: string; + // (undocumented) + type: string; + } + const // (undocumented) + CommitFileAttributesEnum: { + readonly Link: 'link'; + readonly Executable: 'executable'; + readonly Subrepository: 'subrepository'; + readonly Binary: 'binary'; + readonly Lfs: 'lfs'; + }; + // (undocumented) + export type CommitFileAttributesEnum = + typeof CommitFileAttributesEnum[keyof typeof CommitFileAttributesEnum]; + export interface Link { + // (undocumented) + href?: string; + // (undocumented) + name?: string; + } + export interface ModelObject { + // (undocumented) + [key: string]: unknown; + // (undocumented) + type: string; + } + export interface Paginated { + next?: string; + page?: number; + pagelen?: number; + previous?: string; + size?: number; + values?: Array | Set; + } + export interface PaginatedRepositories extends Paginated { + values?: Set; + } + export interface Participant extends ModelObject { + // (undocumented) + approved?: boolean; + participated_on?: string; + // (undocumented) + role?: ParticipantRoleEnum; + // (undocumented) + state?: ParticipantStateEnum; + // (undocumented) + user?: User; + } + const // (undocumented) + ParticipantRoleEnum: { + readonly Participant: 'PARTICIPANT'; + readonly Reviewer: 'REVIEWER'; + }; + // (undocumented) + export type ParticipantRoleEnum = + typeof ParticipantRoleEnum[keyof typeof ParticipantRoleEnum]; + const // (undocumented) + ParticipantStateEnum: { + readonly Approved: 'approved'; + readonly ChangesRequested: 'changes_requested'; + readonly Null: 'null'; + }; + // (undocumented) + export type ParticipantStateEnum = + typeof ParticipantStateEnum[keyof typeof ParticipantStateEnum]; + export interface Project extends ModelObject { + // (undocumented) + created_on?: string; + // (undocumented) + description?: string; + has_publicly_visible_repos?: boolean; + is_private?: boolean; + key?: string; + // (undocumented) + links?: ProjectLinks; + name?: string; + // (undocumented) + owner?: Team; + // (undocumented) + updated_on?: string; + uuid?: string; + } + // (undocumented) + export interface ProjectLinks { + // (undocumented) + avatar?: Link; + // (undocumented) + html?: Link; + } + // (undocumented) + export interface RefLinks { + // (undocumented) + commits?: Link; + // (undocumented) + html?: Link; + // (undocumented) + self?: Link; + } + export interface Repository extends ModelObject { + // (undocumented) + created_on?: string; + // (undocumented) + description?: string; + fork_policy?: RepositoryForkPolicyEnum; + full_name?: string; + // (undocumented) + has_issues?: boolean; + // (undocumented) + has_wiki?: boolean; + // (undocumented) + is_private?: boolean; + // (undocumented) + language?: string; + // (undocumented) + links?: RepositoryLinks; + // (undocumented) + mainbranch?: Branch; + // (undocumented) + name?: string; + // (undocumented) + owner?: Account; + // (undocumented) + parent?: Repository; + // (undocumented) + project?: Project; + // (undocumented) + scm?: RepositoryScmEnum; + // (undocumented) + size?: number; + slug?: string; + // (undocumented) + updated_on?: string; + uuid?: string; + } + const RepositoryForkPolicyEnum: { + readonly AllowForks: 'allow_forks'; + readonly NoPublicForks: 'no_public_forks'; + readonly NoForks: 'no_forks'; + }; + export type RepositoryForkPolicyEnum = + typeof RepositoryForkPolicyEnum[keyof typeof RepositoryForkPolicyEnum]; + const // (undocumented) + RepositoryScmEnum: { + readonly Git: 'git'; + }; + // (undocumented) + export interface RepositoryLinks { + // (undocumented) + avatar?: Link; + // (undocumented) + clone?: Array; + // (undocumented) + commits?: Link; + // (undocumented) + downloads?: Link; + // (undocumented) + forks?: Link; + // (undocumented) + hooks?: Link; + // (undocumented) + html?: Link; + // (undocumented) + pullrequests?: Link; + // (undocumented) + self?: Link; + // (undocumented) + watchers?: Link; + } + // (undocumented) + export type RepositoryScmEnum = + typeof RepositoryScmEnum[keyof typeof RepositoryScmEnum]; + // (undocumented) + export interface SearchCodeSearchResult { + // (undocumented) + readonly content_match_count?: number; + // (undocumented) + readonly content_matches?: Array; + // (undocumented) + file?: CommitFile; + // (undocumented) + readonly path_matches?: Array; + // (undocumented) + readonly type?: string; + } + // (undocumented) + export interface SearchContentMatch { + // (undocumented) + readonly lines?: Array; + } + // (undocumented) + export interface SearchLine { + // (undocumented) + readonly line?: number; + // (undocumented) + readonly segments?: Array; + } + // (undocumented) + export interface SearchResultPage extends Paginated { + // (undocumented) + readonly query_substituted?: boolean; + readonly values?: Array; + } + // (undocumented) + export interface SearchSegment { + // (undocumented) + readonly match?: boolean; + // (undocumented) + readonly text?: string; + } + export interface Team extends Account {} + export interface User extends Account { + account_id?: string; + // (undocumented) + is_staff?: boolean; + } +} + +// @public (undocumented) +export type PaginationOptions = { + page?: number; + pagelen?: number; +}; + +// @public (undocumented) +export type PartialResponseOptions = { + fields?: string; +}; + +// @public (undocumented) +export type RequestOptions = FilterAndSortOptions & + PaginationOptions & + PartialResponseOptions & { + [key: string]: string | number | undefined; + }; + +// @public (undocumented) +export class WithPagination< + TPage extends Models.Paginated, + TResultItem, +> { + constructor( + createUrl: (options: PaginationOptions) => URL, + fetch: (url: URL) => Promise, + ); + // (undocumented) + getPage(options?: PaginationOptions): Promise; + // (undocumented) + iteratePages(options?: PaginationOptions): AsyncGenerator; + // (undocumented) + iterateResults( + options?: PaginationOptions, + ): AsyncGenerator, void, unknown>; +} +``` diff --git a/plugins/bitbucket-cloud-common/bitbucket-cloud.oas.json b/plugins/bitbucket-cloud-common/bitbucket-cloud.oas.json new file mode 100644 index 0000000000..01cd910171 --- /dev/null +++ b/plugins/bitbucket-cloud-common/bitbucket-cloud.oas.json @@ -0,0 +1,25555 @@ +{ + "openapi": "3.0.0", + "info": { + "termsOfService": "https://www.atlassian.com/legal/customer-agreement", + "version": "2.0", + "title": "Bitbucket API", + "description": "Code against the Bitbucket API to automate simple tasks, embed Bitbucket data into your own site, build mobile or desktop apps, or even add custom UI add-ons into Bitbucket itself using the Connect framework.", + "contact": { + "url": "https://support.atlassian.com/bitbucket-cloud/", + "name": "Bitbucket Support", + "email": "support@bitbucket.org" + } + }, + "paths": { + "/addon": { + "delete": { + "responses": { + "204": { + "description": "Request has succeeded. The application has been deleted for the user." + }, + "401": { + "description": "No authorization.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "403": { + "description": "Improper authentication.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Addon"], + "summary": "Delete an app", + "security": [ + { + "oauth2": [] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Deletes the application for the user.\n\nThis endpoint is intended to be used by Bitbucket Connect apps\nand only supports JWT authentication -- that is how Bitbucket\nidentifies the particular installation of the app. Developers\nwith applications registered in the \"Develop Apps\" section\nof Bitbucket Marketplace need not use this endpoint as\nupdates for those applications can be sent out via the\nUI of that section.\n\n```\n$ curl -X DELETE https://api.bitbucket.org/2.0/addon \\\n -H \"Authorization: JWT \"\n```" + }, + "put": { + "responses": { + "204": { + "description": "Request has succeeded. The installation has been updated to the new descriptor." + }, + "400": { + "description": "Scopes have increased or decreased to none.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "401": { + "description": "No authorization.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "403": { + "description": "Improper authentication.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Addon"], + "summary": "Update an installed app", + "security": [ + { + "oauth2": [] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Updates the application installation for the user.\n\nThis endpoint is intended to be used by Bitbucket Connect apps\nand only supports JWT authentication -- that is how Bitbucket\nidentifies the particular installation of the app. Developers\nwith applications registered in the \"Develop Apps\" section\nof Bitbucket need not use this endpoint as updates for those\napplications can be sent out via the UI of that section.\n\nPassing an empty body will update the installation using the\nexisting descriptor URL.\n\n```\n$ curl -X PUT https://api.bitbucket.org/2.0/addon \\\n -H \"Authorization: JWT \" \\\n --header \"Content-Type: application/json\" \\\n --data '{}'\n```\n\nThe new `descriptor` for the installation can be also provided\nin the body directly.\n\n```\n$ curl -X PUT https://api.bitbucket.org/2.0/addon \\\n -H \"Authorization: JWT \" \\\n --header \"Content-Type: application/json\" \\\n --data '{\"descriptor\": $NEW_DESCRIPTOR}'\n```\n\nIn both these modes the URL of the descriptor cannot be changed. To\nchange the descriptor location and upgrade an installation\nthe request must be made exclusively with a `descriptor_url`.\n\n ```\n$ curl -X PUT https://api.bitbucket.org/2.0/addon \\\n -H \"Authorization: JWT \" \\\n --header \"Content-Type: application/json\" \\\n --data '{\"descriptor_url\": $NEW_URL}'\n```\n\nThe `descriptor_url` must exactly match the marketplace registration\nthat Atlassian has for the application. Contact your Atlassian\ndeveloper advocate to update this registration. Once the registration\nhas been updated you may call this resource for each installation.\n\nNote that the scopes of the application cannot be increased\nin the new descriptor nor reduced to none." + }, + "parameters": [] + }, + "/addon/linkers": { + "get": { + "responses": { + "200": { + "description": "Successful." + }, + "401": { + "description": "Authentication must use app JWT", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Addon"], + "summary": "List linkers for an app", + "security": [ + { + "oauth2": [] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Gets a list of all [linkers](/cloud/bitbucket/modules/linker/)\nfor the authenticated application." + }, + "parameters": [] + }, + "/addon/linkers/{linker_key}": { + "get": { + "responses": { + "200": { + "description": "Successful." + }, + "401": { + "description": "Authentication must use app JWT", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "The linker does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Addon"], + "summary": "Get a linker for an app", + "security": [ + { + "oauth2": [] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Gets a [linker](/cloud/bitbucket/modules/linker/) specified by `linker_key`\nfor the authenticated application." + }, + "parameters": [ + { + "name": "linker_key", + "in": "path", + "description": "The unique key of a [linker module](/cloud/bitbucket/modules/linker/)\nas defined in an application descriptor.", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/addon/linkers/{linker_key}/values": { + "delete": { + "responses": { + "204": { + "description": "Successfully deleted the linker values." + }, + "401": { + "description": "Authentication must use app JWT", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "The linker does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Addon"], + "summary": "Delete all linker values", + "security": [ + { + "oauth2": [] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Delete all [linker](/cloud/bitbucket/modules/linker/) values for the\nspecified linker of the authenticated application." + }, + "get": { + "responses": { + "200": { + "description": "Successful." + }, + "401": { + "description": "Authentication must use app JWT", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "The linker does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Addon"], + "summary": "List linker values for a linker", + "security": [ + { + "oauth2": [] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Gets a list of all [linker](/cloud/bitbucket/modules/linker/) values for the\nspecified linker of the authenticated application.\n\nA linker value lets applications supply values to modify its regular expression.\n\nThe base regular expression must use a Bitbucket-specific match group `(?K)`\nwhich will be translated to `([\\w\\-]+)`. A value must match this pattern.\n\n[Read more about linker values](/cloud/bitbucket/modules/linker/#usingthebitbucketapitosupplyvalues)" + }, + "post": { + "responses": { + "201": { + "description": "Successfully created the linker value." + }, + "401": { + "description": "Authentication must use app JWT", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "The linker does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "409": { + "description": "The linker already has the value being added.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Addon"], + "summary": "Create a linker value", + "security": [ + { + "oauth2": [] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Creates a [linker](/cloud/bitbucket/modules/linker/) value for the specified\nlinker of authenticated application.\n\nA linker value lets applications supply values to modify its regular expression.\n\nThe base regular expression must use a Bitbucket-specific match group `(?K)`\nwhich will be translated to `([\\w\\-]+)`. A value must match this pattern.\n\n[Read more about linker values](/cloud/bitbucket/modules/linker/#usingthebitbucketapitosupplyvalues)" + }, + "put": { + "responses": { + "204": { + "description": "Successfully updated the linker values." + }, + "400": { + "description": "Invalid input.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "401": { + "description": "Authentication must use app JWT", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "The linker does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Addon"], + "summary": "Update a linker value", + "security": [ + { + "oauth2": [] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Bulk update [linker](/cloud/bitbucket/modules/linker/) values for the specified\nlinker of the authenticated application.\n\nA linker value lets applications supply values to modify its regular expression.\n\nThe base regular expression must use a Bitbucket-specific match group `(?K)`\nwhich will be translated to `([\\w\\-]+)`. A value must match this pattern.\n\n[Read more about linker values](/cloud/bitbucket/modules/linker/#usingthebitbucketapitosupplyvalues)" + }, + "parameters": [ + { + "name": "linker_key", + "in": "path", + "description": "The unique key of a [linker module](/cloud/bitbucket/modules/linker/)\nas defined in an application descriptor.", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/addon/linkers/{linker_key}/values/{value_id}": { + "delete": { + "responses": { + "204": { + "description": "Successfully deleted the linker value." + }, + "401": { + "description": "Authentication must use app JWT", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "The linker value does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Addon"], + "summary": "Delete a linker value", + "security": [ + { + "oauth2": [] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Delete a single [linker](/cloud/bitbucket/modules/linker/) value\nof the authenticated application." + }, + "get": { + "responses": { + "200": { + "description": "Successful." + }, + "401": { + "description": "Authentication must use app JWT", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "The linker value does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Addon"], + "summary": "Get a linker value", + "security": [ + { + "oauth2": [] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Get a single [linker](/cloud/bitbucket/modules/linker/) value\nof the authenticated application." + }, + "parameters": [ + { + "name": "linker_key", + "in": "path", + "description": "The unique key of a [linker module](/cloud/bitbucket/modules/linker/)\nas defined in an application descriptor.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "value_id", + "in": "path", + "description": "The numeric ID of the linker value.", + "required": true, + "schema": { + "type": "integer" + } + } + ] + }, + "/hook_events": { + "get": { + "responses": { + "200": { + "description": "A mapping of resource/subject types pointing to their individual event types.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/subject_types" + } + } + } + } + }, + "tags": ["Webhooks"], + "summary": "Get a webhook resource", + "security": [ + { + "oauth2": [] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns the webhook resource or subject types on which webhooks can\nbe registered.\n\nEach resource/subject type contains an `events` link that returns the\npaginated list of specific events each individual subject type can\nemit.\n\nThis endpoint is publicly accessible and does not require\nauthentication or scopes.\n\nExample:\n\n```\n$ curl https://api.bitbucket.org/2.0/hook_events\n\n{\n \"repository\": {\n \"links\": {\n \"events\": {\n \"href\": \"https://api.bitbucket.org/2.0/hook_events/repository\"\n }\n }\n },\n \"team\": {\n \"links\": {\n \"events\": {\n \"href\": \"https://api.bitbucket.org/2.0/hook_events/team\"\n }\n }\n },\n \"user\": {\n \"links\": {\n \"events\": {\n \"href\": \"https://api.bitbucket.org/2.0/hook_events/user\"\n }\n }\n }\n}\n```" + }, + "parameters": [] + }, + "/hook_events/{subject_type}": { + "get": { + "responses": { + "200": { + "description": "A paginated list of webhook types available to subscribe on.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_hook_events" + } + } + } + }, + "404": { + "description": "If an invalid `{subject_type}` value was specified.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Webhooks"], + "summary": "List subscribable webhook types", + "security": [ + { + "oauth2": [] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns a paginated list of all valid webhook events for the\nspecified entity.\n**The team and user webhooks are deprecated, and you should use workspace instead.\nFor more information, see [the announcement](https://developer.atlassian.com/cloud/bitbucket/bitbucket-api-teams-deprecation/).**\n\nThis is public data that does not require any scopes or authentication.\n\nExample:\n\nNOTE: The following example is a truncated response object for the `workspace` `subject_type`.\nWe return the same structure for the other `subject_type` objects.\n\n```\n$ curl https://api.bitbucket.org/2.0/hook_events/workspace\n{\n \"page\": 1,\n \"pagelen\": 30,\n \"size\": 21,\n \"values\": [\n {\n \"category\": \"Repository\",\n \"description\": \"Whenever a repository push occurs\",\n \"event\": \"repo:push\",\n \"label\": \"Push\"\n },\n {\n \"category\": \"Repository\",\n \"description\": \"Whenever a repository fork occurs\",\n \"event\": \"repo:fork\",\n \"label\": \"Fork\"\n },\n {\n \"category\": \"Repository\",\n \"description\": \"Whenever a repository import occurs\",\n \"event\": \"repo:imported\",\n \"label\": \"Import\"\n },\n ...\n {\n \"category\":\"Pull Request\",\n \"label\":\"Approved\",\n \"description\":\"When someone has approved a pull request\",\n \"event\":\"pullrequest:approved\"\n },\n ]\n}\n```" + }, + "parameters": [ + { + "name": "subject_type", + "in": "path", + "description": "A resource or subject type.", + "required": true, + "schema": { + "type": "string", + "enum": ["workspace", "user", "repository", "team"] + } + } + ] + }, + "/pullrequests/{selected_user}": { + "get": { + "responses": { + "200": { + "description": "All pull requests authored by the specified user.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_pullrequests" + } + } + } + }, + "404": { + "description": "If the specified user does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "parameters": [ + { + "name": "state", + "in": "query", + "description": "Only return pull requests that are in this state. This parameter can be repeated.", + "schema": { + "type": "string", + "enum": ["MERGED", "SUPERSEDED", "OPEN", "DECLINED"] + } + } + ], + "tags": ["Pullrequests"], + "summary": "List pull requests for a user", + "security": [ + { + "oauth2": ["pullrequest"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns all pull requests authored by the specified user.\n\nBy default only open pull requests are returned. This can be controlled\nusing the `state` query parameter. To retrieve pull requests that are\nin one of multiple states, repeat the `state` parameter for each\nindividual state.\n\nThis endpoint also supports filtering and sorting of the results. See\n[filtering and sorting](/cloud/bitbucket/rest/intro/#filtering) for more details." + }, + "parameters": [ + { + "name": "selected_user", + "in": "path", + "description": "This can either be the username of the pull request author, the author's UUID\nsurrounded by curly-braces, for example: `{account UUID}`, or the author's Atlassian ID.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories": { + "get": { + "responses": { + "200": { + "description": "All public repositories.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_repositories" + } + } + } + } + }, + "parameters": [ + { + "name": "after", + "in": "query", + "description": "Filter the results to include only repositories created on or\nafter this [ISO-8601](https://en.wikipedia.org/wiki/ISO_8601)\n timestamp. Example: `YYYY-MM-DDTHH:mm:ss.sssZ`", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "role", + "in": "query", + "description": "Filters the result based on the authenticated user's role on each repository.\n\n* **member**: returns repositories to which the user has explicit read access\n* **contributor**: returns repositories to which the user has explicit write access\n* **admin**: returns repositories to which the user has explicit administrator access\n* **owner**: returns all repositories owned by the current user\n", + "required": false, + "schema": { + "type": "string", + "enum": ["admin", "contributor", "member", "owner"] + } + }, + { + "name": "q", + "in": "query", + "description": "Query string to narrow down the response as per [filtering and sorting](/cloud/bitbucket/rest/intro/#filtering).\n`role` parameter must also be specified.\n", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "sort", + "in": "query", + "description": "Field by which the results should be sorted as per [filtering and sorting](/cloud/bitbucket/rest/intro/#filtering).\n", + "required": false, + "schema": { + "type": "string" + } + } + ], + "tags": ["Repositories"], + "summary": "List public repositories", + "security": [ + { + "oauth2": ["repository"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns a paginated list of all public repositories.\n\nThis endpoint also supports filtering and sorting of the results. See\n[filtering and sorting](/cloud/bitbucket/rest/intro/#filtering) for more details." + }, + "parameters": [] + }, + "/repositories/{workspace}": { + "get": { + "responses": { + "200": { + "description": "The repositories owned by the specified account.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_repositories" + } + } + } + }, + "404": { + "description": "If the specified account does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "410": { + "description": "If the specified account marked as spam.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "parameters": [ + { + "name": "role", + "in": "query", + "description": "\nFilters the result based on the authenticated user's role on each repository.\n\n* **member**: returns repositories to which the user has explicit read access\n* **contributor**: returns repositories to which the user has explicit write access\n* **admin**: returns repositories to which the user has explicit administrator access\n* **owner**: returns all repositories owned by the current user\n", + "required": false, + "schema": { + "type": "string", + "enum": ["admin", "contributor", "member", "owner"] + } + }, + { + "name": "q", + "in": "query", + "description": "\nQuery string to narrow down the response as per [filtering and sorting](/cloud/bitbucket/rest/intro/#filtering).\n", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "sort", + "in": "query", + "description": "\nField by which the results should be sorted as per [filtering and sorting](/cloud/bitbucket/rest/intro/#filtering).\n ", + "required": false, + "schema": { + "type": "string" + } + } + ], + "tags": ["Repositories"], + "summary": "List repositories in a workspace", + "security": [ + { + "oauth2": ["repository"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns a paginated list of all repositories owned by the specified\nworkspace.\n\nThe result can be narrowed down based on the authenticated user's role.\n\nE.g. with `?role=contributor`, only those repositories that the\nauthenticated user has write access to are returned (this includes any\nrepo the user is an admin on, as that implies write access).\n\nThis endpoint also supports filtering and sorting of the results. See\n[filtering and sorting](/cloud/bitbucket/rest/intro/#filtering) for more details." + }, + "parameters": [ + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}": { + "delete": { + "responses": { + "204": { + "description": "Indicates successful deletion." + }, + "403": { + "description": "If the caller either does not have admin access to the repository, or the repository is set to read-only.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "If the repository does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "parameters": [ + { + "name": "redirect_to", + "in": "query", + "description": "If a repository has been moved to a new location, use this parameter to\nshow users a friendly message in the Bitbucket UI that the repository\nhas moved to a new location. However, a GET to this endpoint will still\nreturn a 404.\n", + "required": false, + "schema": { + "type": "string" + } + } + ], + "tags": ["Repositories"], + "summary": "Delete a repository", + "security": [ + { + "oauth2": ["repository:delete"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Deletes the repository. This is an irreversible operation.\n\nThis does not affect its forks." + }, + "get": { + "responses": { + "200": { + "description": "The repository object.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/repository" + } + } + } + }, + "403": { + "description": "If the repository is private and the authenticated user does not have access to it.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "If no repository exists at this location.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Repositories"], + "summary": "Get a repository", + "security": [ + { + "oauth2": ["repository"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns the object describing this repository." + }, + "post": { + "responses": { + "200": { + "description": "The newly created repository.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/repository" + } + } + } + }, + "400": { + "description": "If the input document was invalid, or if the caller lacks the privilege to create repositories under the targeted account.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "401": { + "description": "If the request was not authenticated.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/repository" + } + } + }, + "description": "The repository that is to be created. Note that most object elements are optional. Elements \"owner\" and \"full_name\" are ignored as the URL implies them." + }, + "tags": ["Repositories"], + "summary": "Create a repository", + "security": [ + { + "oauth2": ["repository:admin"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Creates a new repository.\n\nNote: In order to set the project for the newly created repository,\npass in either the project key or the project UUID as part of the\nrequest body as shown in the examples below:\n\n```\n$ curl -X POST -H \"Content-Type: application/json\" -d '{\n \"scm\": \"git\",\n \"project\": {\n \"key\": \"MARS\"\n }\n}' https://api.bitbucket.org/2.0/repositories/teamsinspace/hablanding\n```\n\nor\n\n```\n$ curl -X POST -H \"Content-Type: application/json\" -d '{\n \"scm\": \"git\",\n \"project\": {\n \"key\": \"{ba516952-992a-4c2d-acbd-17d502922f96}\"\n }\n}' https://api.bitbucket.org/2.0/repositories/teamsinspace/hablanding\n```\n\nThe project must be assigned for all repositories. If the project is not provided,\nthe repository is automatically assigned to the oldest project in the workspace.\n\nNote: In the examples above, the workspace ID `teamsinspace`,\nand/or the repository name `hablanding` can be replaced by UUIDs." + }, + "put": { + "responses": { + "200": { + "description": "The existing repository has been updated", + "headers": { + "Location": { + "description": "The location of the repository. This header is only\nprovided when the repository's name is changed.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/repository" + } + } + } + }, + "201": { + "description": "A new repository has been created", + "headers": { + "Location": { + "description": "The location of the newly created repository", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/repository" + } + } + } + }, + "400": { + "description": "If the input document was invalid, or if the caller lacks the privilege to create repositories under the targeted account.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "401": { + "description": "If the request was not authenticated.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/repository" + } + } + }, + "description": "The repository that is to be updated.\n\nNote that the elements \"owner\" and \"full_name\" are ignored since the\nURL implies them.\n" + }, + "tags": ["Repositories"], + "summary": "Update a repository", + "security": [ + { + "oauth2": ["repository:admin"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Since this endpoint can be used to both update and to create a\nrepository, the request body depends on the intent.\n\n#### Creation\n\nSee the POST documentation for the repository endpoint for an example\nof the request body.\n\n#### Update\n\nNote: Changing the `name` of the repository will cause the location to\nbe changed. This is because the URL of the repo is derived from the\nname (a process called slugification). In such a scenario, it is\npossible for the request to fail if the newly created slug conflicts\nwith an existing repository's slug. But if there is no conflict,\nthe new location will be returned in the `Location` header of the\nresponse." + }, + "parameters": [ + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/branch-restrictions": { + "get": { + "responses": { + "200": { + "description": "A paginated list of branch restrictions", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_branchrestrictions" + } + } + } + }, + "401": { + "description": "If the request was not authenticated", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "403": { + "description": "If the authenticated user does not have admin access to the repository", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "If the repository does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "parameters": [ + { + "name": "kind", + "in": "query", + "description": "Branch restrictions of this type", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "pattern", + "in": "query", + "description": "Branch restrictions applied to branches of this pattern", + "required": false, + "schema": { + "type": "string" + } + } + ], + "tags": ["Branch restrictions"], + "summary": "List branch restrictions", + "security": [ + { + "oauth2": ["repository:admin"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns a paginated list of all branch restrictions on the\nrepository." + }, + "post": { + "responses": { + "201": { + "description": "A paginated list of branch restrictions", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/branchrestriction" + } + } + } + }, + "401": { + "description": "If the request was not authenticated", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "403": { + "description": "If the authenticated user does not have admin access to the repository", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "If the repository does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/branchrestriction" + } + } + }, + "description": "The new rule", + "required": true + }, + "tags": ["Branch restrictions"], + "summary": "Create a branch restriction rule", + "security": [ + { + "oauth2": ["repository:admin"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Creates a new branch restriction rule for a repository.\n\n`kind` describes what will be restricted. Allowed values include:\n`push`, `force`, `delete` and `restrict_merges`.\n\nDifferent kinds of branch restrictions have different requirements:\n\n* `push` and `restrict_merges` require `users` and `groups` to be\n specified. Empty lists are allowed, in which case permission is\n denied for everybody.\n\nThe restriction applies to all branches that match. There are\ntwo ways to match a branch. It is configured in `branch_match_kind`:\n\n1. `glob`: Matches a branch against the `pattern`. A `'*'` in\n `pattern` will expand to match zero or more characters, and every\n other character matches itself. For example, `'foo*'` will match\n `'foo'` and `'foobar'`, but not `'barfoo'`. `'*'` will match all\n branches.\n2. `branching_model`: Matches a branch against the repository's\n branching model. The `branch_type` controls the type of branch\n to match. Allowed values include: `production`, `development`,\n `bugfix`, `release`, `feature` and `hotfix`.\n\nThe combination of `kind` and match must be unique. This means that\ntwo `glob` restrictions in a repository cannot have the same `kind` and\n`pattern`. Additionally, two `branching_model` restrictions in a\nrepository cannot have the same `kind` and `branch_type`.\n\n`users` and `groups` are lists of users and groups that are except from\nthe restriction. They can only be configured in `push` and\n`restrict_merges` restrictions. The `push` restriction stops a user\npushing to matching branches unless that user is in `users` or is a\nmember of a group in `groups`. The `restrict_merges` stops a user\nmerging pull requests to matching branches unless that user is in\n`users` or is a member of a group in `groups`. Adding new users or\ngroups to an existing restriction should be done via `PUT`.\n\nNote that branch restrictions with overlapping matchers is allowed,\nbut the resulting behavior may be surprising." + }, + "parameters": [ + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/branch-restrictions/{id}": { + "delete": { + "responses": { + "204": { + "description": "" + }, + "401": { + "description": "If the request was not authenticated", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "403": { + "description": "If the authenticated user does not have admin access to the repository", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "If the repository or branch restriction id does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Branch restrictions"], + "summary": "Delete a branch restriction rule", + "security": [ + { + "oauth2": ["repository:admin"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Deletes an existing branch restriction rule." + }, + "get": { + "responses": { + "200": { + "description": "The branch restriction rule", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/branchrestriction" + } + } + } + }, + "401": { + "description": "If the request was not authenticated", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "403": { + "description": "If the authenticated user does not have admin access to the repository", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "If the repository or branch restriction id does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Branch restrictions"], + "summary": "Get a branch restriction rule", + "security": [ + { + "oauth2": ["repository:admin"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns a specific branch restriction rule." + }, + "put": { + "responses": { + "200": { + "description": "The updated branch restriction rule", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/branchrestriction" + } + } + } + }, + "401": { + "description": "If the request was not authenticated", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "403": { + "description": "If the authenticated user does not have admin access to the repository", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "If the repository or branch restriction id does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/branchrestriction" + } + } + }, + "description": "The new version of the existing rule", + "required": true + }, + "tags": ["Branch restrictions"], + "summary": "Update a branch restriction rule", + "security": [ + { + "oauth2": ["repository:admin"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Updates an existing branch restriction rule.\n\nFields not present in the request body are ignored.\n\nSee [`POST`](/cloud/bitbucket/rest/api-group-branch-restrictions/#api-repositories-workspace-repo-slug-branch-restrictions-post) for details." + }, + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The restriction rule's id", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/branching-model": { + "get": { + "responses": { + "200": { + "description": "The branching model object", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/branching_model" + } + } + } + }, + "401": { + "description": "If the request was not authenticated", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "403": { + "description": "If the authenticated user does not have read access to the repository", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "If the repository does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Branching model"], + "summary": "Get the branching model for a repository", + "security": [ + { + "oauth2": ["repository"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Return the branching model as applied to the repository. This view is\nread-only. The branching model settings can be changed using the\n[settings](branching-model/settings#get) API.\n\nThe returned object:\n\n1. Always has a `development` property. `development.branch` contains\n the actual repository branch object that is considered to be the\n `development` branch. `development.branch` will not be present\n if it does not exist.\n2. Might have a `production` property. `production` will not\n be present when `production` is disabled.\n `production.branch` contains the actual branch object that is\n considered to be the `production` branch. `production.branch` will\n not be present if it does not exist.\n3. Always has a `branch_types` array which contains all enabled branch\n types.\n\nExample body:\n\n```\n{\n \"development\": {\n \"name\": \"master\",\n \"branch\": {\n \"type\": \"branch\",\n \"name\": \"master\",\n \"target\": {\n \"hash\": \"16dffcb0de1b22e249db6799532074cf32efe80f\"\n }\n },\n \"use_mainbranch\": true\n },\n \"production\": {\n \"name\": \"production\",\n \"branch\": {\n \"type\": \"branch\",\n \"name\": \"production\",\n \"target\": {\n \"hash\": \"16dffcb0de1b22e249db6799532074cf32efe80f\"\n }\n },\n \"use_mainbranch\": false\n },\n \"branch_types\": [\n {\n \"kind\": \"release\",\n \"prefix\": \"release/\"\n },\n {\n \"kind\": \"hotfix\",\n \"prefix\": \"hotfix/\"\n },\n {\n \"kind\": \"feature\",\n \"prefix\": \"feature/\"\n },\n {\n \"kind\": \"bugfix\",\n \"prefix\": \"bugfix/\"\n }\n ],\n \"type\": \"branching_model\",\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/.../branching-model\"\n }\n }\n}\n```" + }, + "parameters": [ + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/branching-model/settings": { + "get": { + "responses": { + "200": { + "description": "The branching model configuration", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/branching_model_settings" + } + } + } + }, + "401": { + "description": "If the request was not authenticated", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "403": { + "description": "If the authenticated user does not have admin access to the repository", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "If the repository does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Branching model"], + "summary": "Get the branching model config for a repository", + "security": [ + { + "oauth2": ["repository:admin"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Return the branching model configuration for a repository. The returned\nobject:\n\n1. Always has a `development` property for the development branch.\n2. Always a `production` property for the production branch. The\n production branch can be disabled.\n3. The `branch_types` contains all the branch types.\n\nThis is the raw configuration for the branching model. A client\nwishing to see the branching model with its actual current branches may\nfind the [active model API](/cloud/bitbucket/rest/api-group-branching-model/#api-repositories-workspace-repo-slug-branching-model-get) more useful.\n\nExample body:\n\n```\n{\n \"development\": {\n \"is_valid\": true,\n \"name\": null,\n \"use_mainbranch\": true\n },\n \"production\": {\n \"is_valid\": true,\n \"name\": \"production\",\n \"use_mainbranch\": false,\n \"enabled\": false\n },\n \"branch_types\": [\n {\n \"kind\": \"release\",\n \"enabled\": true,\n \"prefix\": \"release/\"\n },\n {\n \"kind\": \"hotfix\",\n \"enabled\": true,\n \"prefix\": \"hotfix/\"\n },\n {\n \"kind\": \"feature\",\n \"enabled\": true,\n \"prefix\": \"feature/\"\n },\n {\n \"kind\": \"bugfix\",\n \"enabled\": false,\n \"prefix\": \"bugfix/\"\n }\n ],\n \"type\": \"branching_model_settings\",\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/.../branching-model/settings\"\n }\n }\n}\n```" + }, + "put": { + "responses": { + "200": { + "description": "The updated branching model configuration", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/branching_model_settings" + } + } + } + }, + "400": { + "description": "If the request contains invalid branching model configuration", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "401": { + "description": "If the request was not authenticated", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "403": { + "description": "If the authenticated user does not have admin access to the repository", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "If the repository does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Branching model"], + "summary": "Update the branching model config for a repository", + "security": [ + { + "oauth2": ["repository:admin"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Update the branching model configuration for a repository.\n\nThe `development` branch can be configured to a specific branch or to\ntrack the main branch. When set to a specific branch it must\ncurrently exist. Only the passed properties will be updated. The\nproperties not passed will be left unchanged. A request without a\n`development` property will leave the development branch unchanged.\n\nIt is possible for the `development` branch to be invalid. This\nhappens when it points at a specific branch that has been\ndeleted. This is indicated in the `is_valid` field for the branch. It is\nnot possible to update the settings for `development` if that\nwould leave the branch in an invalid state. Such a request will be\nrejected.\n\nThe `production` branch can be a specific branch, the main\nbranch or disabled. When set to a specific branch it must currently\nexist. The `enabled` property can be used to enable (`true`) or\ndisable (`false`) it. Only the passed properties will be updated. The\nproperties not passed will be left unchanged. A request without a\n`production` property will leave the production branch unchanged.\n\nIt is possible for the `production` branch to be invalid. This\nhappens when it points at a specific branch that has been\ndeleted. This is indicated in the `is_valid` field for the branch. A\nrequest that would leave `production` enabled and invalid will be\nrejected. It is possible to update `production` and make it invalid if\nit would also be left disabled.\n\nThe `branch_types` property contains the branch types to be updated.\nOnly the branch types passed will be updated. All updates will be\nrejected if it would leave the branching model in an invalid state.\nFor branch types this means that:\n\n1. The prefixes for all enabled branch types are valid. For example,\n it is not possible to use '*' inside a Git prefix.\n2. A prefix of an enabled branch type must not be a prefix of another\n enabled branch type. This is to ensure that a branch can be easily\n classified by its prefix unambiguously.\n\nIt is possible to store an invalid prefix if that branch type would be\nleft disabled. Only the passed properties will be updated. The\nproperties not passed will be left unchanged. Each branch type must\nhave a `kind` property to identify it.\n\nExample Body:\n\n```\n {\n \"development\": {\n \"use_mainbranch\": true\n },\n \"production\": {\n \"enabled\": true,\n \"use_mainbranch\": false,\n \"name\": \"production\"\n },\n \"branch_types\": [\n {\n \"kind\": \"bugfix\",\n \"enabled\": true,\n \"prefix\": \"bugfix/\"\n },\n {\n \"kind\": \"feature\",\n \"enabled\": true,\n \"prefix\": \"feature/\"\n },\n {\n \"kind\": \"hotfix\",\n \"prefix\": \"hotfix/\"\n },\n {\n \"kind\": \"release\",\n \"enabled\": false,\n }\n ]\n }\n```\n\nThere is currently a side effect when using this API endpoint. If the\nrepository is inheriting branching model settings from its project,\nupdating the branching model for this repository will disable the\nproject setting inheritance.\n\n\nWe have deprecated this side effect and will remove it on 1 August 2022." + }, + "parameters": [ + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/commit/{commit}": { + "get": { + "responses": { + "200": { + "description": "The commit object", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/commit" + } + } + } + }, + "404": { + "description": "If the specified commit or repository does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Commits"], + "summary": "Get a commit", + "security": [ + { + "oauth2": ["repository"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns the specified commit.\n\nExample:\n\n```\n$ curl https://api.bitbucket.org/2.0/repositories/bitbucket/geordi/commit/f7591a1\n{\n \"rendered\": {\n \"message\": {\n \"raw\": \"Add a GEORDI_OUTPUT_DIR setting\",\n \"markup\": \"markdown\",\n \"html\": \"

Add a GEORDI_OUTPUT_DIR setting

\",\n \"type\": \"rendered\"\n }\n },\n \"hash\": \"f7591a13eda445d9a9167f98eb870319f4b6c2d8\",\n \"repository\": {\n \"name\": \"geordi\",\n \"type\": \"repository\",\n \"full_name\": \"bitbucket/geordi\",\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/bitbucket/geordi\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/bitbucket/geordi\"\n },\n \"avatar\": {\n \"href\": \"https://bytebucket.org/ravatar/%7B85d08b4e-571d-44e9-a507-fa476535aa98%7D?ts=1730260\"\n }\n },\n \"uuid\": \"{85d08b4e-571d-44e9-a507-fa476535aa98}\"\n },\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/bitbucket/geordi/commit/f7591a13eda445d9a9167f98eb870319f4b6c2d8\"\n },\n \"comments\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/bitbucket/geordi/commit/f7591a13eda445d9a9167f98eb870319f4b6c2d8/comments\"\n },\n \"patch\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/bitbucket/geordi/patch/f7591a13eda445d9a9167f98eb870319f4b6c2d8\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/bitbucket/geordi/commits/f7591a13eda445d9a9167f98eb870319f4b6c2d8\"\n },\n \"diff\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/bitbucket/geordi/diff/f7591a13eda445d9a9167f98eb870319f4b6c2d8\"\n },\n \"approve\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/bitbucket/geordi/commit/f7591a13eda445d9a9167f98eb870319f4b6c2d8/approve\"\n },\n \"statuses\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/bitbucket/geordi/commit/f7591a13eda445d9a9167f98eb870319f4b6c2d8/statuses\"\n }\n },\n \"author\": {\n \"raw\": \"Brodie Rao \",\n \"type\": \"author\",\n \"user\": {\n \"display_name\": \"Brodie Rao\",\n \"uuid\": \"{9484702e-c663-4afd-aefb-c93a8cd31c28}\",\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/users/%7B9484702e-c663-4afd-aefb-c93a8cd31c28%7D\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/%7B9484702e-c663-4afd-aefb-c93a8cd31c28%7D/\"\n },\n \"avatar\": {\n \"href\": \"https://avatar-management--avatars.us-west-2.prod.public.atl-paas.net/557058:3aae1e05-702a-41e5-81c8-f36f29afb6ca/613070db-28b0-421f-8dba-ae8a87e2a5c7/128\"\n }\n },\n \"type\": \"user\",\n \"nickname\": \"brodie\",\n \"account_id\": \"557058:3aae1e05-702a-41e5-81c8-f36f29afb6ca\"\n }\n },\n \"summary\": {\n \"raw\": \"Add a GEORDI_OUTPUT_DIR setting\",\n \"markup\": \"markdown\",\n \"html\": \"

Add a GEORDI_OUTPUT_DIR setting

\",\n \"type\": \"rendered\"\n },\n \"participants\": [],\n \"parents\": [\n {\n \"type\": \"commit\",\n \"hash\": \"f06941fec4ef6bcb0c2456927a0cf258fa4f899b\",\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/bitbucket/geordi/commit/f06941fec4ef6bcb0c2456927a0cf258fa4f899b\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/bitbucket/geordi/commits/f06941fec4ef6bcb0c2456927a0cf258fa4f899b\"\n }\n }\n }\n ],\n \"date\": \"2012-07-16T19:37:54+00:00\",\n \"message\": \"Add a GEORDI_OUTPUT_DIR setting\",\n \"type\": \"commit\"\n}\n```" + }, + "parameters": [ + { + "name": "commit", + "in": "path", + "description": "The commit's SHA1.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/commit/{commit}/approve": { + "delete": { + "responses": { + "204": { + "description": "An empty response indicating the authenticated user's approval has been withdrawn." + }, + "404": { + "description": "If the specified commit, or the repository does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Commits"], + "summary": "Unapprove a commit", + "security": [ + { + "oauth2": ["repository:write"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Redact the authenticated user's approval of the specified commit.\n\nThis operation is only available to users that have explicit access to\nthe repository. In contrast, just the fact that a repository is\npublicly accessible to users does not give them the ability to approve\ncommits." + }, + "post": { + "responses": { + "200": { + "description": "The `participant` object recording that the authenticated user approved the commit.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/participant" + } + } + } + }, + "404": { + "description": "If the specified commit, or the repository does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Commits"], + "summary": "Approve a commit", + "security": [ + { + "oauth2": ["repository:write"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Approve the specified commit as the authenticated user.\n\nThis operation is only available to users that have explicit access to\nthe repository. In contrast, just the fact that a repository is\npublicly accessible to users does not give them the ability to approve\ncommits." + }, + "parameters": [ + { + "name": "commit", + "in": "path", + "description": "The commit's SHA1.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/commit/{commit}/comments": { + "get": { + "responses": { + "200": { + "description": "A paginated list of commit comments.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_commit_comments" + } + } + } + } + }, + "parameters": [ + { + "name": "q", + "in": "query", + "description": "Query string to narrow down the response as per\n[filtering and sorting](/cloud/bitbucket/rest/intro/#filtering).\n", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "sort", + "in": "query", + "description": "Field by which the results should be sorted as per\n[filtering and sorting](/cloud/bitbucket/rest/intro/#filtering).\n", + "required": false, + "schema": { + "type": "string" + } + } + ], + "tags": ["Commits"], + "summary": "List a commit's comments", + "security": [ + { + "oauth2": ["repository"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns the commit's comments.\n\nThis includes both global and inline comments.\n\nThe default sorting is oldest to newest and can be overridden with\nthe `sort` query parameter." + }, + "post": { + "responses": { + "201": { + "description": "The newly created comment.", + "headers": { + "Location": { + "description": "The location of the newly created comment.", + "schema": { + "type": "string" + } + } + } + }, + "400": { + "description": "If the comment was detected as spam, or if the parent comment is not attached to the same node as the new comment" + }, + "404": { + "description": "If a parent ID was passed in that cannot be found" + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/commit_comment" + } + } + }, + "description": "The specified comment.", + "required": true + }, + "tags": ["Commits"], + "summary": "Create comment for a commit", + "security": [ + { + "oauth2": ["repository"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Creates new comment on the specified commit.\n\nTo post a reply to an existing comment, include the `parent.id` field:\n\n```\n$ curl https://api.bitbucket.org/2.0/repositories/atlassian/prlinks/commit/db9ba1e031d07a02603eae0e559a7adc010257fc/comments/ \\\n -X POST -u evzijst \\\n -H 'Content-Type: application/json' \\\n -d '{\"content\": {\"raw\": \"One more thing!\"},\n \"parent\": {\"id\": 5728901}}'\n```" + }, + "parameters": [ + { + "name": "commit", + "in": "path", + "description": "The commit's SHA1.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/commit/{commit}/comments/{comment_id}": { + "get": { + "responses": { + "200": { + "description": "The commit comment.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/commit_comment" + } + } + } + } + }, + "tags": ["Commits"], + "summary": "Get a commit comment", + "security": [ + { + "oauth2": ["repository"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns the specified commit comment." + }, + "parameters": [ + { + "name": "comment_id", + "in": "path", + "description": "The id of the comment.", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "name": "commit", + "in": "path", + "description": "The commit's SHA1.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/commit/{commit}/properties/{app_key}/{property_name}": { + "put": { + "responses": { + "204": { + "description": "An empty response." + } + }, + "parameters": [ + { + "required": true, + "description": "The repository container; either the workspace slug or the UUID in curly braces.", + "in": "path", + "name": "workspace", + "schema": { + "type": "string" + } + }, + { + "required": true, + "description": "The repository.", + "in": "path", + "name": "repo_slug", + "schema": { + "type": "string" + } + }, + { + "required": true, + "description": "The commit.", + "in": "path", + "name": "commit", + "schema": { + "type": "string" + } + }, + { + "required": true, + "description": "The key of the Connect app.", + "in": "path", + "name": "app_key", + "schema": { + "type": "string" + } + }, + { + "required": true, + "description": "The name of the property.", + "in": "path", + "name": "property_name", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/application_property" + }, + "tags": ["properties"], + "description": "Update an [application property](/cloud/bitbucket/application-properties/) value stored against a commit.", + "summary": "Update a commit application property", + "operationId": "updateCommitHostedPropertyValue" + }, + "delete": { + "responses": { + "204": { + "description": "An empty response." + } + }, + "parameters": [ + { + "required": true, + "description": "The repository container; either the workspace slug or the UUID in curly braces.", + "in": "path", + "name": "workspace", + "schema": { + "type": "string" + } + }, + { + "required": true, + "description": "The repository.", + "in": "path", + "name": "repo_slug", + "schema": { + "type": "string" + } + }, + { + "required": true, + "description": "The commit.", + "in": "path", + "name": "commit", + "schema": { + "type": "string" + } + }, + { + "required": true, + "description": "The key of the Connect app.", + "in": "path", + "name": "app_key", + "schema": { + "type": "string" + } + }, + { + "required": true, + "description": "The name of the property.", + "in": "path", + "name": "property_name", + "schema": { + "type": "string" + } + } + ], + "tags": ["properties"], + "description": "Delete an [application property](/cloud/bitbucket/application-properties/) value stored against a commit.", + "summary": "Delete a commit application property", + "operationId": "deleteCommitHostedPropertyValue" + }, + "get": { + "responses": { + "200": { + "description": "The value of the property.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/application_property" + } + } + } + } + }, + "parameters": [ + { + "required": true, + "description": "The repository container; either the workspace slug or the UUID in curly braces.", + "in": "path", + "name": "workspace", + "schema": { + "type": "string" + } + }, + { + "required": true, + "description": "The repository.", + "in": "path", + "name": "repo_slug", + "schema": { + "type": "string" + } + }, + { + "required": true, + "description": "The commit.", + "in": "path", + "name": "commit", + "schema": { + "type": "string" + } + }, + { + "required": true, + "description": "The key of the Connect app.", + "in": "path", + "name": "app_key", + "schema": { + "type": "string" + } + }, + { + "required": true, + "description": "The name of the property.", + "in": "path", + "name": "property_name", + "schema": { + "type": "string" + } + } + ], + "tags": ["properties"], + "description": "Retrieve an [application property](/cloud/bitbucket/application-properties/) value stored against a commit.", + "summary": "Get a commit application property", + "operationId": "getCommitHostedPropertyValue" + } + }, + "/repositories/{workspace}/{repo_slug}/commit/{commit}/pullrequests": { + "get": { + "responses": { + "200": { + "description": "The paginated list of pull requests.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_pullrequests" + } + } + } + }, + "202": { + "description": "The repository's pull requests are still being indexed.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_pullrequests" + } + } + } + }, + "404": { + "description": "Either the repository does not exist, or pull request commit links have not yet been indexed.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "parameters": [ + { + "required": true, + "description": "This can either be the workspace ID (slug) or the workspace UUID surrounded by curly-braces", + "in": "path", + "name": "workspace", + "schema": { + "type": "string" + } + }, + { + "required": true, + "description": "The repository; either the UUID in curly braces, or the slug", + "in": "path", + "name": "repo_slug", + "schema": { + "type": "string" + } + }, + { + "required": true, + "description": "The SHA1 of the commit", + "in": "path", + "name": "commit", + "schema": { + "type": "string" + } + }, + { + "description": "Which page to retrieve", + "required": false, + "in": "query", + "name": "page", + "schema": { + "type": "integer", + "format": "int32", + "default": 1 + } + }, + { + "description": "How many pull requests to retrieve per page", + "required": false, + "in": "query", + "name": "pagelen", + "schema": { + "type": "integer", + "format": "int32", + "default": 30 + } + } + ], + "tags": ["Pullrequests"], + "summary": "List pull requests that contain a commit", + "operationId": "getPullrequestsForCommit", + "description": "Returns a paginated list of all pull requests as part of which this commit was reviewed. Pull Request Commit Links app must be installed first before using this API; installation automatically occurs when 'Go to pull request' is clicked from the web interface for a commit's details." + } + }, + "/repositories/{workspace}/{repo_slug}/commit/{commit}/reports": { + "get": { + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_reports" + } + } + } + } + }, + "parameters": [ + { + "description": "This can either be the workspace ID (slug) or the workspace UUID surrounded by curly-braces, for example `{workspace UUID}`.", + "required": true, + "name": "workspace", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The repository.", + "required": true, + "name": "repo_slug", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The commit for which to retrieve reports.", + "required": true, + "name": "commit", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "tags": ["Reports", "Commits"], + "summary": "List reports", + "operationId": "getReportsForCommit", + "description": "Returns a paginated list of Reports linked to this commit." + } + }, + "/repositories/{workspace}/{repo_slug}/commit/{commit}/reports/{reportId}": { + "put": { + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/report" + } + } + } + }, + "400": { + "description": "The provided Report object is malformed or incomplete.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "parameters": [ + { + "description": "This can either be the workspace ID (slug) or the workspace UUID surrounded by curly-braces, for example `{workspace UUID}`.", + "required": true, + "name": "workspace", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The repository.", + "required": true, + "name": "repo_slug", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The commit the report belongs to.", + "required": true, + "name": "commit", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "Either the uuid or external-id of the report.", + "required": true, + "name": "reportId", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/report" + } + } + }, + "description": "The report to create or update", + "required": true + }, + "tags": ["Reports", "Commits"], + "summary": "Create or update a report", + "operationId": "createOrUpdateReport", + "description": "Creates or updates a report for the specified commit.\nTo upload a report, make sure to generate an ID that is unique across all reports for that commit. If you want to use an existing id from your own system, we recommend prefixing it with your system's name to avoid collisions, for example, mySystem-001.\n\n### Sample cURL request:\n```\ncurl --request PUT 'https://api.bitbucket.org/2.0/repositories///commit//reports/mysystem-001' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{\n \"title\": \"Security scan report\",\n \"details\": \"This pull request introduces 10 new dependency vulnerabilities.\",\n \"report_type\": \"SECURITY\",\n \"reporter\": \"mySystem\",\n \"link\": \"http://www.mysystem.com/reports/001\",\n \"result\": \"FAILED\",\n \"data\": [\n {\n \"title\": \"Duration (seconds)\",\n \"type\": \"DURATION\",\n \"value\": 14\n },\n {\n \"title\": \"Safe to merge?\",\n \"type\": \"BOOLEAN\",\n \"value\": false\n }\n ]\n}'\n```\n\n### Possible field values:\nreport_type: SECURITY, COVERAGE, TEST, BUG\nresult: PASSED, FAILED, PENDING\ndata.type: BOOLEAN, DATE, DURATION, LINK, NUMBER, PERCENTAGE, TEXT\n\n#### Data field formats\n| Type Field | Value Field Type | Value Field Display |\n|:--------------|:------------------|:--------------------|\n| None/ Omitted | Number, String or Boolean (not an array or object) | Plain text |\n| BOOLEAN\t| Boolean | The value will be read as a JSON boolean and displayed as 'Yes' or 'No'. |\n| DATE | Number | The value will be read as a JSON number in the form of a Unix timestamp (milliseconds) and will be displayed as a relative date if the date is less than one week ago, otherwise it will be displayed as an absolute date. |\n| DURATION | Number | The value will be read as a JSON number in milliseconds and will be displayed in a human readable duration format. |\n| LINK | Object: `{\"text\": \"Link text here\", \"href\": \"https://link.to.annotation/in/external/tool\"}` | The value will be read as a JSON object containing the fields \"text\" and \"href\" and will be displayed as a clickable link on the report. |\n| NUMBER | Number | The value will be read as a JSON number and large numbers will be displayed in a human readable format (e.g. 14.3k). |\n| PERCENTAGE | Number (between 0 and 100) | The value will be read as a JSON number between 0 and 100 and will be displayed with a percentage sign. |\n| TEXT | String | The value will be read as a JSON string and will be displayed as-is |\n\nPlease refer to the [Code Insights documentation](https://confluence.atlassian.com/bitbucket/code-insights-994316785.html) for more information.\n" + }, + "get": { + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/report" + } + } + } + }, + "404": { + "description": "The report with the given ID was not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "parameters": [ + { + "description": "This can either be the workspace ID (slug) or the workspace UUID surrounded by curly-braces, for example `{workspace UUID}`.", + "required": true, + "name": "workspace", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The repository.", + "required": true, + "name": "repo_slug", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The commit the report belongs to.", + "required": true, + "name": "commit", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "Either the uuid or external-id of the report.", + "required": true, + "name": "reportId", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "tags": ["Reports", "Commits"], + "summary": "Get a report", + "operationId": "getReport", + "description": "Returns a single Report matching the provided ID." + }, + "delete": { + "responses": { + "204": { + "description": "No content" + } + }, + "parameters": [ + { + "description": "This can either be the workspace ID (slug) or the workspace UUID surrounded by curly-braces, for example `{workspace UUID}`.", + "required": true, + "name": "workspace", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The repository.", + "required": true, + "name": "repo_slug", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The commit the report belongs to.", + "required": true, + "name": "commit", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "Either the uuid or external-id of the report.", + "required": true, + "name": "reportId", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "tags": ["Reports", "Commits"], + "summary": "Delete a report", + "operationId": "deleteReport", + "description": "Deletes a single Report matching the provided ID." + } + }, + "/repositories/{workspace}/{repo_slug}/commit/{commit}/reports/{reportId}/annotations": { + "post": { + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/report_annotation" + }, + "type": "array" + } + } + } + } + }, + "parameters": [ + { + "description": "This can either be the workspace ID (slug) or the workspace UUID surrounded by curly-braces, for example `{workspace UUID}`.", + "required": true, + "name": "workspace", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The repository.", + "required": true, + "name": "repo_slug", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The commit for which to retrieve reports.", + "required": true, + "name": "commit", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "Uuid or external-if of the report for which to get annotations for.", + "required": true, + "name": "reportId", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "minItems": 1, + "items": { + "$ref": "#/components/schemas/report_annotation" + }, + "type": "array", + "maxItems": 100 + } + } + }, + "description": "The annotations to create or update", + "required": true + }, + "tags": ["Reports", "Commits"], + "summary": "Bulk create or update annotations", + "operationId": "bulkCreateOrUpdateAnnotations", + "description": "Bulk upload of annotations.\nAnnotations are individual findings that have been identified as part of a report, for example, a line of code that represents a vulnerability. These annotations can be attached to a specific file and even a specific line in that file, however, that is optional. Annotations are not mandatory and a report can contain up to 1000 annotations.\n\nAdd the annotations you want to upload as objects in a JSON array and make sure each annotation has the external_id field set to a unique value. If you want to use an existing id from your own system, we recommend prefixing it with your system's name to avoid collisions, for example, mySystem-annotation001. The external id can later be used to identify the report as an alternative to the generated [UUID](https://developer.atlassian.com/bitbucket/api/2/reference/meta/uri-uuid#uuid). You can upload up to 100 annotations per POST request.\n\n### Sample cURL request:\n```\ncurl --location 'https://api.bitbucket.org/2.0/repositories///commit//reports/mysystem-001/annotations' \\\n--header 'Content-Type: application/json' \\\n--data-raw '[\n {\n \"external_id\": \"mysystem-annotation001\",\n \"title\": \"Security scan report\",\n \"annotation_type\": \"VULNERABILITY\",\n \"summary\": \"This line represents a security threat.\",\n \"severity\": \"HIGH\",\n \"path\": \"my-service/src/main/java/com/myCompany/mysystem/logic/Main.java\",\n \"line\": 42\n },\n {\n \"external_id\": \"mySystem-annotation002\",\n \"title\": \"Bug report\",\n \"annotation_type\": \"BUG\",\n \"result\": \"FAILED\",\n \"summary\": \"This line might introduce a bug.\",\n \"severity\": \"MEDIUM\",\n \"path\": \"my-service/src/main/java/com/myCompany/mysystem/logic/Helper.java\",\n \"line\": 13\n }\n]'\n```\n\n### Possible field values:\nannotation_type: VULNERABILITY, CODE_SMELL, BUG\nresult: PASSED, FAILED, IGNORED, SKIPPED\nseverity: HIGH, MEDIUM, LOW, CRITICAL\n\nPlease refer to the [Code Insights documentation](https://confluence.atlassian.com/bitbucket/code-insights-994316785.html) for more information.\n" + }, + "get": { + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_annotations" + } + } + } + } + }, + "parameters": [ + { + "description": "This can either be the workspace ID (slug) or the workspace UUID surrounded by curly-braces, for example `{workspace UUID}`.", + "required": true, + "name": "workspace", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The repository.", + "required": true, + "name": "repo_slug", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The commit for which to retrieve reports.", + "required": true, + "name": "commit", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "Uuid or external-if of the report for which to get annotations for.", + "required": true, + "name": "reportId", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "tags": ["Reports", "Commits"], + "summary": "List annotations", + "operationId": "getAnnotationsForReport", + "description": "Returns a paginated list of Annotations for a specified report." + } + }, + "/repositories/{workspace}/{repo_slug}/commit/{commit}/reports/{reportId}/annotations/{annotationId}": { + "put": { + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/report_annotation" + } + } + } + }, + "400": { + "description": "The provided Annotation object is malformed or incomplete.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "parameters": [ + { + "description": "This can either be the workspace ID (slug) or the workspace UUID surrounded by curly-braces, for example `{workspace UUID}`.", + "required": true, + "name": "workspace", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The repository.", + "required": true, + "name": "repo_slug", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The commit the report belongs to.", + "required": true, + "name": "commit", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "Either the uuid or external-id of the report.", + "required": true, + "name": "reportId", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "Either the uuid or external-id of the annotation.", + "required": true, + "name": "annotationId", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/report_annotation" + } + } + }, + "description": "The annotation to create or update", + "required": true + }, + "tags": ["Reports", "Commits"], + "summary": "Create or update an annotation", + "operationId": "createOrUpdateAnnotation", + "description": "Creates or updates an individual annotation for the specified report.\nAnnotations are individual findings that have been identified as part of a report, for example, a line of code that represents a vulnerability. These annotations can be attached to a specific file and even a specific line in that file, however, that is optional. Annotations are not mandatory and a report can contain up to 1000 annotations.\n\nJust as reports, annotation needs to be uploaded with a unique ID that can later be used to identify the report as an alternative to the generated [UUID](https://developer.atlassian.com/bitbucket/api/2/reference/meta/uri-uuid#uuid). If you want to use an existing id from your own system, we recommend prefixing it with your system's name to avoid collisions, for example, mySystem-annotation001.\n\n### Sample cURL request:\n```\ncurl --request PUT 'https://api.bitbucket.org/2.0/repositories///commit//reports/mySystem-001/annotations/mysystem-annotation001' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{\n \"title\": \"Security scan report\",\n \"annotation_type\": \"VULNERABILITY\",\n \"summary\": \"This line represents a security thread.\",\n \"severity\": \"HIGH\",\n \"path\": \"my-service/src/main/java/com/myCompany/mysystem/logic/Main.java\",\n \"line\": 42\n}'\n```\n\n### Possible field values:\nannotation_type: VULNERABILITY, CODE_SMELL, BUG\nresult: PASSED, FAILED, IGNORED, SKIPPED\nseverity: HIGH, MEDIUM, LOW, CRITICAL\n\nPlease refer to the [Code Insights documentation](https://confluence.atlassian.com/bitbucket/code-insights-994316785.html) for more information.\n" + }, + "get": { + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/report_annotation" + } + } + } + }, + "404": { + "description": "The annotation with the given ID was not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "parameters": [ + { + "description": "This can either be the workspace ID (slug) or the workspace UUID surrounded by curly-braces, for example `{workspace UUID}`.", + "required": true, + "name": "workspace", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The repository.", + "required": true, + "name": "repo_slug", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The commit the report belongs to.", + "required": true, + "name": "commit", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "Either the uuid or external-id of the report.", + "required": true, + "name": "reportId", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "Either the uuid or external-id of the annotation.", + "required": true, + "name": "annotationId", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "tags": ["Reports", "Commits"], + "summary": "Get an annotation", + "operationId": "getAnnotation", + "description": "Returns a single Annotation matching the provided ID." + }, + "delete": { + "responses": { + "204": { + "description": "No content" + } + }, + "parameters": [ + { + "description": "This can either be the workspace ID (slug) or the workspace UUID surrounded by curly-braces, for example `{workspace UUID}`.", + "required": true, + "name": "workspace", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The repository.", + "required": true, + "name": "repo_slug", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The commit the annotation belongs to.", + "required": true, + "name": "commit", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "Either the uuid or external-id of the annotation.", + "required": true, + "name": "reportId", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "Either the uuid or external-id of the annotation.", + "required": true, + "name": "annotationId", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "tags": ["Reports", "Commits"], + "summary": "Delete an annotation", + "operationId": "deleteAnnotation", + "description": "Deletes a single Annotation matching the provided ID." + } + }, + "/repositories/{workspace}/{repo_slug}/commit/{commit}/statuses": { + "get": { + "responses": { + "200": { + "description": "A paginated list of all commit statuses for this commit.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_commitstatuses" + } + } + } + }, + "401": { + "description": "If the repository is private and the request was not authenticated." + }, + "404": { + "description": "If the repository or commit does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "parameters": [ + { + "name": "q", + "in": "query", + "description": "Query string to narrow down the response as per\n[filtering and sorting](/cloud/bitbucket/rest/intro/#filtering).\n", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "sort", + "in": "query", + "description": "Field by which the results should be sorted as per\n[filtering and sorting](/cloud/bitbucket/rest/intro/#filtering).\nDefaults to `created_on`.\n", + "required": false, + "schema": { + "type": "string" + } + } + ], + "tags": ["Commit statuses"], + "summary": "List commit statuses for a commit", + "security": [ + { + "oauth2": ["repository"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns all statuses (e.g. build results) for a specific commit." + }, + "parameters": [ + { + "name": "commit", + "in": "path", + "description": "The commit's SHA1.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/commit/{commit}/statuses/build": { + "post": { + "responses": { + "201": { + "description": "The newly created build status object.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/commitstatus" + } + } + } + }, + "401": { + "description": "If the repository is private and the request was not authenticated." + }, + "404": { + "description": "If the repository, commit, or build status key does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/commitstatus" + } + } + }, + "description": "The new commit status object." + }, + "tags": ["Commit statuses"], + "summary": "Create a build status for a commit", + "security": [ + { + "oauth2": ["repository"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Creates a new build status against the specified commit.\n\nIf the specified key already exists, the existing status object will\nbe overwritten.\n\nExample:\n\n```\ncurl https://api.bitbucket.org/2.0/repositories/my-workspace/my-repo/commit/e10dae226959c2194f2b07b077c07762d93821cf/statuses/build/ -X POST -u jdoe -H 'Content-Type: application/json' -d '{\n \"key\": \"MY-BUILD\",\n \"state\": \"SUCCESSFUL\",\n \"description\": \"42 tests passed\",\n \"url\": \"https://www.example.org/my-build-result\"\n }'\n```\n\nWhen creating a new commit status, you can use a URI template for the URL.\nTemplates are URLs that contain variable names that Bitbucket will\nevaluate at runtime whenever the URL is displayed anywhere similar to\nparameter substitution in\n[Bitbucket Connect](https://developer.atlassian.com/bitbucket/concepts/context-parameters.html).\nFor example, one could use `https://foo.com/builds/{repository.full_name}`\nwhich Bitbucket will turn into `https://foo.com/builds/foo/bar` at render time.\nThe context variables available are `repository` and `commit`." + }, + "parameters": [ + { + "name": "commit", + "in": "path", + "description": "The commit's SHA1.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/commit/{commit}/statuses/build/{key}": { + "get": { + "responses": { + "200": { + "description": "The build status object with the specified key.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/commitstatus" + } + } + } + }, + "401": { + "description": "If the repository is private and the request was not authenticated." + }, + "404": { + "description": "If the repository, commit, or build status key does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Commit statuses"], + "summary": "Get a build status for a commit", + "security": [ + { + "oauth2": ["repository"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns the specified build status for a commit." + }, + "put": { + "responses": { + "200": { + "description": "The updated build status object.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/commitstatus" + } + } + } + }, + "401": { + "description": "If the repository is private and the request was not authenticated." + }, + "404": { + "description": "If the repository or build does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/commitstatus" + } + } + }, + "description": "The updated build status object" + }, + "tags": ["Commit statuses"], + "summary": "Update a build status for a commit", + "security": [ + { + "oauth2": ["repository"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Used to update the current status of a build status object on the\nspecific commit.\n\nThis operation can also be used to change other properties of the\nbuild status:\n\n* `state`\n* `name`\n* `description`\n* `url`\n* `refname`\n\nThe `key` cannot be changed." + }, + "parameters": [ + { + "name": "commit", + "in": "path", + "description": "The commit's SHA1.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "key", + "in": "path", + "description": "The build status' unique key", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/commits": { + "get": { + "responses": { + "200": { + "description": "A paginated list of commits", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_changeset" + } + } + } + }, + "404": { + "description": "If the specified repository does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Commits"], + "summary": "List commits", + "security": [ + { + "oauth2": ["repository"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "These are the repository's commits. They are paginated and returned\nin reverse chronological order, similar to the output of `git log`.\nLike these tools, the DAG can be filtered.\n\n#### GET /repositories/{workspace}/{repo_slug}/commits/\n\nReturns all commits in the repo in topological order (newest commit\nfirst). All branches and tags are included (similar to\n`git log --all`).\n\n#### GET /repositories/{workspace}/{repo_slug}/commits/?exclude=master\n\nReturns all commits in the repo that are not on master\n(similar to `git log --all ^master`).\n\n#### GET /repositories/{workspace}/{repo_slug}/commits/?include=foo&include=bar&exclude=fu&exclude=fubar\n\nReturns all commits that are on refs `foo` or `bar`, but not on `fu` or\n`fubar` (similar to `git log foo bar ^fu ^fubar`).\n\nAn optional `path` parameter can be specified that will limit the\nresults to commits that affect that path. `path` can either be a file\nor a directory. If a directory is specified, commits are returned that\nhave modified any file in the directory tree rooted by `path`. It is\nimportant to note that if the `path` parameter is specified, the commits\nreturned by this endpoint may no longer be a DAG, parent commits that\ndo not modify the path will be omitted from the response.\n\n#### GET /repositories/{workspace}/{repo_slug}/commits/?path=README.md&include=foo&include=bar&exclude=master\n\nReturns all commits that are on refs `foo` or `bar`, but not on `master`\nthat changed the file README.md.\n\n#### GET /repositories/{workspace}/{repo_slug}/commits/?path=src/&include=foo&include=bar&exclude=master\n\nReturns all commits that are on refs `foo` or `bar`, but not on `master`\nthat changed to a file in any file in the directory src or its children.\n\nBecause the response could include a very large number of commits, it\nis paginated. Follow the 'next' link in the response to navigate to the\nnext page of commits. As with other paginated resources, do not\nconstruct your own links.\n\nWhen the include and exclude parameters are more than can fit in a\nquery string, clients can use a `x-www-form-urlencoded` POST instead." + }, + "post": { + "responses": { + "200": { + "description": "A paginated list of commits", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_changeset" + } + } + } + }, + "404": { + "description": "If the specified repository does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Commits"], + "summary": "List commits with include/exclude", + "security": [ + { + "oauth2": ["repository"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Identical to `GET /repositories/{workspace}/{repo_slug}/commits`,\nexcept that POST allows clients to place the include and exclude\nparameters in the request body to avoid URL length issues.\n\n**Note that this resource does NOT support new commit creation.**" + }, + "parameters": [ + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/commits/{revision}": { + "get": { + "responses": { + "200": { + "description": "A paginated list of commits", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_changeset" + } + } + } + }, + "404": { + "description": "If the specified revision does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Commits"], + "summary": "List commits for revision", + "security": [ + { + "oauth2": ["repository"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "These are the repository's commits. They are paginated and returned\nin reverse chronological order, similar to the output of `git log`.\nLike these tools, the DAG can be filtered.\n\n#### GET /repositories/{workspace}/{repo_slug}/commits/master\n\nReturns all commits on rev `master` (similar to `git log master`).\n\n#### GET /repositories/{workspace}/{repo_slug}/commits/dev?include=foo&exclude=master\n\nReturns all commits on ref `dev` or `foo`, except those that are reachable on\n`master` (similar to `git log dev foo ^master`).\n\nAn optional `path` parameter can be specified that will limit the\nresults to commits that affect that path. `path` can either be a file\nor a directory. If a directory is specified, commits are returned that\nhave modified any file in the directory tree rooted by `path`. It is\nimportant to note that if the `path` parameter is specified, the commits\nreturned by this endpoint may no longer be a DAG, parent commits that\ndo not modify the path will be omitted from the response.\n\n#### GET /repositories/{workspace}/{repo_slug}/commits/dev?path=README.md&include=foo&include=bar&exclude=master\n\nReturns all commits that are on refs `dev` or `foo` or `bar`, but not on `master`\nthat changed the file README.md.\n\n#### GET /repositories/{workspace}/{repo_slug}/commits/dev?path=src/&include=foo&exclude=master\n\nReturns all commits that are on refs `dev` or `foo`, but not on `master`\nthat changed to a file in any file in the directory src or its children.\n\nBecause the response could include a very large number of commits, it\nis paginated. Follow the 'next' link in the response to navigate to the\nnext page of commits. As with other paginated resources, do not\nconstruct your own links.\n\nWhen the include and exclude parameters are more than can fit in a\nquery string, clients can use a `x-www-form-urlencoded` POST instead." + }, + "post": { + "responses": { + "200": { + "description": "A paginated list of commits", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_changeset" + } + } + } + }, + "404": { + "description": "If the specified revision does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Commits"], + "summary": "List commits for revision using include/exclude", + "security": [ + { + "oauth2": ["repository"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Identical to `GET /repositories/{workspace}/{repo_slug}/commits/{revision}`,\nexcept that POST allows clients to place the include and exclude\nparameters in the request body to avoid URL length issues.\n\n**Note that this resource does NOT support new commit creation.**" + }, + "parameters": [ + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "revision", + "in": "path", + "description": "The commit's SHA1.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/components": { + "get": { + "responses": { + "200": { + "description": "The components that have been defined in the issue tracker.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_components" + } + } + } + }, + "404": { + "description": "The specified repository does not exist or does not have the issue tracker enabled.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Issue tracker"], + "summary": "List components", + "security": [ + { + "oauth2": ["issue"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns the components that have been defined in the issue tracker.\n\nThis resource is only available on repositories that have the issue\ntracker enabled." + }, + "parameters": [ + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/components/{component_id}": { + "get": { + "responses": { + "200": { + "description": "The specified component object.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/component" + } + } + } + }, + "404": { + "description": "The specified repository or component does not exist or does not have the issue tracker enabled.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Issue tracker"], + "summary": "Get a component for issues", + "security": [ + { + "oauth2": ["issue"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns the specified issue tracker component object." + }, + "parameters": [ + { + "name": "component_id", + "in": "path", + "description": "The component's id", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/default-reviewers": { + "get": { + "responses": { + "200": { + "description": "The paginated list of default reviewers" + }, + "403": { + "description": "If the authenticated user does not have access to view the default reviewers", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Pullrequests"], + "summary": "List default reviewers", + "security": [ + { + "oauth2": ["pullrequest"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns the repository's default reviewers.\n\nThese are the users that are automatically added as reviewers on every\nnew pull request that is created." + }, + "parameters": [ + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/default-reviewers/{target_username}": { + "delete": { + "responses": { + "204": { + "description": "The specified user successfully removed from the default reviewers" + }, + "403": { + "description": "If the authenticated user does not have access modify the default reviewers", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "If the specified user does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Pullrequests"], + "summary": "Remove a user from the default reviewers", + "security": [ + { + "oauth2": ["repository:admin"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Removes a default reviewer from the repository." + }, + "get": { + "responses": { + "200": { + "description": "The specified user is a default reviewer" + }, + "403": { + "description": "If the authenticated user does not have access to check if the specified user is a default reviewer", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "If the specified user does not exist or is not a default reviewer", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Pullrequests"], + "summary": "Get a default reviewer", + "security": [ + { + "oauth2": ["pullrequest"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns the specified reviewer.\n\nThis can be used to test whether a user is among the repository's\ndefault reviewers list. A 404 indicates that that specified user is not\na default reviewer." + }, + "put": { + "responses": { + "200": { + "description": "The specified user was successfully added to the default reviewers" + }, + "400": { + "description": "If the authenticated user tried to add a team, bot user, or user without access to the repository to the default reviewers", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "403": { + "description": "If the authenticated user does not have permission to modify the default reviewers", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "If the specified user does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Pullrequests"], + "summary": "Add a user to the default reviewers", + "security": [ + { + "oauth2": ["repository:admin"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Adds the specified user to the repository's list of default\nreviewers.\n\nThis method is idempotent. Adding a user a second time has no effect." + }, + "parameters": [ + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "target_username", + "in": "path", + "description": "This can either be the username or the UUID of the default reviewer,\nsurrounded by curly-braces, for example: `{account UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/deploy-keys": { + "get": { + "responses": { + "200": { + "description": "Deploy keys matching the repository", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_deploy_keys" + } + } + } + }, + "403": { + "description": "If the specified user or repository is not accessible to the current user" + }, + "404": { + "description": "If the specified user or repository does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Deployments"], + "summary": "List deploy keys", + "security": [ + { + "oauth2": ["repository", "repository:admin"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns all deploy-keys belonging to a repository.\n\nExample:\n```\n$ curl -H \"Authorization \" \\\nhttps://api.bitbucket.org/2.0/repositories/mleu/test/deploy-keys\n\nOutput:\n{\n \"pagelen\": 10,\n \"values\": [\n {\n \"id\": 123,\n \"key\": \"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDAK/b1cHHDr/TEV1JGQl+WjCwStKG6Bhrv0rFpEsYlyTBm1fzN0VOJJYn4ZOPCPJwqse6fGbXntEs+BbXiptR+++HycVgl65TMR0b5ul5AgwrVdZdT7qjCOCgaSV74/9xlHDK8oqgGnfA7ZoBBU+qpVyaloSjBdJfLtPY/xqj4yHnXKYzrtn/uFc4Kp9Tb7PUg9Io3qohSTGJGVHnsVblq/rToJG7L5xIo0OxK0SJSQ5vuId93ZuFZrCNMXj8JDHZeSEtjJzpRCBEXHxpOPhAcbm4MzULgkFHhAVgp4JbkrT99/wpvZ7r9AdkTg7HGqL3rlaDrEcWfL7Lu6TnhBdq5\",\n \"label\": \"mykey\",\n \"type\": \"deploy_key\",\n \"created_on\": \"2018-08-15T23:50:59.993890+00:00\",\n \"repository\": {\n \"full_name\": \"mleu/test\",\n \"name\": \"test\",\n \"type\": \"repository\",\n \"uuid\": \"{85d08b4e-571d-44e9-a507-fa476535aa98}\"\n },\n \"links\":{\n \"self\":{\n \"href\": \"https://api.bitbucket.org/2.0/repositories/mleu/test/deploy-keys/123\"\n }\n }\n \"last_used\": null,\n \"comment\": \"mleu@C02W454JHTD8\"\n }\n ],\n \"page\": 1,\n \"size\": 1\n}\n```" + }, + "post": { + "responses": { + "200": { + "description": "The deploy key that was created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/deploy_key" + } + } + } + }, + "400": { + "description": "Invalid deploy key inputs" + }, + "403": { + "description": "If the specified user or repository is not accessible to the current user" + }, + "404": { + "description": "If the specified user or repository does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Deployments"], + "summary": "Add a deploy key", + "security": [ + { + "oauth2": ["repository", "repository:admin"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Create a new deploy key in a repository. Note: If authenticating a deploy key\nwith an OAuth consumer, any changes to the OAuth consumer will subsequently\ninvalidate the deploy key.\n\n\nExample:\n```\n$ curl -XPOST \\\n-H \"Authorization \" \\\n-H \"Content-type: application/json\" \\\nhttps://api.bitbucket.org/2.0/repositories/mleu/test/deploy-keys -d \\\n'{\n \"key\": \"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDAK/b1cHHDr/TEV1JGQl+WjCwStKG6Bhrv0rFpEsYlyTBm1fzN0VOJJYn4ZOPCPJwqse6fGbXntEs+BbXiptR+++HycVgl65TMR0b5ul5AgwrVdZdT7qjCOCgaSV74/9xlHDK8oqgGnfA7ZoBBU+qpVyaloSjBdJfLtPY/xqj4yHnXKYzrtn/uFc4Kp9Tb7PUg9Io3qohSTGJGVHnsVblq/rToJG7L5xIo0OxK0SJSQ5vuId93ZuFZrCNMXj8JDHZeSEtjJzpRCBEXHxpOPhAcbm4MzULgkFHhAVgp4JbkrT99/wpvZ7r9AdkTg7HGqL3rlaDrEcWfL7Lu6TnhBdq5 mleu@C02W454JHTD8\",\n \"label\": \"mydeploykey\"\n}'\n\nOutput:\n{\n \"id\": 123,\n \"key\": \"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDAK/b1cHHDr/TEV1JGQl+WjCwStKG6Bhrv0rFpEsYlyTBm1fzN0VOJJYn4ZOPCPJwqse6fGbXntEs+BbXiptR+++HycVgl65TMR0b5ul5AgwrVdZdT7qjCOCgaSV74/9xlHDK8oqgGnfA7ZoBBU+qpVyaloSjBdJfLtPY/xqj4yHnXKYzrtn/uFc4Kp9Tb7PUg9Io3qohSTGJGVHnsVblq/rToJG7L5xIo0OxK0SJSQ5vuId93ZuFZrCNMXj8JDHZeSEtjJzpRCBEXHxpOPhAcbm4MzULgkFHhAVgp4JbkrT99/wpvZ7r9AdkTg7HGqL3rlaDrEcWfL7Lu6TnhBdq5\",\n \"label\": \"mydeploykey\",\n \"type\": \"deploy_key\",\n \"created_on\": \"2018-08-15T23:50:59.993890+00:00\",\n \"repository\": {\n \"full_name\": \"mleu/test\",\n \"name\": \"test\",\n \"type\": \"repository\",\n \"uuid\": \"{85d08b4e-571d-44e9-a507-fa476535aa98}\"\n },\n \"links\":{\n \"self\":{\n \"href\": \"https://api.bitbucket.org/2.0/repositories/mleu/test/deploy-keys/123\"\n }\n }\n \"last_used\": null,\n \"comment\": \"mleu@C02W454JHTD8\"\n}\n```" + }, + "parameters": [ + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/deploy-keys/{key_id}": { + "delete": { + "responses": { + "204": { + "description": "The key has been deleted" + }, + "403": { + "description": "If the current user does not have permission to delete a key for the specified user" + }, + "404": { + "description": "If the specified user, repository, or deploy key does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Deployments"], + "summary": "Delete a deploy key", + "security": [ + { + "oauth2": ["repository", "repository:admin"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "This deletes a deploy key from a repository.\n\nExample:\n```\n$ curl -XDELETE \\\n-H \"Authorization \" \\\nhttps://api.bitbucket.org/2.0/repositories/mleu/test/deploy-keys/1234\n```" + }, + "get": { + "responses": { + "200": { + "description": "Deploy key matching the key ID", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/deploy_key" + } + } + } + }, + "403": { + "description": "If the specified user or repository is not accessible to the current user" + }, + "404": { + "description": "If the specified user or repository does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Deployments"], + "summary": "Get a deploy key", + "security": [ + { + "oauth2": ["repository", "repository:admin"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns the deploy key belonging to a specific key.\n\nExample:\n```\n$ curl -H \"Authorization \" \\\nhttps://api.bitbucket.org/2.0/repositories/mleu/test/deploy-key/1234\n\nOutput:\n{\n \"comment\": \"mleu@C02W454JHTD8\",\n \"last_used\": null,\n \"links\": {\n \"self\": {\n \"href\": https://api.bitbucket.org/2.0/repositories/mleu/test/deploy-key/1234\"\n }\n },\n \"repository\": {\n \"full_name\": \"mleu/test\",\n \"name\": \"test\",\n \"type\": \"repository\",\n \"uuid\": \"{85d08b4e-571d-44e9-a507-fa476535aa98}\"\n },\n \"label\": \"mykey\",\n \"created_on\": \"2018-08-15T23:50:59.993890+00:00\",\n \"key\": \"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDAK/b1cHHDr/TEV1JGQl+WjCwStKG6Bhrv0rFpEsYlyTBm1fzN0VOJJYn4ZOPCPJwqse6fGbXntEs+BbXiptR+++HycVgl65TMR0b5ul5AgwrVdZdT7qjCOCgaSV74/9xlHDK8oqgGnfA7ZoBBU+qpVyaloSjBdJfLtPY/xqj4yHnXKYzrtn/uFc4Kp9Tb7PUg9Io3qohSTGJGVHnsVblq/rToJG7L5xIo0OxK0SJSQ5vuId93ZuFZrCNMXj8JDHZeSEtjJzpRCBEXHxpOPhAcbm4MzULgkFHhAVgp4JbkrT99/wpvZ7r9AdkTg7HGqL3rlaDrEcWfL7Lu6TnhBdq5\",\n \"id\": 1234,\n \"type\": \"deploy_key\"\n}\n```" + }, + "put": { + "responses": { + "200": { + "description": "The newly updated deploy key.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/deploy_key" + } + } + } + }, + "400": { + "description": "If the submitted key or related value is invalid", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "403": { + "description": "If the current user does not have permission to add a key for the specified user" + }, + "404": { + "description": "If the specified user, repository, or deploy key does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Deployments"], + "summary": "Update a deploy key", + "security": [ + { + "oauth2": ["repository", "repository:admin"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Create a new deploy key in a repository.\n\nThe same key needs to be passed in but the comment and label can change.\n\nExample:\n```\n$ curl -XPUT \\\n-H \"Authorization \" \\\n-H \"Content-type: application/json\" \\\nhttps://api.bitbucket.org/2.0/repositories/mleu/test/deploy-keys/1234 -d \\\n'{\n \"label\": \"newlabel\",\n \"key\": \"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDAK/b1cHHDr/TEV1JGQl+WjCwStKG6Bhrv0rFpEsYlyTBm1fzN0VOJJYn4ZOPCPJwqse6fGbXntEs+BbXiptR+++HycVgl65TMR0b5ul5AgwrVdZdT7qjCOCgaSV74/9xlHDK8oqgGnfA7ZoBBU+qpVyaloSjBdJfLtPY/xqj4yHnXKYzrtn/uFc4Kp9Tb7PUg9Io3qohSTGJGVHnsVblq/rToJG7L5xIo0OxK0SJSQ5vuId93ZuFZrCNMXj8JDHZeSEtjJzpRCBEXHxpOPhAcbm4MzULgkFHhAVgp4JbkrT99/wpvZ7r9AdkTg7HGqL3rlaDrEcWfL7Lu6TnhBdq5 newcomment\",\n}'\n\nOutput:\n{\n \"comment\": \"newcomment\",\n \"last_used\": null,\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/mleu/test/deploy-keys/1234\"\n }\n },\n \"repository\": {\n \"full_name\": \"mleu/test\",\n \"name\": \"test\",\n \"type\": \"repository\",\n \"uuid\": \"{85d08b4e-571d-44e9-a507-fa476535aa98}\"\n },\n \"label\": \"newlabel\",\n \"created_on\": \"2018-08-15T23:50:59.993890+00:00\",\n \"key\": \"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDAK/b1cHHDr/TEV1JGQl+WjCwStKG6Bhrv0rFpEsYlyTBm1fzN0VOJJYn4ZOPCPJwqse6fGbXntEs+BbXiptR+++HycVgl65TMR0b5ul5AgwrVdZdT7qjCOCgaSV74/9xlHDK8oqgGnfA7ZoBBU+qpVyaloSjBdJfLtPY/xqj4yHnXKYzrtn/uFc4Kp9Tb7PUg9Io3qohSTGJGVHnsVblq/rToJG7L5xIo0OxK0SJSQ5vuId93ZuFZrCNMXj8JDHZeSEtjJzpRCBEXHxpOPhAcbm4MzULgkFHhAVgp4JbkrT99/wpvZ7r9AdkTg7HGqL3rlaDrEcWfL7Lu6TnhBdq5\",\n \"id\": 1234,\n \"type\": \"deploy_key\"\n}\n```" + }, + "parameters": [ + { + "name": "key_id", + "in": "path", + "description": "The key ID matching the deploy key.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/deployments/": { + "get": { + "responses": { + "200": { + "description": "The matching deployments.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_deployments" + } + } + } + } + }, + "description": "Find deployments", + "parameters": [ + { + "description": "This can either be the workspace ID (slug) or the workspace UUID surrounded by curly-braces, for example `{workspace UUID}`.", + "required": true, + "name": "workspace", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The repository.", + "required": true, + "name": "repo_slug", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "tags": ["Deployments"], + "summary": "List deployments", + "operationId": "getDeploymentsForRepository" + } + }, + "/repositories/{workspace}/{repo_slug}/deployments/{deployment_uuid}": { + "get": { + "responses": { + "200": { + "description": "The deployment.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/deployment" + } + } + } + }, + "404": { + "description": "No account, repository or deployment with the UUID provided exists.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "description": "Retrieve a deployment", + "parameters": [ + { + "description": "This can either be the workspace ID (slug) or the workspace UUID surrounded by curly-braces, for example `{workspace UUID}`.", + "required": true, + "name": "workspace", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The repository.", + "required": true, + "name": "repo_slug", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The deployment UUID.", + "required": true, + "name": "deployment_uuid", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "tags": ["Deployments"], + "summary": "Get a deployment", + "operationId": "getDeploymentForRepository" + } + }, + "/repositories/{workspace}/{repo_slug}/deployments_config/environments/{environment_uuid}/variables": { + "post": { + "responses": { + "201": { + "headers": { + "Location": { + "description": "The URL of the newly created variable.", + "schema": { + "type": "string" + } + } + }, + "description": "The variable was created.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/deployment_variable" + } + } + } + }, + "404": { + "description": "The account, repository, environment or variable with the given UUID was not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "409": { + "description": "A variable with the provided key already exists.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "description": "Create a deployment environment level variable.", + "parameters": [ + { + "description": "This can either be the workspace ID (slug) or the workspace UUID surrounded by curly-braces, for example `{workspace UUID}`.", + "required": true, + "name": "workspace", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The repository.", + "required": true, + "name": "repo_slug", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The environment.", + "required": true, + "name": "environment_uuid", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/deployment_variable" + } + } + }, + "description": "The variable to create", + "required": true + }, + "tags": ["Pipelines"], + "summary": "Create a variable for an environment", + "operationId": "createDeploymentVariable" + }, + "get": { + "responses": { + "200": { + "description": "The retrieved deployment variables.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_deployment_variable" + } + } + } + } + }, + "description": "Find deployment environment level variables.", + "parameters": [ + { + "description": "This can either be the workspace ID (slug) or the workspace UUID surrounded by curly-braces, for example `{workspace UUID}`.", + "required": true, + "name": "workspace", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The repository.", + "required": true, + "name": "repo_slug", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The environment.", + "required": true, + "name": "environment_uuid", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "tags": ["Pipelines"], + "summary": "List variables for an environment", + "operationId": "getDeploymentVariables" + } + }, + "/repositories/{workspace}/{repo_slug}/deployments_config/environments/{environment_uuid}/variables/{variable_uuid}": { + "put": { + "responses": { + "200": { + "description": "The deployment variable was updated.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/deployment_variable" + } + } + } + }, + "404": { + "description": "The account, repository, environment or variable with the given UUID was not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "description": "Update a deployment environment level variable.", + "parameters": [ + { + "description": "This can either be the workspace ID (slug) or the workspace UUID surrounded by curly-braces, for example `{workspace UUID}`.", + "required": true, + "name": "workspace", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The repository.", + "required": true, + "name": "repo_slug", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The environment.", + "required": true, + "name": "environment_uuid", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The UUID of the variable to update.", + "required": true, + "name": "variable_uuid", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/deployment_variable" + } + } + }, + "description": "The updated deployment variable.", + "required": true + }, + "tags": ["Pipelines"], + "summary": "Update a variable for an environment", + "operationId": "updateDeploymentVariable" + }, + "delete": { + "responses": { + "204": { + "description": "The variable was deleted." + }, + "404": { + "description": "The account, repository, environment or variable with given UUID was not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "description": "Delete a deployment environment level variable.", + "parameters": [ + { + "description": "This can either be the workspace ID (slug) or the workspace UUID surrounded by curly-braces, for example `{workspace UUID}`.", + "required": true, + "name": "workspace", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The repository.", + "required": true, + "name": "repo_slug", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The environment.", + "required": true, + "name": "environment_uuid", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The UUID of the variable to delete.", + "required": true, + "name": "variable_uuid", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "tags": ["Pipelines"], + "summary": "Delete a variable for an environment", + "operationId": "deleteDeploymentVariable" + } + }, + "/repositories/{workspace}/{repo_slug}/diff/{spec}": { + "get": { + "responses": { + "200": { + "description": "The raw diff" + }, + "555": { + "description": "If the diff was too large and timed out.\n\nSince this endpoint does not employ any form of pagination, but\ninstead returns the diff as a single document, it can run into\ntrouble on very large diffs. If Bitbucket times out in cases\nlike these, a 555 status code is returned.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "parameters": [ + { + "name": "context", + "in": "query", + "description": "Generate diffs with lines of context instead of the usual three.", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "path", + "in": "query", + "description": "Limit the diff to a particular file (this parameter\ncan be repeated for multiple paths).", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "ignore_whitespace", + "in": "query", + "description": "Generate diffs that ignore whitespace.", + "required": false, + "schema": { + "type": "boolean" + } + }, + { + "name": "binary", + "in": "query", + "description": "Generate diffs that include binary files, true if omitted.", + "required": false, + "schema": { + "type": "boolean" + } + }, + { + "name": "renames", + "in": "query", + "description": "Whether to perform rename detection, true if omitted.", + "required": false, + "schema": { + "type": "boolean" + } + }, + { + "name": "merge", + "in": "query", + "description": "This parameter is deprecated and will be removed at the end\nof 2022. The 'topic' parameter should be used instead. The\n'merge' and 'topic' parameters cannot be both used at the same\ntime.\n\nIf true, the source commit is merged into the\ndestination commit, and then a diff from the\ndestination to the merge result is returned. If false,\na simple 'two dot' diff between the source and\ndestination is returned. True if omitted.", + "required": false, + "schema": { + "type": "boolean" + } + }, + { + "name": "topic", + "in": "query", + "description": "If true, returns 2-way 'three-dot' diff.\nThis is a diff between the source commit and the merge base\nof the source commit and the destination commit.\nIf false, a simple 'two dot' diff between the source and\ndestination is returned.", + "required": false, + "schema": { + "type": "boolean" + } + } + ], + "tags": ["Commits"], + "summary": "Compare two commits", + "security": [ + { + "oauth2": ["repository"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Produces a raw git-style diff.\n\n#### Single commit spec\n\nIf the `spec` argument to this API is a single commit, the diff is\nproduced against the first parent of the specified commit.\n\n#### Two commit spec\n\nTwo commits separated by `..` may be provided as the `spec`, e.g.,\n`3a8b42..9ff173`. When two commits are provided and the `topic` query\nparameter is true or absent, this API produces a 2-way three dot diff.\nThis is the diff between source commit and the merge base of the source\ncommit and the destination commit. When the `topic` query param is false,\na simple git-style diff is produced.\n\nThe two commits are interpreted as follows:\n\n* First commit: the commit containing the changes we wish to preview\n* Second commit: the commit representing the state to which we want to\n compare the first commit\n* **Note**: This is the opposite of the order used in `git diff`.\n\n#### Comparison to patches\n\nWhile similar to patches, diffs:\n\n* Don't have a commit header (username, commit message, etc)\n* Support the optional `path=foo/bar.py` query param to filter\n the diff to just that one file diff\n\n#### Response\n\nThe raw diff is returned as-is, in whatever encoding the files in the\nrepository use. It is not decoded into unicode. As such, the\ncontent-type is `text/plain`." + }, + "parameters": [ + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "spec", + "in": "path", + "description": "A commit SHA (e.g. `3a8b42`) or a commit range using double dot\nnotation (e.g. `3a8b42..9ff173`).\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/diffstat/{spec}": { + "get": { + "responses": { + "200": { + "description": "The diff stats", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_diffstats" + } + } + } + }, + "555": { + "description": "If generating the diffstat timed out.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Commits"], + "summary": "Compare two commit diff stats", + "security": [ + { + "oauth2": ["repository"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Produces a response in JSON format with a record for every path\nmodified, including information on the type of the change and the\nnumber of lines added and removed.\n\n#### Single commit spec\n\nIf the `spec` argument to this API is a single commit, the diff is\nproduced against the first parent of the specified commit.\n\n#### Two commit spec\n\nTwo commits separated by `..` may be provided as the `spec`, e.g.,\n`3a8b42..9ff173`. When two commits are provided and the `topic` query\nparameter is true or absent, this API produces a 2-way three dot diff.\nThis is the diff between source commit and the merge base of the source\ncommit and the destination commit. When the `topic` query param is false,\na simple git-style diff is produced.\n\nThe two commits are interpreted as follows:\n\n* First commit: the commit containing the changes we wish to preview\n* Second commit: the commit representing the state to which we want to\n compare the first commit\n* **Note**: This is the opposite of the order used in `git diff`.\n\n#### Sample output\n```\ncurl https://api.bitbucket.org/2.0/repositories/bitbucket/geordi/diffstat/d222fa2..e174964\n{\n \"pagelen\": 500,\n \"values\": [\n {\n \"type\": \"diffstat\",\n \"status\": \"modified\",\n \"lines_removed\": 1,\n \"lines_added\": 2,\n \"old\": {\n \"path\": \"setup.py\",\n \"escaped_path\": \"setup.py\",\n \"type\": \"commit_file\",\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/bitbucket/geordi/src/e1749643d655d7c7014001a6c0f58abaf42ad850/setup.py\"\n }\n }\n },\n \"new\": {\n \"path\": \"setup.py\",\n \"escaped_path\": \"setup.py\",\n \"type\": \"commit_file\",\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/bitbucket/geordi/src/d222fa235229c55dad20b190b0b571adf737d5a6/setup.py\"\n }\n }\n }\n }\n ],\n \"page\": 1,\n \"size\": 1\n}\n```" + }, + "parameters": [ + { + "name": "ignore_whitespace", + "in": "query", + "description": "Generate diffs that ignore whitespace", + "required": false, + "schema": { + "type": "boolean" + } + }, + { + "name": "merge", + "in": "query", + "description": "This parameter is deprecated and will be removed at the end\nof 2022. The 'topic' parameter should be used instead. The\n'merge' and 'topic' parameters cannot be both used at the same\ntime.\n\nIf true, the source commit is merged into the\ndestination commit, and then a diffstat from the\ndestination to the merge result is returned. If false,\na simple 'two dot' diffstat between the source and\ndestination is returned. True if omitted.", + "required": false, + "schema": { + "type": "boolean" + } + }, + { + "name": "path", + "in": "query", + "description": "Limit the diffstat to a particular file (this parameter\ncan be repeated for multiple paths).", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "renames", + "in": "query", + "description": "Whether to perform rename detection, true if omitted.", + "required": false, + "schema": { + "type": "boolean" + } + }, + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "spec", + "in": "path", + "description": "A commit SHA (e.g. `3a8b42`) or a commit range using double dot\nnotation (e.g. `3a8b42..9ff173`).\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "topic", + "in": "query", + "description": "If true, returns 2-way 'three-dot' diff.\nThis is a diff between the source commit and the merge base\nof the source commit and the destination commit.\nIf false, a simple 'two dot' diff between the source and\ndestination is returned.", + "required": false, + "schema": { + "type": "boolean" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/downloads": { + "get": { + "responses": { + "200": { + "description": "Returns a paginated list of the downloads associated with the repository." + }, + "403": { + "description": "User is not authorized to read from the repository.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Downloads"], + "summary": "List download artifacts", + "security": [ + { + "oauth2": ["repository"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns a list of download links associated with the repository." + }, + "post": { + "responses": { + "201": { + "description": "The artifact was uploaded sucessfully." + }, + "400": { + "description": "Bad Request.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "403": { + "description": "User is not authorized to write to the repository.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "406": { + "description": "Unsupported Content-Type. Use multiplart/form-data.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Downloads"], + "summary": "Upload a download artifact", + "security": [ + { + "oauth2": ["repository:write"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Upload new download artifacts.\n\nTo upload files, perform a `multipart/form-data` POST containing one\nor more `files` fields:\n\n $ echo Hello World > hello.txt\n $ curl -s -u evzijst -X POST https://api.bitbucket.org/2.0/repositories/evzijst/git-tests/downloads -F files=@hello.txt\n\nWhen a file is uploaded with the same name as an existing artifact,\nthen the existing file will be replaced." + }, + "parameters": [ + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/downloads/{filename}": { + "delete": { + "responses": { + "204": { + "description": "The specified download artifact was deleted." + }, + "403": { + "description": "User is not authorized to write to the repository.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "The specified download does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Downloads"], + "summary": "Delete a download artifact", + "security": [ + { + "oauth2": ["repository:write"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Deletes the specified download artifact from the repository." + }, + "get": { + "responses": { + "302": { + "description": "Redirects to the url of the specified download artifact." + }, + "403": { + "description": "User is not authorized to read from the repository.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "The specified download artifact does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Downloads"], + "summary": "Get a download artifact link", + "security": [ + { + "oauth2": ["repository"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Return a redirect to the contents of a download artifact.\n\nThis endpoint returns the actual file contents and not the artifact's\nmetadata.\n\n $ curl -s -L https://api.bitbucket.org/2.0/repositories/evzijst/git-tests/downloads/hello.txt\n Hello World" + }, + "parameters": [ + { + "name": "filename", + "in": "path", + "description": "Name of the file.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/environments/": { + "post": { + "responses": { + "201": { + "headers": { + "Location": { + "description": "The URL of the newly created environment.", + "schema": { + "type": "string" + } + } + }, + "description": "The environment was created.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/deployment_environment" + } + } + } + }, + "404": { + "description": "The account or repository does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "409": { + "description": "An environment host with the provided name already exists.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "description": "Create an environment.", + "parameters": [ + { + "description": "This can either be the workspace ID (slug) or the workspace UUID surrounded by curly-braces, for example `{workspace UUID}`.", + "required": true, + "name": "workspace", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The repository.", + "required": true, + "name": "repo_slug", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/deployment_environment" + } + } + }, + "description": "The environment to create.", + "required": true + }, + "tags": ["Deployments"], + "summary": "Create an environment", + "operationId": "createEnvironment" + }, + "get": { + "responses": { + "200": { + "description": "The matching environments.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_environments" + } + } + } + } + }, + "description": "Find environments", + "parameters": [ + { + "description": "This can either be the workspace ID (slug) or the workspace UUID surrounded by curly-braces, for example `{workspace UUID}`.", + "required": true, + "name": "workspace", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The repository.", + "required": true, + "name": "repo_slug", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "tags": ["Deployments"], + "summary": "List environments", + "operationId": "getEnvironmentsForRepository" + } + }, + "/repositories/{workspace}/{repo_slug}/environments/{environment_uuid}": { + "get": { + "responses": { + "200": { + "description": "The environment.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/deployment_environment" + } + } + } + }, + "404": { + "description": "No account, repository or environment with the UUID provided exists.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "description": "Retrieve an environment", + "parameters": [ + { + "description": "This can either be the workspace ID (slug) or the workspace UUID surrounded by curly-braces, for example `{workspace UUID}`.", + "required": true, + "name": "workspace", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The repository.", + "required": true, + "name": "repo_slug", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The environment UUID.", + "required": true, + "name": "environment_uuid", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "tags": ["Deployments"], + "summary": "Get an environment", + "operationId": "getEnvironmentForRepository" + }, + "delete": { + "responses": { + "204": { + "description": "The environment was deleted." + }, + "404": { + "description": "No account or repository with the UUID provided exists.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "description": "Delete an environment", + "parameters": [ + { + "description": "This can either be the workspace ID (slug) or the workspace UUID surrounded by curly-braces, for example `{workspace UUID}`.", + "required": true, + "name": "workspace", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The repository.", + "required": true, + "name": "repo_slug", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The environment UUID.", + "required": true, + "name": "environment_uuid", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "tags": ["Deployments"], + "summary": "Delete an environment", + "operationId": "deleteEnvironmentForRepository" + } + }, + "/repositories/{workspace}/{repo_slug}/environments/{environment_uuid}/changes/": { + "post": { + "responses": { + "202": { + "description": "The environment update request was accepted." + }, + "404": { + "description": "No account, repository or environment with the UUID provided exists.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "description": "Update an environment", + "parameters": [ + { + "description": "This can either be the workspace ID (slug) or the workspace UUID surrounded by curly-braces, for example `{workspace UUID}`.", + "required": true, + "name": "workspace", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The repository.", + "required": true, + "name": "repo_slug", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The environment UUID.", + "required": true, + "name": "environment_uuid", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "tags": ["Deployments"], + "summary": "Update an environment", + "operationId": "updateEnvironmentForRepository" + } + }, + "/repositories/{workspace}/{repo_slug}/filehistory/{commit}/{path}": { + "get": { + "responses": { + "200": { + "description": "A paginated list of commits that modified the specified file", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_files" + } + } + } + }, + "404": { + "description": "If the repository does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "parameters": [ + { + "name": "renames", + "in": "query", + "description": "\nWhen `true`, Bitbucket will follow the history of the file across\nrenames (this is the default behavior). This can be turned off by\nspecifying `false`.", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "q", + "in": "query", + "description": "\nQuery string to narrow down the response as per\n[filtering and sorting](/cloud/bitbucket/rest/intro/#filtering).", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "sort", + "in": "query", + "description": "\nName of a response property sort the result by as per\n[filtering and sorting](/cloud/bitbucket/rest/intro/#sorting-query-results).\n", + "required": false, + "schema": { + "type": "string" + } + } + ], + "tags": ["Source", "Repositories"], + "summary": "List commits that modified a file", + "security": [ + { + "oauth2": ["repository"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns a paginated list of commits that modified the specified file.\n\nCommits are returned in reverse chronological order. This is roughly\nequivalent to the following commands:\n\n $ git log --follow --date-order \n\nBy default, Bitbucket will follow renames and the path name in the\nreturned entries reflects that. This can be turned off using the\n`?renames=false` query parameter.\n\nResults are returned in descending chronological order by default, and\nlike most endpoints you can\n[filter and sort](/cloud/bitbucket/rest/intro/#filtering) the response to\nonly provide exactly the data you want.\n\nFor example, if you wanted to find commits made before 2011-05-18\nagainst a file named `README.rst`, but you only wanted the path and\ndate, your query would look like this:\n\n```\n$ curl 'https://api.bitbucket.org/2.0/repositories/evzijst/dogslow/filehistory/master/README.rst'\\\n '?fields=values.next,values.path,values.commit.date&q=commit.date<=2011-05-18'\n{\n \"values\": [\n {\n \"commit\": {\n \"date\": \"2011-05-17T07:32:09+00:00\"\n },\n \"path\": \"README.rst\"\n },\n {\n \"commit\": {\n \"date\": \"2011-05-16T06:33:28+00:00\"\n },\n \"path\": \"README.txt\"\n },\n {\n \"commit\": {\n \"date\": \"2011-05-16T06:15:39+00:00\"\n },\n \"path\": \"README.txt\"\n }\n ]\n}\n```\n\nIn the response you can see that the file was renamed to `README.rst`\nby the commit made on 2011-05-16, and was previously named `README.txt`." + }, + "parameters": [ + { + "name": "commit", + "in": "path", + "description": "The commit's SHA1.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "path", + "in": "path", + "description": "Path to the file.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/forks": { + "get": { + "responses": { + "200": { + "description": "All forks.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_repositories" + } + } + } + } + }, + "parameters": [ + { + "name": "role", + "in": "query", + "description": "Filters the result based on the authenticated user's role on each repository.\n\n* **member**: returns repositories to which the user has explicit read access\n* **contributor**: returns repositories to which the user has explicit write access\n* **admin**: returns repositories to which the user has explicit administrator access\n* **owner**: returns all repositories owned by the current user\n", + "required": false, + "schema": { + "type": "string", + "enum": ["admin", "contributor", "member", "owner"] + } + }, + { + "name": "q", + "in": "query", + "description": "Query string to narrow down the response as per [filtering and sorting](/cloud/bitbucket/rest/intro/#filtering).\n", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "sort", + "in": "query", + "description": "Field by which the results should be sorted as per [filtering and sorting](/cloud/bitbucket/rest/intro/#filtering).\n", + "required": false, + "schema": { + "type": "string" + } + } + ], + "tags": ["Repositories"], + "summary": "List repository forks", + "security": [ + { + "oauth2": ["repository"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns a paginated list of all the forks of the specified\nrepository." + }, + "post": { + "responses": { + "201": { + "description": "The newly created fork.", + "headers": { + "Location": { + "description": "The URL of the newly created fork", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/repository" + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/repository" + } + } + }, + "description": "A repository object. This can be left blank." + }, + "tags": ["Repositories"], + "summary": "Fork a repository", + "security": [ + { + "oauth2": ["repository:write"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Creates a new fork of the specified repository.\n\n#### Forking a repository\n\nTo create a fork, specify the workspace explicitly as part of the\nrequest body:\n\n```\n$ curl -X POST -u jdoe https://api.bitbucket.org/2.0/repositories/atlassian/bbql/forks \\\n -H 'Content-Type: application/json' -d '{\n \"name\": \"bbql_fork\",\n \"workspace\": {\n \"slug\": \"atlassian\"\n }\n}'\n```\n\nTo fork a repository into the same workspace, also specify a new `name`.\n\nWhen you specify a value for `name`, it will also affect the `slug`.\nThe `slug` is reflected in the repository URL of the new fork. It is\nderived from `name` by substituting non-ASCII characters, removes\nwhitespace, and changes characters to lower case. For example,\n`My repo` would turn into `my_repo`.\n\nYou need contributor access to create new forks within a workspace.\n\n\n#### Change the properties of a new fork\n\nBy default the fork inherits most of its properties from the parent.\nHowever, since the optional POST body document follows the normal\n`repository` JSON schema and you can override the new fork's\nproperties.\n\nProperties that can be overridden include:\n\n* description\n* fork_policy\n* language\n* mainbranch\n* is_private (note that a private repo's fork_policy might prohibit\n the creation of public forks, in which `is_private=False` would fail)\n* has_issues (to initialize or disable the new repo's issue tracker --\n note that the actual contents of the parent repository's issue\n tracker are not copied during forking)\n* has_wiki (to initialize or disable the new repo's wiki --\n note that the actual contents of the parent repository's wiki are not\n copied during forking)\n* project (when forking into a private project, the fork's `is_private`\n must be `true`)\n\nProperties that cannot be modified include:\n\n* scm\n* parent\n* full_name" + }, + "parameters": [ + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/hooks": { + "get": { + "responses": { + "200": { + "description": "The paginated list of installed webhooks.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_webhook_subscriptions" + } + } + } + }, + "403": { + "description": "If the authenticated user does not have permission to access the webhooks.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "If the repository does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Repositories", "Webhooks"], + "summary": "List webhooks for a repository", + "security": [ + { + "oauth2": ["webhook"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns a paginated list of webhooks installed on this repository." + }, + "post": { + "responses": { + "201": { + "description": "If the webhook was registered successfully.", + "headers": { + "Location": { + "description": "The URL of new newly created webhook.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/webhook_subscription" + } + } + } + }, + "403": { + "description": "If the authenticated user does not have permission to install webhooks on the specified repository.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "If the repository does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Repositories", "Webhooks"], + "summary": "Create a webhook for a repository", + "security": [ + { + "oauth2": ["webhook"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Creates a new webhook on the specified repository.\n\nExample:\n\n```\n$ curl -X POST -u credentials -H 'Content-Type: application/json'\n https://api.bitbucket.org/2.0/repositories/my-workspace/my-repo-slug/hooks\n -d '\n {\n \"description\": \"Webhook Description\",\n \"url\": \"https://example.com/\",\n \"active\": true,\n \"events\": [\n \"repo:push\",\n \"issue:created\",\n \"issue:updated\"\n ]\n }'\n```\n\nNote that this call requires the webhook scope, as well as any scope\nthat applies to the events that the webhook subscribes to. In the\nexample above that means: `webhook`, `repository` and `issue`.\n\nAlso note that the `url` must properly resolve and cannot be an\ninternal, non-routed address." + }, + "parameters": [ + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/hooks/{uid}": { + "delete": { + "responses": { + "204": { + "description": "When the webhook was deleted successfully" + }, + "403": { + "description": "If the authenticated user does not have permission to delete the webhook.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "If the webhook or repository does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Repositories", "Webhooks"], + "summary": "Delete a webhook for a repository", + "security": [ + { + "oauth2": ["webhook"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Deletes the specified webhook subscription from the given\nrepository." + }, + "get": { + "responses": { + "200": { + "description": "The webhook subscription object.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/webhook_subscription" + } + } + } + }, + "404": { + "description": "If the webhook or repository does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Repositories", "Webhooks"], + "summary": "Get a webhook for a repository", + "security": [ + { + "oauth2": ["webhook"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns the webhook with the specified id installed on the specified\nrepository." + }, + "put": { + "responses": { + "200": { + "description": "The webhook subscription object.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/webhook_subscription" + } + } + } + }, + "403": { + "description": "If the authenticated user does not have permission to update the webhook.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "If the webhook or repository does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Repositories", "Webhooks"], + "summary": "Update a webhook for a repository", + "security": [ + { + "oauth2": ["webhook"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Updates the specified webhook subscription.\n\nThe following properties can be mutated:\n\n* `description`\n* `url`\n* `active`\n* `events`" + }, + "parameters": [ + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "uid", + "in": "path", + "description": "Installed webhook's ID", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/issues": { + "get": { + "responses": { + "200": { + "description": "A paginated list of the issues matching any filter criteria that were provided.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_issues" + } + } + } + }, + "404": { + "description": "The specified repository does not exist or does not have the issue tracker enabled.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Issue tracker"], + "summary": "List issues", + "security": [ + { + "oauth2": ["issue"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns the issues in the issue tracker." + }, + "post": { + "responses": { + "201": { + "description": "The newly created issue.", + "headers": { + "Location": { + "description": "The (absolute) URL of the newly created issue.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/issue" + } + } + } + }, + "401": { + "description": "When the request wasn't authenticated.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "403": { + "description": "When the authenticated user isn't authorized to create the issue.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "The specified repository does not exist or does not have the issue tracker enabled.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/issue" + } + } + }, + "description": "The new issue. The only required element is `title`. All other elements can be omitted from the body.", + "required": true + }, + "tags": ["Issue tracker"], + "summary": "Create an issue", + "security": [ + { + "oauth2": ["issue:write"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Creates a new issue.\n\nThis call requires authentication. Private repositories or private\nissue trackers require the caller to authenticate with an account that\nhas appropriate authorization.\n\nThe authenticated user is used for the issue's `reporter` field." + }, + "parameters": [ + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/issues/export": { + "post": { + "responses": { + "202": { + "description": "The export job has been accepted" + }, + "401": { + "description": "The request wasn't authenticated properly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "403": { + "description": "When the authenticated user does not have admin permission on the repo", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "The repo does not exist or does not have an issue tracker", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/export_options" + } + } + }, + "description": "The options to apply to the export. Available options include `project_key` and `project_name` which, if specified, are used as the project key and name in the exported Jira json format. Option `send_email` specifies whether an email should be sent upon export result. Option `include_attachments` specifies whether attachments are included in the export." + }, + "tags": ["Issue tracker"], + "summary": "Export issues", + "security": [ + { + "oauth2": ["issue", "repository:admin"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "A POST request to this endpoint initiates a new background celery task that archives the repo's issues.\n\nFor example, you can run:\n\ncurl -u -X POST http://api.bitbucket.org/2.0/repositories///\nissues/export\n\nWhen the job has been accepted, it will return a 202 (Accepted) along with a unique url to this job in the\n'Location' response header. This url is the endpoint for where the user can obtain their zip files.\"" + }, + "parameters": [ + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/issues/export/{repo_name}-issues-{task_id}.zip": { + "get": { + "responses": { + "202": { + "description": "Export job accepted", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/issue_job_status" + } + } + } + }, + "401": { + "description": "The request wasn't authenticated properly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "403": { + "description": "When the authenticated user does not have admin permission on the repo", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "No export job has begun", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Issue tracker"], + "summary": "Check issue export status", + "security": [ + { + "oauth2": ["issue", "repository:admin"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "This endpoint is used to poll for the progress of an issue export\njob and return the zip file after the job is complete.\nAs long as the job is running, this will return a 200 response\nwith in the response body a description of the current status.\n\nAfter the job has been scheduled, but before it starts executing, this\nendpoint's response is:\n\n{\n \"type\": \"issue_job_status\",\n \"status\": \"ACCEPTED\",\n \"phase\": \"Initializing\",\n \"total\": 0,\n \"count\": 0,\n \"pct\": 0\n}\n\n\nThen once it starts running, it becomes:\n\n{\n \"type\": \"issue_job_status\",\n \"status\": \"STARTED\",\n \"phase\": \"Attachments\",\n \"total\": 15,\n \"count\": 11,\n \"pct\": 73\n}\n\nOnce the job has successfully completed, it returns a stream of the zip file." + }, + "parameters": [ + { + "name": "repo_name", + "in": "path", + "description": "The name of the repo", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "task_id", + "in": "path", + "description": "The ID of the export task", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/issues/import": { + "get": { + "responses": { + "200": { + "description": "Import job complete with either FAILURE or SUCCESS status", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/issue_job_status" + } + } + } + }, + "202": { + "description": "Import job started", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/issue_job_status" + } + } + } + }, + "401": { + "description": "The request wasn't authenticated properly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "403": { + "description": "When the authenticated user does not have admin permission on the repo", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "No export job has begun", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Issue tracker"], + "summary": "Check issue import status", + "security": [ + { + "oauth2": ["issue:write", "repository:admin"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "When using GET, this endpoint reports the status of the current import task. Request example:\n\n```\n$ curl -u -X GET https://api.bitbucket.org/2.0/repositories///issues/import\n```\n\nAfter the job has been scheduled, but before it starts executing, this endpoint's response is:\n\n```\n< HTTP/1.1 202 Accepted\n{\n \"type\": \"issue_job_status\",\n \"status\": \"PENDING\",\n \"phase\": \"Attachments\",\n \"total\": 15,\n \"count\": 0,\n \"percent\": 0\n}\n```\n\nOnce it starts running, it is a 202 response with status STARTED and progress filled.\n\nAfter it is finished, it becomes a 200 response with status SUCCESS or FAILURE." + }, + "post": { + "responses": { + "202": { + "description": "Import job accepted", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/issue_job_status" + } + } + } + }, + "401": { + "description": "The request wasn't authenticated properly", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "403": { + "description": "When the authenticated user does not have admin permission on the repo", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "No export job has begun", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "409": { + "description": "Import already running", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Issue tracker"], + "summary": "Import issues", + "security": [ + { + "oauth2": ["issue:write", "repository:admin"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "A POST request to this endpoint will import the zip file given by the archive parameter into the repository. All\nexisting issues will be deleted and replaced by the contents of the imported zip file.\n\nImports are done through a multipart/form-data POST. There is one valid and required form field, with the name\n\"archive,\" which needs to be a file field:\n\n```\n$ curl -u -X POST -F archive=@/path/to/file.zip https://api.bitbucket.org/2.0/repositories///issues/import\n```\n\nWhen the import job is accepted, here is example output:\n\n```\n< HTTP/1.1 202 Accepted\n\n{\n \"type\": \"issue_job_status\",\n \"status\": \"ACCEPTED\",\n \"phase\": \"Attachments\",\n \"total\": 15,\n \"count\": 0,\n \"percent\": 0\n}\n```" + }, + "parameters": [ + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/issues/{issue_id}": { + "delete": { + "responses": { + "200": { + "description": "The issue object.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/issue" + } + } + } + }, + "403": { + "description": "When the authenticated user isn't authorized to delete the issue.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "The specified repository or issue does not exist or does not have the issue tracker enabled.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Issue tracker"], + "summary": "Delete an issue", + "security": [ + { + "oauth2": ["issue:write"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Deletes the specified issue. This requires write access to the\nrepository." + }, + "get": { + "responses": { + "200": { + "description": "The issue object.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/issue" + } + } + } + }, + "403": { + "description": "When the authenticated user isn't authorized to access the issue.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "The specified repository or issue does not exist or does not have the issue tracker enabled.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "410": { + "description": "The specified issue is unavailable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Issue tracker"], + "summary": "Get an issue", + "security": [ + { + "oauth2": ["issue"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns the specified issue." + }, + "put": { + "responses": { + "200": { + "description": "The updated issue object.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/issue" + } + } + } + }, + "403": { + "description": "When the authenticated user isn't authorized to access the issue.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "The specified repository or issue does not exist or does not have the issue tracker enabled.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Issue tracker"], + "summary": "Update an issue", + "security": [ + { + "oauth2": ["issue:write"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Modifies the issue.\n\n```\n$ curl https://api.bitbucket.org/2.0/repostories/evzijst/dogslow/issues/123 \\\n -u evzijst -s -X PUT -H 'Content-Type: application/json' \\\n -d '{\n \"title\": \"Updated title\",\n \"assignee\": {\n \"username\": \"evzijst\"\n },\n \"priority\": \"minor\",\n \"version\": {\n \"name\": \"1.0\"\n },\n \"component\": null\n}'\n```\n\nThis example changes the `title`, `assignee`, `priority` and the\n`version`. It also removes the value of the `component` from the issue\nby setting the field to `null`. Any field not present keeps its existing\nvalue.\n\nEach time an issue is edited in the UI or through the API, an immutable\nchange record is created under the `/issues/123/changes` endpoint. It\nalso has a comment associated with the change." + }, + "parameters": [ + { + "name": "issue_id", + "in": "path", + "description": "The issue id", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/issues/{issue_id}/attachments": { + "get": { + "responses": { + "200": { + "description": "A paginated list of all attachments for this issue.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_issue_attachments" + } + } + } + }, + "401": { + "description": "If the issue tracker is private and the request was not authenticated." + }, + "404": { + "description": "The specified repository or issue does not exist or does not have the issue tracker enabled.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Issue tracker"], + "summary": "List attachments for an issue", + "security": [ + { + "oauth2": ["issue"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns all attachments for this issue.\n\nThis returns the files' meta data. This does not return the files'\nactual contents.\n\nThe files are always ordered by their upload date." + }, + "post": { + "responses": { + "201": { + "description": "An empty response document.", + "headers": { + "Location": { + "description": "The URL to the issue's collection of attachments.", + "schema": { + "type": "string" + } + } + } + }, + "400": { + "description": "If no files were uploaded, or if the wrong `Content-Type` was used." + }, + "401": { + "description": "If the issue tracker is private and the request was not authenticated." + }, + "404": { + "description": "The specified repository or issue does not exist or does not have the issue tracker enabled.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Issue tracker"], + "summary": "Upload an attachment to an issue", + "security": [ + { + "oauth2": ["issue:write"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Upload new issue attachments.\n\nTo upload files, perform a `multipart/form-data` POST containing one\nor more file fields.\n\nWhen a file is uploaded with the same name as an existing attachment,\nthen the existing file will be replaced." + }, + "parameters": [ + { + "name": "issue_id", + "in": "path", + "description": "The issue id", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/issues/{issue_id}/attachments/{path}": { + "delete": { + "responses": { + "204": { + "description": "Indicates that the deletion was successful" + }, + "401": { + "description": "If the issue tracker is private and the request was not authenticated." + }, + "404": { + "description": "The specified repository or issue does not exist or does not have the issue tracker enabled.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Issue tracker"], + "summary": "Delete an attachment for an issue", + "security": [ + { + "oauth2": ["issue:write"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Deletes an attachment." + }, + "get": { + "responses": { + "302": { + "description": "A redirect to the file's contents", + "headers": { + "Location": { + "schema": { + "type": "string" + } + } + } + }, + "401": { + "description": "If the issue tracker is private and the request was not authenticated." + }, + "404": { + "description": "The specified repository or issue does not exist or does not have the issue tracker enabled.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Issue tracker"], + "summary": "Get attachment for an issue", + "security": [ + { + "oauth2": ["issue"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns the contents of the specified file attachment.\n\nNote that this endpoint does not return a JSON response, but instead\nreturns a redirect pointing to the actual file that in turn will return\nthe raw contents.\n\nThe redirect URL contains a one-time token that has a limited lifetime.\nAs a result, the link should not be persisted, stored, or shared." + }, + "parameters": [ + { + "name": "issue_id", + "in": "path", + "description": "The issue id", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "path", + "in": "path", + "description": "Path to the file.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/issues/{issue_id}/changes": { + "get": { + "responses": { + "200": { + "description": "Returns all the issue changes that were made on the specified issue.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_log_entries" + } + } + } + }, + "404": { + "description": "The specified repository or issue does not exist or does not have the issue tracker enabled.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "parameters": [ + { + "name": "q", + "in": "query", + "description": "\nQuery string to narrow down the response. See\n[filtering and sorting](/cloud/bitbucket/rest/intro/#filtering) for details.", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "sort", + "in": "query", + "description": "\nName of a response property to sort results. See\n[filtering and sorting](/cloud/bitbucket/rest/intro/#sorting-query-results)\nfor details.\n", + "required": false, + "schema": { + "type": "string" + } + } + ], + "tags": ["Issue tracker"], + "summary": "List changes on an issue", + "security": [ + { + "oauth2": ["issue"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns the list of all changes that have been made to the specified\nissue. Changes are returned in chronological order with the oldest\nchange first.\n\nEach time an issue is edited in the UI or through the API, an immutable\nchange record is created under the `/issues/123/changes` endpoint. It\nalso has a comment associated with the change.\n\nNote that this operation is changing significantly, due to privacy changes.\nSee the [announcement](https://developer.atlassian.com/cloud/bitbucket/bitbucket-api-changes-gdpr/#changes-to-the-issue-changes-api)\nfor details.\n\n```\n$ curl -s https://api.bitbucket.org/2.0/repositories/evzijst/dogslow/issues/1/changes - | jq .\n\n{\n \"pagelen\": 20,\n \"values\": [\n {\n \"changes\": {\n \"priority\": {\n \"new\": \"trivial\",\n \"old\": \"major\"\n },\n \"assignee\": {\n \"new\": \"\",\n \"old\": \"evzijst\"\n },\n \"assignee_account_id\": {\n \"new\": \"\",\n \"old\": \"557058:c0b72ad0-1cb5-4018-9cdc-0cde8492c443\"\n },\n \"kind\": {\n \"new\": \"enhancement\",\n \"old\": \"bug\"\n }\n },\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/evzijst/dogslow/issues/1/changes/2\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/evzijst/dogslow/issues/1#comment-2\"\n }\n },\n \"issue\": {\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/evzijst/dogslow/issues/1\"\n }\n },\n \"type\": \"issue\",\n \"id\": 1,\n \"repository\": {\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/evzijst/dogslow\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/evzijst/dogslow\"\n },\n \"avatar\": {\n \"href\": \"https://bitbucket.org/evzijst/dogslow/avatar/32/\"\n }\n },\n \"type\": \"repository\",\n \"name\": \"dogslow\",\n \"full_name\": \"evzijst/dogslow\",\n \"uuid\": \"{988b17c6-1a47-4e70-84ee-854d5f012bf6}\"\n },\n \"title\": \"Updated title\"\n },\n \"created_on\": \"2018-03-03T00:35:28.353630+00:00\",\n \"user\": {\n \"username\": \"evzijst\",\n \"nickname\": \"evzijst\",\n \"display_name\": \"evzijst\",\n \"type\": \"user\",\n \"uuid\": \"{aaa7972b-38af-4fb1-802d-6e3854c95778}\",\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/users/evzijst\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/evzijst/\"\n },\n \"avatar\": {\n \"href\": \"https://bitbucket.org/account/evzijst/avatar/32/\"\n }\n }\n },\n \"message\": {\n \"raw\": \"Removed assignee, changed kind and priority.\",\n \"markup\": \"markdown\",\n \"html\": \"

Removed assignee, changed kind and priority.

\",\n \"type\": \"rendered\"\n },\n \"type\": \"issue_change\",\n \"id\": 2\n }\n ],\n \"page\": 1\n}\n```\n\nChanges support [filtering and sorting](/cloud/bitbucket/rest/intro/#filtering) that\ncan be used to search for specific changes. For instance, to see\nwhen an issue transitioned to \"resolved\":\n\n```\n$ curl -s https://api.bitbucket.org/2.0/repositories/site/master/issues/1/changes \\\n -G --data-urlencode='q=changes.state.new = \"resolved\"'\n```\n\nThis resource is only available on repositories that have the issue\ntracker enabled.\n\nN.B.\n\nThe `changes.assignee` and `changes.assignee_account_id` fields are not\na `user` object. Instead, they contain the raw `username` and\n`account_id` of the user. This is to protect the integrity of the audit\nlog even after a user account gets deleted.\n\nThe `changes.assignee` field is deprecated will disappear in the\nfuture. Use `changes.assignee_account_id` instead." + }, + "post": { + "responses": { + "201": { + "description": "The newly created issue change.", + "headers": { + "Location": { + "description": "The (absolute) URL of the newly created issue change.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/issue_change" + } + } + } + }, + "401": { + "description": "When the request wasn't authenticated.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "403": { + "description": "When the authenticated user isn't authorized to modify the issue.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "The specified repository or issue does not exist or does not have the issue tracker enabled.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/issue_change" + } + } + }, + "description": "The new issue state change. The only required elements are `changes.[].new`. All other elements can be omitted from the body.", + "required": true + }, + "tags": ["Issue tracker"], + "summary": "Modify the state of an issue", + "security": [ + { + "oauth2": ["issue:write"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Makes a change to the specified issue.\n\nFor example, to change an issue's state and assignee, create a new\nchange object that modifies these fields:\n\n```\ncurl https://api.bitbucket.org/2.0/site/master/issues/1234/changes \\\n -s -u evzijst -X POST -H \"Content-Type: application/json\" \\\n -d '{\n \"changes\": {\n \"assignee_account_id\": {\n \"new\": \"557058:c0b72ad0-1cb5-4018-9cdc-0cde8492c443\"\n },\n \"state\": {\n \"new\": 'resolved\"\n }\n }\n \"message\": {\n \"raw\": \"This is now resolved.\"\n }\n }'\n```\n\nThe above example also includes a custom comment to go alongside the\nchange. This comment will also be visible on the issue page in the UI.\n\nThe fields of the `changes` object are strings, not objects. This\nallows for immutable change log records, even after user accounts,\nmilestones, or other objects recorded in a change entry, get renamed or\ndeleted.\n\nThe `assignee_account_id` field stores the account id. When POSTing a\nnew change and changing the assignee, the client should therefore use\nthe user's account_id in the `changes.assignee_account_id.new` field.\n\nThis call requires authentication. Private repositories or private\nissue trackers require the caller to authenticate with an account that\nhas appropriate authorization." + }, + "parameters": [ + { + "name": "issue_id", + "in": "path", + "description": "The issue id", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/issues/{issue_id}/changes/{change_id}": { + "get": { + "responses": { + "200": { + "description": "The specified issue change object.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/issue_change" + } + } + } + }, + "404": { + "description": "The specified repository or issue change does not exist or does not have the issue tracker enabled.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Issue tracker"], + "summary": "Get issue change object", + "security": [ + { + "oauth2": ["issue"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns the specified issue change object.\n\nThis resource is only available on repositories that have the issue\ntracker enabled." + }, + "parameters": [ + { + "name": "change_id", + "in": "path", + "description": "The issue change id", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "issue_id", + "in": "path", + "description": "The issue id", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/issues/{issue_id}/comments": { + "get": { + "responses": { + "200": { + "description": "A paginated list of issue comments.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_issue_comments" + } + } + } + } + }, + "parameters": [ + { + "name": "q", + "in": "query", + "description": "\nQuery string to narrow down the response as per\n[filtering and sorting](/cloud/bitbucket/rest/intro/#filtering).", + "required": false, + "schema": { + "type": "string" + } + } + ], + "tags": ["Issue tracker"], + "summary": "List comments on an issue", + "security": [ + { + "oauth2": ["issue"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns a paginated list of all comments that were made on the\nspecified issue.\n\nThe default sorting is oldest to newest and can be overridden with\nthe `sort` query parameter.\n\nThis endpoint also supports filtering and sorting of the results. See\n[filtering and sorting](/cloud/bitbucket/rest/intro/#filtering) for more details." + }, + "post": { + "responses": { + "201": { + "description": "The newly created comment.", + "headers": { + "Location": { + "description": "The location of the newly issue comment.", + "schema": { + "type": "string" + } + } + } + }, + "400": { + "description": "If the input was invalid, or if the comment being created is detected as spam ", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/issue_comment" + } + } + }, + "description": "The new issue comment object.", + "required": true + }, + "tags": ["Issue tracker"], + "summary": "Create a comment on an issue", + "security": [ + { + "oauth2": ["issue:write"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Creates a new issue comment.\n\n```\n$ curl https://api.bitbucket.org/2.0/repositories/atlassian/prlinks/issues/42/comments/ \\\n -X POST -u evzijst \\\n -H 'Content-Type: application/json' \\\n -d '{\"content\": {\"raw\": \"Lorem ipsum.\"}}'\n```" + }, + "parameters": [ + { + "name": "issue_id", + "in": "path", + "description": "The issue id", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/issues/{issue_id}/comments/{comment_id}": { + "delete": { + "responses": { + "204": { + "description": "Indicates successful deletion." + } + }, + "requestBody": { + "$ref": "#/components/requestBodies/issue_comment" + }, + "tags": ["Issue tracker"], + "summary": "Delete a comment on an issue", + "security": [ + { + "oauth2": ["issue:write"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Deletes the specified comment." + }, + "get": { + "responses": { + "200": { + "description": "The issue comment.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/issue_comment" + } + } + } + } + }, + "tags": ["Issue tracker"], + "summary": "Get a comment on an issue", + "security": [ + { + "oauth2": ["issue"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns the specified issue comment object." + }, + "put": { + "responses": { + "200": { + "description": "The updated issue comment.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/issue_comment" + } + } + } + }, + "400": { + "description": "If the input was invalid, or if the update to the comment is detected as spam ", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "requestBody": { + "$ref": "#/components/requestBodies/issue_comment" + }, + "tags": ["Issue tracker"], + "summary": "Update a comment on an issue", + "security": [ + { + "oauth2": ["issue:write"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Updates the content of the specified issue comment. Note that only\nthe `content.raw` field can be modified.\n\n```\n$ curl https://api.bitbucket.org/2.0/repositories/atlassian/prlinks/issues/42/comments/5728901 \\\n -X PUT -u evzijst \\\n -H 'Content-Type: application/json' \\\n -d '{\"content\": {\"raw\": \"Lorem ipsum.\"}'\n```" + }, + "parameters": [ + { + "name": "comment_id", + "in": "path", + "description": "The id of the comment.", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "name": "issue_id", + "in": "path", + "description": "The issue id", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/issues/{issue_id}/vote": { + "delete": { + "responses": { + "default": { + "description": "Unexpected error.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Issue tracker"], + "summary": "Remove vote for an issue", + "security": [ + { + "oauth2": ["account:write", "issue:write"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Retract your vote." + }, + "get": { + "responses": { + "204": { + "description": "If the authenticated user has not voted for this issue.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "401": { + "description": "When the request wasn't authenticated.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "If the authenticated user has not voted for this issue, or when the repo does not exist, or does not have an issue tracker.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Issue tracker"], + "summary": "Check if current user voted for an issue", + "security": [ + { + "oauth2": ["account", "issue"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Check whether the authenticated user has voted for this issue.\nA 204 status code indicates that the user has voted, while a 404\nimplies they haven't." + }, + "put": { + "responses": { + "204": { + "description": "Indicating the authenticated user has cast their vote successfully.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "401": { + "description": "When the request wasn't authenticated.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "The specified repository or issue does not exist or does not have the issue tracker enabled.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Issue tracker"], + "summary": "Vote for an issue", + "security": [ + { + "oauth2": ["account:write", "issue"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Vote for this issue.\n\nTo cast your vote, do an empty PUT. The 204 status code indicates that\nthe operation was successful." + }, + "parameters": [ + { + "name": "issue_id", + "in": "path", + "description": "The issue id", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/issues/{issue_id}/watch": { + "delete": { + "responses": { + "204": { + "description": "Indicates that the authenticated user successfully stopped watching this issue.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "401": { + "description": "When the request wasn't authenticated.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "The specified repository or issue does not exist or does not have the issue tracker enabled.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Issue tracker"], + "summary": "Stop watching an issue", + "security": [ + { + "oauth2": ["account:write", "issue:write"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Stop watching this issue." + }, + "get": { + "responses": { + "204": { + "description": "If the authenticated user is watching this issue.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "401": { + "description": "When the request wasn't authenticated.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "If the authenticated user is not watching this issue, or when the repo does not exist, or does not have an issue tracker.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Issue tracker"], + "summary": "Check if current user is watching a issue", + "security": [ + { + "oauth2": ["account", "issue"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Indicated whether or not the authenticated user is watching this\nissue." + }, + "put": { + "responses": { + "204": { + "description": "Indicates that the authenticated user successfully started watching this issue.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "401": { + "description": "When the request wasn't authenticated.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "If the authenticated user is not watching this issue, or when the repo does not exist, or does not have an issue tracker.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Issue tracker"], + "summary": "Watch an issue", + "security": [ + { + "oauth2": ["account:write", "issue"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Start watching this issue.\n\nTo start watching this issue, do an empty PUT. The 204 status code\nindicates that the operation was successful." + }, + "parameters": [ + { + "name": "issue_id", + "in": "path", + "description": "The issue id", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/merge-base/{revspec}": { + "get": { + "responses": { + "200": { + "description": "The merge base of the provided spec.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/commit" + } + } + } + }, + "401": { + "description": "If the request was not authenticated.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "403": { + "description": "If the authenticated user does not have access to any of the repositories specified.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "If the repository or ref in the spec does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Commits"], + "summary": "Get the common ancestor between two commits", + "security": [ + { + "oauth2": ["repository"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns the best common ancestor between two commits, specified in a revspec\nof 2 commits (e.g. 3a8b42..9ff173).\n\nIf more than one best common ancestor exists, only one will be returned. It is\nunspecified which will be returned." + }, + "parameters": [ + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "revspec", + "in": "path", + "description": "A commit range using double dot notation (e.g. `3a8b42..9ff173`).\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/milestones": { + "get": { + "responses": { + "200": { + "description": "The milestones that have been defined in the issue tracker.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_milestones" + } + } + } + }, + "404": { + "description": "The specified repository does not exist or does not have the issue tracker enabled.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Issue tracker"], + "summary": "List milestones", + "security": [ + { + "oauth2": ["issue"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns the milestones that have been defined in the issue tracker.\n\nThis resource is only available on repositories that have the issue\ntracker enabled." + }, + "parameters": [ + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/milestones/{milestone_id}": { + "get": { + "responses": { + "200": { + "description": "The specified milestone object.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/milestone" + } + } + } + }, + "404": { + "description": "The specified repository or milestone does not exist or does not have the issue tracker enabled.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Issue tracker"], + "summary": "Get a milestone", + "security": [ + { + "oauth2": ["issue"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns the specified issue tracker milestone object." + }, + "parameters": [ + { + "name": "milestone_id", + "in": "path", + "description": "The milestone's id", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/patch/{spec}": { + "get": { + "responses": { + "200": { + "description": "The raw patches" + }, + "555": { + "description": "If the diff was too large and timed out.\n\nSince this endpoint does not employ any form of pagination, but\ninstead returns the diff as a single document, it can run into\ntrouble on very large diffs. If Bitbucket times out in cases\nlike these, a 555 status code is returned.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Commits"], + "summary": "Get a patch for two commits", + "security": [ + { + "oauth2": ["repository"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Produces a raw patch for a single commit (diffed against its first\nparent), or a patch-series for a revspec of 2 commits (e.g.\n`3a8b42..9ff173` where the first commit represents the source and the\nsecond commit the destination).\n\nIn case of the latter (diffing a revspec), a patch series is returned\nfor the commits on the source branch (`3a8b42` and its ancestors in\nour example).\n\nWhile similar to diffs, patches:\n\n* Have a commit header (username, commit message, etc)\n* Do not support the `path=foo/bar.py` query parameter\n\nThe raw patch is returned as-is, in whatever encoding the files in the\nrepository use. It is not decoded into unicode. As such, the\ncontent-type is `text/plain`." + }, + "parameters": [ + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "spec", + "in": "path", + "description": "A commit SHA (e.g. `3a8b42`) or a commit range using double dot\nnotation (e.g. `3a8b42..9ff173`).\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/permissions-config/groups": { + "get": { + "responses": { + "200": { + "description": "Paginated of explicit group permissions on the repository.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_repository_group_permissions" + } + } + } + }, + "401": { + "description": "The user couldn't be authenticated.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "403": { + "description": "The requesting user isn't an admin of the repository.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "One or both of the workspace and repository doesn't exist for the given identifiers.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Repositories"], + "summary": "List explicit group permissions for a repository", + "security": [ + { + "oauth2": ["repository:admin"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns a paginated list of explicit group permissions for the given repository.\nThis endpoint does not support BBQL features.\n\nExample:\n\n```\n$ curl https://api.bitbucket.org/2.0/repositories/atlassian_tutorial/geordi/permissions-config/groups\n\nHTTP/1.1 200\nLocation: https://api.bitbucket.org/2.0/repositories/atlassian_tutorial/geordi/permissions-config/groups\n\n{\n \"pagelen\": 10,\n \"values\": [\n {\n \"type\": \"repository_group_permission\",\n \"group\": {\n \"type\": \"group\",\n \"name\": \"Administrators\",\n \"slug\": \"administrators\"\n },\n \"permission\": \"admin\",\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/atlassian_tutorial/\n geordi/permissions-config/groups/administrators\"\n }\n }\n },\n {\n \"type\": \"repository_group_permission\",\n \"group\": {\n \"type\": \"group\",\n \"name\": \"Developers\",\n \"slug\": \"developers\"\n },\n \"permission\": \"read\",\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/atlassian_tutorial/\n geordi/permissions-config/groups/developers\"\n }\n }\n }\n ],\n \"page\": 1,\n \"size\": 2\n}\n```" + }, + "parameters": [ + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/permissions-config/groups/{group_slug}": { + "delete": { + "responses": { + "204": { + "description": "Group permission deleted" + }, + "401": { + "description": "The user couldn't be authenticated.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "403": { + "description": "The requesting user isn't an admin of the repository, or the authentication method was not via app password.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "The workspace does not exist, the repository does not exist,or the group does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Repositories"], + "summary": "Delete an explicit group permission for a repository", + "security": [ + { + "oauth2": ["repository:admin"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Deletes the repository group permission between the requested repository and group, if one exists.\n\nOnly users with admin permission for the repository may access this resource.\n\nExample:\n\n$ curl -X DELETE https://api.bitbucket.org/2.0/repositories/atlassian_tutorial\n/geordi/permissions-config/groups/developers\n\n\nHTTP/1.1 204" + }, + "get": { + "responses": { + "200": { + "description": "Group permission for group slug and repository", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/repository_group_permission" + } + } + } + }, + "401": { + "description": "The user couldn't be authenticated.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "403": { + "description": "The requesting user isn't an admin of the repository.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "The given user, workspace, and/or repository could not be found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Repositories"], + "summary": "Get an explicit group permission for a repository", + "security": [ + { + "oauth2": ["repository:admin"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns the group permission for a given group slug and repository\n\nOnly users with admin permission for the repository may access this resource.\n\nPermissions can be:\n\n* `admin`\n* `write`\n* `read`\n* `none`\n\nExample:\n\n```\n$ curl https://api.bitbucket.org/2.0/repositories/atlassian_tutorial/geordi/permissions-config/groups/developers\n\nHTTP/1.1 200\nLocation:\nhttps://api.bitbucket.org/2.0/repositories/atlassian_tutorial/geordi/permissions-config/groups/developers\n\n{\n \"type\": \"repository_group_permission\",\n \"group\": {\n \"type\": \"group\",\n \"name\": \"Developers\",\n \"slug\": \"developers\"\n },\n \"repository\": {\n \"type\": \"repository\",\n \"name\": \"geordi\",\n \"full_name\": \"atlassian_tutorial/geordi\",\n \"uuid\": \"{85d08b4e-571d-44e9-a507-fa476535aa98}\"\n },\n \"permission\": \"read\",\n \"links\": {\n \"self\": {\n \"href\":\n \"https://api.bitbucket.org/2.0/repositories/atlassian_tutorial/geordi/permissions-config/groups/developers\"\n }\n }\n}\n```" + }, + "put": { + "responses": { + "200": { + "description": "Group permission updated", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/repository_group_permission" + } + } + } + }, + "400": { + "description": "No permission value was provided or the value is invalid(not one of read, write, or admin)", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "401": { + "description": "The user couldn't be authenticated.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "403": { + "description": "The requesting user isn't an admin of the repository, or the authentication method was not via app password.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "The workspace does not exist, the repository does not exist,or the group does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Repositories"], + "summary": "Update an explicit group permission for a repository", + "security": [ + { + "oauth2": ["repository:admin"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Updates the group permission if it exists.\n\nOnly users with admin permission for the repository may access this resource.\n\nThe only authentication method supported for this endpoint is via app passwords.\n\nPermissions can be:\n\n* `admin`\n* `write`\n* `read`\n\nExample:\n```\n$ curl -X PUT -H \"Content-Type: application/json\"\nhttps://api.bitbucket.org/2.0/repositories/atlassian_tutorial/geordi/permissions-config/groups/developers\n-d\n'{\n \"permission\": \"write\"\n}'\n\nHTTP/1.1 200\nLocation:\nhttps://api.bitbucket.org/2.0/repositories/atlassian_tutorial/geordi/permissions-config/groups/developers\n\n{\n \"type\": \"repository_group_permission\",\n \"group\": {\n \"type\": \"group\",\n \"name\": \"Developers\",\n \"slug\": \"developers\"\n },\n \"repository\": {\n \"type\": \"repository\",\n \"name\": \"geordi\",\n \"full_name\": \"atlassian_tutorial/geordi\",\n \"uuid\": \"{85d08b4e-571d-44e9-a507-fa476535aa98}\"\n },\n \"permission\": \"write\",\n \"links\": {\n \"self\": {\n \"href\":\n \"https://api.bitbucket.org/2.0/repositories/atlassian_tutorial/geordi/permissions-config/groups/developers\"\n }\n }\n}\n```" + }, + "parameters": [ + { + "name": "group_slug", + "in": "path", + "description": "Slug of the requested group.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/permissions-config/users": { + "get": { + "responses": { + "200": { + "description": "Paginated of explicit user permissions on the repository.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_repository_user_permissions" + } + } + } + }, + "401": { + "description": "The user couldn't be authenticated.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "403": { + "description": "The requesting user isn't an admin of the repository.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "No repository exists for the given repo slug and workspace.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Repositories"], + "summary": "List explicit user permissions for a repository", + "security": [ + { + "oauth2": ["repository:admin"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns a paginated list of explicit user permissions for the given repository.\n\nExample:\n\n```\n$ curl https://api.bitbucket.org/2.0/repositories/atlassian_tutorial/geordi/permissions-config/users\n\n{\n \"pagelen\": 10,\n \"values\": [\n {\n \"type\": \"repository_user_permission\",\n \"user\": {\n \"type\": \"user\",\n \"display_name\": \"Colin Cameron\",\n \"uuid\": \"{d301aafa-d676-4ee0-88be-962be7417567}\",\n \"account_id\": \"557058:ba8948b2-49da-43a9-9e8b-e7249b8e324a\"\n },\n \"permission\": \"admin\",\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/atlassian_tutorial/geordi/\n permissions-config/users/557058:ba8948b2-49da-43a9-9e8b-e7249b8e324a\"\n }\n }\n },\n {\n \"type\": \"repository_user_permission\",\n \"user\": {\n \"type\": \"user\",\n \"display_name\": \"Sean Conaty\",\n \"uuid\": \"{504c3b62-8120-4f0c-a7bc-87800b9d6f70}\",\n \"account_id\": \"557058:ba8948b2-49da-43a9-9e8b-e7249b8e324c\"\n },\n \"permission\": \"write\",\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0//repositories/atlassian_tutorial/geordi/\n permissions-config/users/557058:ba8948b2-49da-43a9-9e8b-e7249b8e324c\"\n }\n }\n }\n ],\n \"page\": 1,\n \"size\": 2\n}\n```" + }, + "parameters": [ + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/permissions-config/users/{selected_user_id}": { + "delete": { + "responses": { + "204": { + "description": "The repository user permission was deleted and no content returned." + }, + "401": { + "description": "The user couldn't be authenticated.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "403": { + "description": "The requesting user isn't an admin of the repository, or the authentication method was not via app password.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "One or more of the workspace, repository, and user doesn't exist for the given identifiers.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Repositories"], + "summary": "Delete an explicit user permission for a repository", + "security": [ + { + "oauth2": ["repository:admin"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Deletes the repository user permission between the requested repository and user, if one exists.\n\nOnly users with admin permission for the repository may access this resource.\n\nThe only authentication method for this endpoint is via app passwords.\n\n```\n$ curl -X DELETE https://api.bitbucket.org/2.0/repositories/atlassian_tutorial/geordi/\npermissions-config/users/557058:ba8948b2-49da-43a9-9e8b-e7249b8e324a\n\n\nHTTP/1.1 204\n```" + }, + "get": { + "responses": { + "200": { + "description": "Explicit user permission for user and repository", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/repository_user_permission" + } + } + } + }, + "401": { + "description": "The user couldn't be authenticated.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "403": { + "description": "The requesting user isn't an admin of the repository.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "One or both of the workspace and repository doesn't exist for the given identifiers.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Repositories"], + "summary": "Get an explicit user permission for a repository", + "security": [ + { + "oauth2": ["repository:admin"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns the explicit user permission for a given user and repository.\n\nOnly users with admin permission for the repository may access this resource.\n\nPermissions can be:\n\n* `admin`\n* `write`\n* `read`\n* `none`\n\nExample:\n\n```\n$ curl 'https://api.bitbucket.org/2.0/repositories/atlassian_tutorial/geordi/\n permissions-config/users/557058:ba8948b2-49da-43a9-9e8b-e7249b8e324a'\n\nHTTP/1.1 200\nLocation: 'https://api.bitbucket.org/2.0/repositories/atlassian_tutorial/geordi/\n permissions-config/users/557058:ba8948b2-49da-43a9-9e8b-e7249b8e324a'\n\n{\n \"type\": \"repository_user_permission\",\n \"user\": {\n \"type\": \"user\",\n \"display_name\": \"Colin Cameron\",\n \"uuid\": \"{d301aafa-d676-4ee0-88be-962be7417567}\",\n \"account_id\": \"557058:ba8948b2-49da-43a9-9e8b-e7249b8e324a\"\n },\n \"repository\": {\n \"type\": \"repository\",\n \"name\": \"geordi\",\n \"full_name\": \"atlassian_tutorial/geordi\",\n \"uuid\": \"{85d08b4e-571d-44e9-a507-fa476535aa98}\"\n },\n \"permission\": \"admin\",\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/atlassian_tutorial/geordi/\n permissions-config/users/557058:ba8948b2-49da-43a9-9e8b-e7249b8e324a\"\n }\n }\n}\n```" + }, + "put": { + "responses": { + "200": { + "description": "Explicit user permission updated", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/repository_user_permission" + } + } + } + }, + "400": { + "description": "No permission value was provided or the value is invalid (not one of read, write, or admin), or the selected user is not a valid user to update.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "401": { + "description": "The user couldn't be authenticated.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "403": { + "description": "The requesting user isn't an admin of the repository, or the authentication method was not via app password.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "One or more of the workspace, repo, and selected user doesn't exist for the given identifiers.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Repositories"], + "summary": "Update an explicit user permission for a repository", + "security": [ + { + "oauth2": ["repository:admin"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Updates the explicit user permission for a given user and repository. The selected user must be a member of\nthe workspace, and cannot be the workspace owner.\nOnly users with admin permission for the repository may access this resource.\n\nThe only authentication method for this endpoint is via app passwords.\n\nPermissions can be:\n\n* `admin`\n* `write`\n* `read`\n\nExample:\n\n```\n$ curl -X PUT -H \"Content-Type: application/json\" 'https://api.bitbucket.org/2.0/repositories/\natlassian_tutorial/geordi/permissions-config/users/557058:ba8948b2-49da-43a9-9e8b-e7249b8e324a'\n-d '{\n \"permission\": \"write\"\n}'\n\nHTTP/1.1 200\nLocation: 'https://api.bitbucket.org/2.0/repositories/atlassian_tutorial/geordi/\npermissions-config/users/557058:ba8948b2-49da-43a9-9e8b-e7249b8e324a'\n\n\n{\n \"type\": \"repository_user_permission\",\n \"user\": {\n \"type\": \"user\",\n \"display_name\": \"Colin Cameron\",\n \"uuid\": \"{d301aafa-d676-4ee0-88be-962be7417567}\",\n \"account_id\": \"557058:ba8948b2-49da-43a9-9e8b-e7249b8e324a\"\n },\n \"repository\": {\n \"type\": \"repository\",\n \"name\": \"geordi\",\n \"full_name\": \"atlassian_tutorial/geordi\",\n \"uuid\": \"{85d08b4e-571d-44e9-a507-fa476535aa98}\"\n },\n \"permission\": \"write\",\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/atlassian_tutorial/geordi/\n permissions-config/users/557058:ba8948b2-49da-43a9-9e8b-e7249b8e324a\"\n }\n }\n}\n```" + }, + "parameters": [ + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "selected_user_id", + "in": "path", + "description": "This can either be the UUID of the account, surrounded by curly-braces, for\nexample: `{account UUID}`, OR an Atlassian Account ID.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/pipelines-config/caches/": { + "get": { + "responses": { + "200": { + "description": "The list of caches for the given repository.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_pipeline_caches" + } + } + } + }, + "404": { + "description": "The account or repository was not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "description": "Retrieve the repository pipelines caches.", + "parameters": [ + { + "description": "The account.", + "required": true, + "name": "workspace", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The repository.", + "required": true, + "name": "repo_slug", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "tags": ["Pipelines"], + "summary": "List caches", + "operationId": "getRepositoryPipelineCaches" + } + }, + "/repositories/{workspace}/{repo_slug}/pipelines-config/caches/{cache_uuid}": { + "delete": { + "responses": { + "204": { + "description": "The cache was deleted." + }, + "404": { + "description": "The workspace, repository or cache_uuid with given UUID was not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "description": "Delete a repository cache.", + "parameters": [ + { + "description": "The account.", + "required": true, + "name": "workspace", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The repository.", + "required": true, + "name": "repo_slug", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The UUID of the cache to delete.", + "required": true, + "name": "cache_uuid", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "tags": ["Pipelines"], + "summary": "Delete a cache", + "operationId": "deleteRepositoryPipelineCache" + } + }, + "/repositories/{workspace}/{repo_slug}/pipelines-config/caches/{cache_uuid}/content-uri": { + "get": { + "responses": { + "200": { + "description": "The cache content uri.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/pipeline_cache_content_uri" + } + } + } + }, + "404": { + "description": "The workspace, repository or cache_uuid with given UUID was not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "description": "Retrieve the URI of the content of the specified cache.", + "parameters": [ + { + "description": "The account.", + "required": true, + "name": "workspace", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The repository.", + "required": true, + "name": "repo_slug", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The UUID of the cache.", + "required": true, + "name": "cache_uuid", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "tags": ["Pipelines"], + "summary": "Get cache content URI", + "operationId": "getRepositoryPipelineCacheContentURI" + } + }, + "/repositories/{workspace}/{repo_slug}/pipelines/": { + "post": { + "responses": { + "201": { + "headers": { + "Location": { + "description": "The URL of the newly created pipeline.", + "schema": { + "type": "string" + } + } + }, + "description": "The initiated pipeline.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/pipeline" + } + } + } + }, + "400": { + "description": "The account or repository is not enabled, the yml file does not exist in the repository for the given revision, or the request body contained invalid properties.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "The account or repository was not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "description": "Endpoint to create and initiate a pipeline.\nThere are a couple of different options to initiate a pipeline, where the payload of the request will determine which type of pipeline will be instantiated.\n# Trigger a Pipeline for a branch\nOne way to trigger pipelines is by specifying the branch for which you want to trigger a pipeline.\nThe specified branch will be used to determine which pipeline definition from the `bitbucket-pipelines.yml` file will be applied to initiate the pipeline. The pipeline will then do a clone of the repository and checkout the latest revision of the specified branch.\n\n### Example\n\n```\n$ curl -X POST -is -u username:password \\\n -H 'Content-Type: application/json' \\\n https://api.bitbucket.org/2.0/repositories/jeroendr/meat-demo2/pipelines/ \\\n -d '\n {\n \"target\": {\n \"ref_type\": \"branch\",\n \"type\": \"pipeline_ref_target\",\n \"ref_name\": \"master\"\n }\n }'\n```\n# Trigger a Pipeline for a commit on a branch or tag\nYou can initiate a pipeline for a specific commit and in the context of a specified reference (e.g. a branch, tag or bookmark).\nThe specified reference will be used to determine which pipeline definition from the bitbucket-pipelines.yml file will be applied to initiate the pipeline. The pipeline will clone the repository and then do a checkout the specified reference.\n\nThe following reference types are supported:\n\n* `branch`\n* `named_branch`\n* `bookmark`\n * `tag`\n\n### Example\n\n```\n$ curl -X POST -is -u username:password \\\n -H 'Content-Type: application/json' \\\n https://api.bitbucket.org/2.0/repositories/jeroendr/meat-demo2/pipelines/ \\\n -d '\n {\n \"target\": {\n \"commit\": {\n \"type\": \"commit\",\n \"hash\": \"ce5b7431602f7cbba007062eeb55225c6e18e956\"\n },\n \"ref_type\": \"branch\",\n \"type\": \"pipeline_ref_target\",\n \"ref_name\": \"master\"\n }\n }'\n```\n# Trigger a specific pipeline definition for a commit\nYou can trigger a specific pipeline that is defined in your `bitbucket-pipelines.yml` file for a specific commit.\nIn addition to the commit revision, you specify the type and pattern of the selector that identifies the pipeline definition. The resulting pipeline will then clone the repository and checkout the specified revision.\n\n### Example\n\n```\n$ curl -X POST -is -u username:password \\\n -H 'Content-Type: application/json' \\\n https://api.bitbucket.org/2.0/repositories/jeroendr/meat-demo2/pipelines/ \\\n -d '\n {\n \"target\": {\n \"commit\": {\n \"hash\":\"a3c4e02c9a3755eccdc3764e6ea13facdf30f923\",\n \"type\":\"commit\"\n },\n \"selector\": {\n \"type\":\"custom\",\n \"pattern\":\"Deploy to production\"\n },\n \"type\":\"pipeline_commit_target\"\n }\n }'\n```\n# Trigger a specific pipeline definition for a commit on a branch or tag\nYou can trigger a specific pipeline that is defined in your `bitbucket-pipelines.yml` file for a specific commit in the context of a specified reference.\nIn addition to the commit revision, you specify the type and pattern of the selector that identifies the pipeline definition, as well as the reference information. The resulting pipeline will then clone the repository a checkout the specified reference.\n\n### Example\n\n```\n$ curl -X POST -is -u username:password \\\n -H 'Content-Type: application/json' \\\n https://api.bitbucket.org/2.0/repositories/jeroendr/meat-demo2/pipelines/ \\\n -d '\n {\n \"target\": {\n \"commit\": {\n \"hash\":\"a3c4e02c9a3755eccdc3764e6ea13facdf30f923\",\n \"type\":\"commit\"\n },\n \"selector\": {\n \"type\": \"custom\",\n \"pattern\": \"Deploy to production\"\n },\n \"type\": \"pipeline_ref_target\",\n \"ref_name\": \"master\",\n \"ref_type\": \"branch\"\n }\n }'\n```\n\n\n# Trigger a custom pipeline with variables\nIn addition to triggering a custom pipeline that is defined in your `bitbucket-pipelines.yml` file as shown in the examples above, you can specify variables that will be available for your build. In the request, provide a list of variables, specifying the following for each variable: key, value, and whether it should be secured or not (this field is optional and defaults to not secured).\n\n### Example\n\n```\n$ curl -X POST -is -u username:password \\\n -H 'Content-Type: application/json' \\\n https://api.bitbucket.org/2.0/repositories/{workspace}/{repo_slug}/pipelines/ \\\n -d '\n {\n \"target\": {\n \"type\": \"pipeline_ref_target\",\n \"ref_type\": \"branch\",\n \"ref_name\": \"master\",\n \"selector\": {\n \"type\": \"custom\",\n \"pattern\": \"Deploy to production\"\n }\n },\n \"variables\": [\n {\n \"key\": \"var1key\",\n \"value\": \"var1value\",\n \"secured\": true\n },\n {\n \"key\": \"var2key\",\n \"value\": \"var2value\"\n }\n ]\n }'\n```\n\n# Trigger a pull request pipeline\n\nYou can also initiate a pipeline for a specific pull request.\n\n### Example\n\n```\n$ curl -X POST -is -u username:password \\\n -H 'Content-Type: application/json' \\\n https://api.bitbucket.org/2.0/repositories/{workspace}/{repo_slug}/pipelines/ \\\n -d '\n {\n\t\"target\": {\n \"type\": \"pipeline_pullrequest_target\",\n\t \"source\": \"pull-request-branch\",\n \"destination\": \"master\",\n \"destination_commit\": {\n \t \"hash\" : \"9f848b7\"\n },\n \"commit\": {\n \t\"hash\" : \"1a372fc\"\n },\n \"pullrequest\" : {\n \t\"id\" : \"3\"\n },\n\t \"selector\": {\n \"type\": \"pull-requests\",\n \"pattern\": \"**\"\n }\n }\n }'\n```\n", + "parameters": [ + { + "description": "This can either be the workspace ID (slug) or the workspace UUID surrounded by curly-braces, for example `{workspace UUID}`.", + "required": true, + "name": "workspace", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The repository.", + "required": true, + "name": "repo_slug", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/pipeline" + } + } + }, + "description": "The pipeline to initiate.", + "required": true + }, + "tags": ["Pipelines"], + "summary": "Run a pipeline", + "operationId": "createPipelineForRepository" + }, + "get": { + "responses": { + "200": { + "description": "The matching pipelines.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_pipelines" + } + } + } + } + }, + "description": "Find pipelines", + "parameters": [ + { + "description": "This can either be the workspace ID (slug) or the workspace UUID surrounded by curly-braces, for example `{workspace UUID}`.", + "required": true, + "name": "workspace", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The repository.", + "required": true, + "name": "repo_slug", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "tags": ["Pipelines"], + "summary": "List pipelines", + "operationId": "getPipelinesForRepository" + } + }, + "/repositories/{workspace}/{repo_slug}/pipelines/{pipeline_uuid}": { + "get": { + "responses": { + "200": { + "description": "The pipeline.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/pipeline" + } + } + } + }, + "404": { + "description": "No account, repository or pipeline with the UUID provided exists.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "description": "Retrieve a specified pipeline", + "parameters": [ + { + "description": "This can either be the workspace ID (slug) or the workspace UUID surrounded by curly-braces, for example `{workspace UUID}`.", + "required": true, + "name": "workspace", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The repository.", + "required": true, + "name": "repo_slug", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The pipeline UUID.", + "required": true, + "name": "pipeline_uuid", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "tags": ["Pipelines"], + "summary": "Get a pipeline", + "operationId": "getPipelineForRepository" + } + }, + "/repositories/{workspace}/{repo_slug}/pipelines/{pipeline_uuid}/steps/": { + "get": { + "responses": { + "200": { + "description": "The steps.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_pipeline_steps" + } + } + } + } + }, + "description": "Find steps for the given pipeline.", + "parameters": [ + { + "description": "This can either be the workspace ID (slug) or the workspace UUID surrounded by curly-braces, for example `{workspace UUID}`.", + "required": true, + "name": "workspace", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The repository.", + "required": true, + "name": "repo_slug", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The UUID of the pipeline.", + "required": true, + "name": "pipeline_uuid", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "tags": ["Pipelines"], + "summary": "List steps for a pipeline", + "operationId": "getPipelineStepsForRepository" + } + }, + "/repositories/{workspace}/{repo_slug}/pipelines/{pipeline_uuid}/steps/{step_uuid}": { + "get": { + "responses": { + "200": { + "description": "The step.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/pipeline_step" + } + } + } + }, + "404": { + "description": "No account, repository, pipeline or step with the UUID provided exists for the pipeline with the UUID provided.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "description": "Retrieve a given step of a pipeline.", + "parameters": [ + { + "description": "This can either be the workspace ID (slug) or the workspace UUID surrounded by curly-braces, for example `{workspace UUID}`.", + "required": true, + "name": "workspace", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The repository.", + "required": true, + "name": "repo_slug", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The UUID of the pipeline.", + "required": true, + "name": "pipeline_uuid", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The UUID of the step.", + "required": true, + "name": "step_uuid", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "tags": ["Pipelines"], + "summary": "Get a step of a pipeline", + "operationId": "getPipelineStepForRepository" + } + }, + "/repositories/{workspace}/{repo_slug}/pipelines/{pipeline_uuid}/steps/{step_uuid}/log": { + "get": { + "responses": { + "200": { + "description": "The raw log file for this pipeline step." + }, + "304": { + "description": "The log has the same etag as the provided If-None-Match header.", + "content": { + "application/octet-stream": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "A pipeline with the given UUID does not exist, a step with the given UUID does not exist in the pipeline or a log file does not exist for the given step.", + "content": { + "application/octet-stream": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "416": { + "description": "The requested range does not exist for requests that specified the [HTTP Range header](https://tools.ietf.org/html/rfc7233#section-3.1).", + "content": { + "application/octet-stream": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "parameters": [ + { + "description": "This can either be the workspace ID (slug) or the workspace UUID surrounded by curly-braces, for example `{workspace UUID}`.", + "required": true, + "name": "workspace", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The repository.", + "required": true, + "name": "repo_slug", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The UUID of the pipeline.", + "required": true, + "name": "pipeline_uuid", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The UUID of the step.", + "required": true, + "name": "step_uuid", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "tags": ["Pipelines"], + "summary": "Get log file for a step", + "operationId": "getPipelineStepLogForRepository", + "description": "Retrieve the log file for a given step of a pipeline.\n\nThis endpoint supports (and encourages!) the use of [HTTP Range requests](https://tools.ietf.org/html/rfc7233) to deal with potentially very large log files." + } + }, + "/repositories/{workspace}/{repo_slug}/pipelines/{pipeline_uuid}/steps/{step_uuid}/logs/{log_uuid}": { + "get": { + "responses": { + "200": { + "description": "The raw log file for the build container or service container." + }, + "404": { + "description": "No account, repository, pipeline, step or log exist for the provided path.", + "content": { + "application/octet-stream": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "parameters": [ + { + "description": "This can either be the workspace ID (slug) or the workspace UUID surrounded by curly-braces, for example `{workspace UUID}`.", + "required": true, + "name": "workspace", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The repository.", + "required": true, + "name": "repo_slug", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The UUID of the pipeline.", + "required": true, + "name": "pipeline_uuid", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The UUID of the step.", + "required": true, + "name": "step_uuid", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "For the main build container specify the step UUID; for a service container specify the service container UUID", + "required": true, + "name": "log_uuid", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "tags": ["Pipelines"], + "summary": "Get the logs for the build container or a service container for a given step of a pipeline.", + "operationId": "getPipelineContainerLog", + "description": "Retrieve the log file for a build container or service container.\n\nThis endpoint supports (and encourages!) the use of [HTTP Range requests](https://tools.ietf.org/html/rfc7233) to deal with potentially very large log files." + } + }, + "/repositories/{workspace}/{repo_slug}/pipelines/{pipeline_uuid}/steps/{step_uuid}/test_reports": { + "get": { + "responses": { + "200": { + "description": "A summary of test reports for this pipeline step." + }, + "404": { + "description": "No account, repository, pipeline, step or test reports exist for the provided path.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "parameters": [ + { + "description": "This can either be the workspace ID (slug) or the workspace UUID surrounded by curly-braces, for example `{workspace UUID}`.", + "required": true, + "name": "workspace", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The repository.", + "required": true, + "name": "repo_slug", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The UUID of the pipeline.", + "required": true, + "name": "pipeline_uuid", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The UUID of the step.", + "required": true, + "name": "step_uuid", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "tags": ["Pipelines"], + "summary": "Get a summary of test reports for a given step of a pipeline.", + "operationId": "getPipelineTestReports" + } + }, + "/repositories/{workspace}/{repo_slug}/pipelines/{pipeline_uuid}/steps/{step_uuid}/test_reports/test_cases": { + "get": { + "responses": { + "200": { + "description": "Test cases for this pipeline step." + }, + "404": { + "description": "No account, repository, pipeline, step or test reports exist for the provided path.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "parameters": [ + { + "description": "This can either be the workspace ID (slug) or the workspace UUID surrounded by curly-braces, for example `{workspace UUID}`.", + "required": true, + "name": "workspace", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The repository.", + "required": true, + "name": "repo_slug", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The UUID of the pipeline.", + "required": true, + "name": "pipeline_uuid", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The UUID of the step.", + "required": true, + "name": "step_uuid", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "tags": ["Pipelines"], + "summary": "Get test cases for a given step of a pipeline.", + "operationId": "getPipelineTestReportTestCases" + } + }, + "/repositories/{workspace}/{repo_slug}/pipelines/{pipeline_uuid}/steps/{step_uuid}/test_reports/test_cases/{test_case_uuid}/test_case_reasons": { + "get": { + "responses": { + "200": { + "description": "Test case reasons (output)." + }, + "404": { + "description": "No account, repository, pipeline, step or test case with the UUID provided exists for the pipeline with the UUID provided.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "parameters": [ + { + "description": "This can either be the workspace ID (slug) or the workspace UUID surrounded by curly-braces, for example `{workspace UUID}`.", + "required": true, + "name": "workspace", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The repository.", + "required": true, + "name": "repo_slug", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The UUID of the pipeline.", + "required": true, + "name": "pipeline_uuid", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The UUID of the step.", + "required": true, + "name": "step_uuid", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The UUID of the test case.", + "required": true, + "name": "test_case_uuid", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "tags": ["Pipelines"], + "summary": "Get test case reasons (output) for a given test case in a step of a pipeline.", + "operationId": "getPipelineTestReportTestCaseReasons" + } + }, + "/repositories/{workspace}/{repo_slug}/pipelines/{pipeline_uuid}/stopPipeline": { + "post": { + "responses": { + "204": { + "description": "The pipeline has been signaled to stop." + }, + "400": { + "description": "The specified pipeline has already completed.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "Either the account, repository or pipeline with the given UUID does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "description": "Signal the stop of a pipeline and all of its steps that not have completed yet.", + "parameters": [ + { + "description": "This can either be the workspace ID (slug) or the workspace UUID surrounded by curly-braces, for example `{workspace UUID}`.", + "required": true, + "name": "workspace", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The repository.", + "required": true, + "name": "repo_slug", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The UUID of the pipeline.", + "required": true, + "name": "pipeline_uuid", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "tags": ["Pipelines"], + "summary": "Stop a pipeline", + "operationId": "stopPipeline" + } + }, + "/repositories/{workspace}/{repo_slug}/pipelines_config": { + "put": { + "responses": { + "200": { + "description": "The repository pipelines configuration was updated.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/pipelines_config" + } + } + } + } + }, + "description": "Update the pipelines configuration for a repository.", + "parameters": [ + { + "description": "This can either be the workspace ID (slug) or the workspace UUID surrounded by curly-braces, for example `{workspace UUID}`.", + "required": true, + "name": "workspace", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The repository.", + "required": true, + "name": "repo_slug", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/pipelines_config" + } + } + }, + "description": "The updated repository pipelines configuration.", + "required": true + }, + "tags": ["Pipelines"], + "summary": "Update configuration", + "operationId": "updateRepositoryPipelineConfig" + }, + "get": { + "responses": { + "200": { + "description": "The repository pipelines configuration.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/pipelines_config" + } + } + } + } + }, + "description": "Retrieve the repository pipelines configuration.", + "parameters": [ + { + "description": "The account.", + "required": true, + "name": "workspace", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The repository.", + "required": true, + "name": "repo_slug", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "tags": ["Pipelines"], + "summary": "Get configuration", + "operationId": "getRepositoryPipelineConfig" + } + }, + "/repositories/{workspace}/{repo_slug}/pipelines_config/build_number": { + "put": { + "responses": { + "200": { + "description": "The build number has been configured.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/pipeline_build_number" + } + } + } + }, + "400": { + "description": "The update failed because the next number was invalid (it should be higher than the current number).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "The account or repository was not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "description": "Update the next build number that should be assigned to a pipeline. The next build number that will be configured has to be strictly higher than the current latest build number for this repository.", + "parameters": [ + { + "description": "This can either be the workspace ID (slug) or the workspace UUID surrounded by curly-braces, for example `{workspace UUID}`.", + "required": true, + "name": "workspace", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The repository.", + "required": true, + "name": "repo_slug", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/pipeline_build_number" + } + } + }, + "description": "The build number to update.", + "required": true + }, + "tags": ["Pipelines"], + "summary": "Update the next build number", + "operationId": "updateRepositoryBuildNumber" + } + }, + "/repositories/{workspace}/{repo_slug}/pipelines_config/schedules/": { + "post": { + "responses": { + "201": { + "description": "The created schedule.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/pipeline_schedule" + } + } + } + }, + "400": { + "description": "There were errors validating the request.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "401": { + "description": "The maximum limit of schedules for this repository was reached.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "The account or repository was not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "description": "Create a schedule for the given repository.", + "parameters": [ + { + "description": "This can either be the workspace ID (slug) or the workspace UUID surrounded by curly-braces, for example `{workspace UUID}`.", + "required": true, + "name": "workspace", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The repository.", + "required": true, + "name": "repo_slug", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/pipeline_schedule" + } + } + }, + "description": "The schedule to create.", + "required": true + }, + "tags": ["Pipelines"], + "summary": "Create a schedule", + "operationId": "createRepositoryPipelineSchedule" + }, + "get": { + "responses": { + "200": { + "description": "The list of schedules.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_pipeline_schedules" + } + } + } + }, + "404": { + "description": "The account or repository was not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "description": "Retrieve the configured schedules for the given repository.", + "parameters": [ + { + "description": "This can either be the workspace ID (slug) or the workspace UUID surrounded by curly-braces, for example `{workspace UUID}`.", + "required": true, + "name": "workspace", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The repository.", + "required": true, + "name": "repo_slug", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "tags": ["Pipelines"], + "summary": "List schedules", + "operationId": "getRepositoryPipelineSchedules" + } + }, + "/repositories/{workspace}/{repo_slug}/pipelines_config/schedules/{schedule_uuid}": { + "put": { + "responses": { + "200": { + "description": "The schedule is updated.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/pipeline_schedule" + } + } + } + }, + "404": { + "description": "The account, repository or schedule was not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "description": "Update a schedule.", + "parameters": [ + { + "description": "This can either be the workspace ID (slug) or the workspace UUID surrounded by curly-braces, for example `{workspace UUID}`.", + "required": true, + "name": "workspace", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The repository.", + "required": true, + "name": "repo_slug", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The uuid of the schedule.", + "required": true, + "name": "schedule_uuid", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/pipeline_schedule" + } + } + }, + "description": "The schedule to update.", + "required": true + }, + "tags": ["Pipelines"], + "summary": "Update a schedule", + "operationId": "updateRepositoryPipelineSchedule" + }, + "get": { + "responses": { + "200": { + "description": "The requested schedule.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/pipeline_schedule" + } + } + } + }, + "404": { + "description": "The account, repository or schedule was not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "description": "Retrieve a schedule by its UUID.", + "parameters": [ + { + "description": "This can either be the workspace ID (slug) or the workspace UUID surrounded by curly-braces, for example `{workspace UUID}`.", + "required": true, + "name": "workspace", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The repository.", + "required": true, + "name": "repo_slug", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The uuid of the schedule.", + "required": true, + "name": "schedule_uuid", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "tags": ["Pipelines"], + "summary": "Get a schedule", + "operationId": "getRepositoryPipelineSchedule" + }, + "delete": { + "responses": { + "204": { + "description": "The schedule was deleted." + }, + "404": { + "description": "The account, repository or schedule was not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "description": "Delete a schedule.", + "parameters": [ + { + "description": "This can either be the workspace ID (slug) or the workspace UUID surrounded by curly-braces, for example `{workspace UUID}`.", + "required": true, + "name": "workspace", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The repository.", + "required": true, + "name": "repo_slug", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The uuid of the schedule.", + "required": true, + "name": "schedule_uuid", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "tags": ["Pipelines"], + "summary": "Delete a schedule", + "operationId": "deleteRepositoryPipelineSchedule" + } + }, + "/repositories/{workspace}/{repo_slug}/pipelines_config/schedules/{schedule_uuid}/executions/": { + "get": { + "responses": { + "200": { + "description": "The list of executions of a schedule.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_pipeline_schedule_executions" + } + } + } + }, + "404": { + "description": "The account or repository was not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "description": "Retrieve the executions of a given schedule.", + "parameters": [ + { + "description": "This can either be the workspace ID (slug) or the workspace UUID surrounded by curly-braces, for example `{workspace UUID}`.", + "required": true, + "name": "workspace", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The repository.", + "required": true, + "name": "repo_slug", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The uuid of the schedule.", + "required": true, + "name": "schedule_uuid", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "tags": ["Pipelines"], + "summary": "List executions of a schedule", + "operationId": "getRepositoryPipelineScheduleExecutions" + } + }, + "/repositories/{workspace}/{repo_slug}/pipelines_config/ssh/key_pair": { + "put": { + "responses": { + "200": { + "description": "The SSH key pair was created or updated.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/pipeline_ssh_key_pair" + } + } + } + }, + "404": { + "description": "The account, repository or SSH key pair was not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "description": "Create or update the repository SSH key pair. The private key will be set as a default SSH identity in your build container.", + "parameters": [ + { + "description": "This can either be the workspace ID (slug) or the workspace UUID surrounded by curly-braces, for example `{workspace UUID}`.", + "required": true, + "name": "workspace", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The repository.", + "required": true, + "name": "repo_slug", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/pipeline_ssh_key_pair" + } + } + }, + "description": "The created or updated SSH key pair.", + "required": true + }, + "tags": ["Pipelines"], + "summary": "Update SSH key pair", + "operationId": "updateRepositoryPipelineKeyPair" + }, + "get": { + "responses": { + "200": { + "description": "The SSH key pair.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/pipeline_ssh_key_pair" + } + } + } + }, + "404": { + "description": "The account, repository or SSH key pair was not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "description": "Retrieve the repository SSH key pair excluding the SSH private key. The private key is a write only field and will never be exposed in the logs or the REST API.", + "parameters": [ + { + "description": "This can either be the workspace ID (slug) or the workspace UUID surrounded by curly-braces, for example `{workspace UUID}`.", + "required": true, + "name": "workspace", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The repository.", + "required": true, + "name": "repo_slug", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "tags": ["Pipelines"], + "summary": "Get SSH key pair", + "operationId": "getRepositoryPipelineSshKeyPair" + }, + "delete": { + "responses": { + "204": { + "description": "The SSH key pair was deleted." + }, + "404": { + "description": "The account, repository or SSH key pair was not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "description": "Delete the repository SSH key pair.", + "parameters": [ + { + "description": "This can either be the workspace ID (slug) or the workspace UUID surrounded by curly-braces, for example `{workspace UUID}`.", + "required": true, + "name": "workspace", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The repository.", + "required": true, + "name": "repo_slug", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "tags": ["Pipelines"], + "summary": "Delete SSH key pair", + "operationId": "deleteRepositoryPipelineKeyPair" + } + }, + "/repositories/{workspace}/{repo_slug}/pipelines_config/ssh/known_hosts/": { + "post": { + "responses": { + "201": { + "headers": { + "Location": { + "description": "The URL of the newly created pipeline known host.", + "schema": { + "type": "string" + } + } + }, + "description": "The known host was created.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/pipeline_known_host" + } + } + } + }, + "404": { + "description": "The account or repository does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "409": { + "description": "A known host with the provided hostname already exists.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "description": "Create a repository level known host.", + "parameters": [ + { + "description": "This can either be the workspace ID (slug) or the workspace UUID surrounded by curly-braces, for example `{workspace UUID}`.", + "required": true, + "name": "workspace", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The repository.", + "required": true, + "name": "repo_slug", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/pipeline_known_host" + } + } + }, + "description": "The known host to create.", + "required": true + }, + "tags": ["Pipelines"], + "summary": "Create a known host", + "operationId": "createRepositoryPipelineKnownHost" + }, + "get": { + "responses": { + "200": { + "description": "The retrieved known hosts.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_pipeline_known_hosts" + } + } + } + } + }, + "description": "Find repository level known hosts.", + "parameters": [ + { + "description": "This can either be the workspace ID (slug) or the workspace UUID surrounded by curly-braces, for example `{workspace UUID}`.", + "required": true, + "name": "workspace", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The repository.", + "required": true, + "name": "repo_slug", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "tags": ["Pipelines"], + "summary": "List known hosts", + "operationId": "getRepositoryPipelineKnownHosts" + } + }, + "/repositories/{workspace}/{repo_slug}/pipelines_config/ssh/known_hosts/{known_host_uuid}": { + "put": { + "responses": { + "200": { + "description": "The known host was updated.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/pipeline_known_host" + } + } + } + }, + "404": { + "description": "The account, repository or known host with the given UUID was not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "description": "Update a repository level known host.", + "parameters": [ + { + "description": "This can either be the workspace ID (slug) or the workspace UUID surrounded by curly-braces, for example `{workspace UUID}`.", + "required": true, + "name": "workspace", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The repository.", + "required": true, + "name": "repo_slug", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The UUID of the known host to update.", + "required": true, + "name": "known_host_uuid", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/pipeline_known_host" + } + } + }, + "description": "The updated known host.", + "required": true + }, + "tags": ["Pipelines"], + "summary": "Update a known host", + "operationId": "updateRepositoryPipelineKnownHost" + }, + "get": { + "responses": { + "200": { + "description": "The known host.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/pipeline_known_host" + } + } + } + }, + "404": { + "description": "The account, repository or known host with the specified UUID was not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "description": "Retrieve a repository level known host.", + "parameters": [ + { + "description": "This can either be the workspace ID (slug) or the workspace UUID surrounded by curly-braces, for example `{workspace UUID}`.", + "required": true, + "name": "workspace", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The repository.", + "required": true, + "name": "repo_slug", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The UUID of the known host to retrieve.", + "required": true, + "name": "known_host_uuid", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "tags": ["Pipelines"], + "summary": "Get a known host", + "operationId": "getRepositoryPipelineKnownHost" + }, + "delete": { + "responses": { + "204": { + "description": "The known host was deleted." + }, + "404": { + "description": "The account, repository or known host with given UUID was not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "description": "Delete a repository level known host.", + "parameters": [ + { + "description": "This can either be the workspace ID (slug) or the workspace UUID surrounded by curly-braces, for example `{workspace UUID}`.", + "required": true, + "name": "workspace", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The repository.", + "required": true, + "name": "repo_slug", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The UUID of the known host to delete.", + "required": true, + "name": "known_host_uuid", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "tags": ["Pipelines"], + "summary": "Delete a known host", + "operationId": "deleteRepositoryPipelineKnownHost" + } + }, + "/repositories/{workspace}/{repo_slug}/pipelines_config/variables/": { + "post": { + "responses": { + "201": { + "headers": { + "Location": { + "description": "The URL of the newly created pipeline variable.", + "schema": { + "type": "string" + } + } + }, + "description": "The variable was created.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/pipeline_variable" + } + } + } + }, + "404": { + "description": "The account or repository does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "409": { + "description": "A variable with the provided key already exists.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "description": "Create a repository level variable.", + "parameters": [ + { + "description": "This can either be the workspace ID (slug) or the workspace UUID surrounded by curly-braces, for example `{workspace UUID}`.", + "required": true, + "name": "workspace", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The repository.", + "required": true, + "name": "repo_slug", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/pipeline_variable" + } + } + }, + "description": "The variable to create.", + "required": true + }, + "tags": ["Pipelines"], + "summary": "Create a variable for a repository", + "operationId": "createRepositoryPipelineVariable" + }, + "get": { + "responses": { + "200": { + "description": "The retrieved variables.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_pipeline_variables" + } + } + } + } + }, + "description": "Find repository level variables.", + "parameters": [ + { + "description": "This can either be the workspace ID (slug) or the workspace UUID surrounded by curly-braces, for example `{workspace UUID}`.", + "required": true, + "name": "workspace", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The repository.", + "required": true, + "name": "repo_slug", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "tags": ["Pipelines"], + "summary": "List variables for a repository", + "operationId": "getRepositoryPipelineVariables" + } + }, + "/repositories/{workspace}/{repo_slug}/pipelines_config/variables/{variable_uuid}": { + "put": { + "responses": { + "200": { + "description": "The variable was updated.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/pipeline_variable" + } + } + } + }, + "404": { + "description": "The account, repository or variable with the given UUID was not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "description": "Update a repository level variable.", + "parameters": [ + { + "description": "This can either be the workspace ID (slug) or the workspace UUID surrounded by curly-braces, for example `{workspace UUID}`.", + "required": true, + "name": "workspace", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The repository.", + "required": true, + "name": "repo_slug", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The UUID of the variable to update.", + "required": true, + "name": "variable_uuid", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/pipeline_variable" + } + } + }, + "description": "The updated variable", + "required": true + }, + "tags": ["Pipelines"], + "summary": "Update a variable for a repository", + "operationId": "updateRepositoryPipelineVariable" + }, + "get": { + "responses": { + "200": { + "description": "The variable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/pipeline_variable" + } + } + } + }, + "404": { + "description": "The account, repository or variable with the specified UUID was not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "description": "Retrieve a repository level variable.", + "parameters": [ + { + "description": "This can either be the workspace ID (slug) or the workspace UUID surrounded by curly-braces, for example `{workspace UUID}`.", + "required": true, + "name": "workspace", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The repository.", + "required": true, + "name": "repo_slug", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The UUID of the variable to retrieve.", + "required": true, + "name": "variable_uuid", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "tags": ["Pipelines"], + "summary": "Get a variable for a repository", + "operationId": "getRepositoryPipelineVariable" + }, + "delete": { + "responses": { + "204": { + "description": "The variable was deleted." + }, + "404": { + "description": "The account, repository or variable with given UUID was not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "description": "Delete a repository level variable.", + "parameters": [ + { + "description": "This can either be the workspace ID (slug) or the workspace UUID surrounded by curly-braces, for example `{workspace UUID}`.", + "required": true, + "name": "workspace", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The repository.", + "required": true, + "name": "repo_slug", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The UUID of the variable to delete.", + "required": true, + "name": "variable_uuid", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "tags": ["Pipelines"], + "summary": "Delete a variable for a repository", + "operationId": "deleteRepositoryPipelineVariable" + } + }, + "/repositories/{workspace}/{repo_slug}/properties/{app_key}/{property_name}": { + "put": { + "responses": { + "204": { + "description": "An empty response." + } + }, + "parameters": [ + { + "required": true, + "description": "The repository container; either the workspace slug or the UUID in curly braces.", + "in": "path", + "name": "workspace", + "schema": { + "type": "string" + } + }, + { + "required": true, + "description": "The repository.", + "in": "path", + "name": "repo_slug", + "schema": { + "type": "string" + } + }, + { + "required": true, + "description": "The key of the Connect app.", + "in": "path", + "name": "app_key", + "schema": { + "type": "string" + } + }, + { + "required": true, + "description": "The name of the property.", + "in": "path", + "name": "property_name", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/application_property" + }, + "tags": ["properties"], + "description": "Update an [application property](/cloud/bitbucket/application-properties/) value stored against a repository.", + "summary": "Update a repository application property", + "operationId": "updateRepositoryHostedPropertyValue" + }, + "delete": { + "responses": { + "204": { + "description": "An empty response." + } + }, + "parameters": [ + { + "required": true, + "description": "The repository container; either the workspace slug or the UUID in curly braces.", + "in": "path", + "name": "workspace", + "schema": { + "type": "string" + } + }, + { + "required": true, + "description": "The repository.", + "in": "path", + "name": "repo_slug", + "schema": { + "type": "string" + } + }, + { + "required": true, + "description": "The key of the Connect app.", + "in": "path", + "name": "app_key", + "schema": { + "type": "string" + } + }, + { + "required": true, + "description": "The name of the property.", + "in": "path", + "name": "property_name", + "schema": { + "type": "string" + } + } + ], + "tags": ["properties"], + "description": "Delete an [application property](/cloud/bitbucket/application-properties/) value stored against a repository.", + "summary": "Delete a repository application property", + "operationId": "deleteRepositoryHostedPropertyValue" + }, + "get": { + "responses": { + "200": { + "description": "The value of the property.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/application_property" + } + } + } + } + }, + "parameters": [ + { + "required": true, + "description": "The repository container; either the workspace slug or the UUID in curly braces.", + "in": "path", + "name": "workspace", + "schema": { + "type": "string" + } + }, + { + "required": true, + "description": "The repository.", + "in": "path", + "name": "repo_slug", + "schema": { + "type": "string" + } + }, + { + "required": true, + "description": "The key of the Connect app.", + "in": "path", + "name": "app_key", + "schema": { + "type": "string" + } + }, + { + "required": true, + "description": "The name of the property.", + "in": "path", + "name": "property_name", + "schema": { + "type": "string" + } + } + ], + "tags": ["properties"], + "description": "Retrieve an [application property](/cloud/bitbucket/application-properties/) value stored against a repository.", + "summary": "Get a repository application property", + "operationId": "getRepositoryHostedPropertyValue" + } + }, + "/repositories/{workspace}/{repo_slug}/pullrequests": { + "get": { + "responses": { + "200": { + "description": "All pull requests on the specified repository.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_pullrequests" + } + } + } + }, + "401": { + "description": "If the repository is private and the request was not authenticated." + }, + "404": { + "description": "If the specified repository does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "parameters": [ + { + "name": "state", + "in": "query", + "description": "Only return pull requests that are in this state. This parameter can be repeated.", + "schema": { + "type": "string", + "enum": ["MERGED", "SUPERSEDED", "OPEN", "DECLINED"] + } + } + ], + "tags": ["Pullrequests"], + "summary": "List pull requests", + "security": [ + { + "oauth2": ["pullrequest"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns all pull requests on the specified repository.\n\nBy default only open pull requests are returned. This can be controlled\nusing the `state` query parameter. To retrieve pull requests that are\nin one of multiple states, repeat the `state` parameter for each\nindividual state.\n\nThis endpoint also supports filtering and sorting of the results. See\n[filtering and sorting](/cloud/bitbucket/rest/intro/#filtering) for more details." + }, + "post": { + "responses": { + "201": { + "description": "The newly created pull request.", + "headers": { + "Location": { + "description": "The URL of new newly created pull request.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/pullrequest" + } + } + } + }, + "400": { + "description": "If the input document was invalid, or if the caller lacks the privilege to create repositories under the targeted account.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "401": { + "description": "If the request was not authenticated.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/pullrequest" + } + } + }, + "description": "The new pull request.\n\nThe request URL you POST to becomes the destination repository URL. For this reason, you must specify an explicit source repository in the request object if you want to pull from a different repository (fork).\n\nSince not all elements are required or even mutable, you only need to include the elements you want to initialize, such as the source branch and the title." + }, + "tags": ["Pullrequests"], + "summary": "Create a pull request", + "security": [ + { + "oauth2": ["pullrequest:write"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Creates a new pull request where the destination repository is\nthis repository and the author is the authenticated user.\n\nThe minimum required fields to create a pull request are `title` and\n`source`, specified by a branch name.\n\n```\ncurl https://api.bitbucket.org/2.0/repositories/my-workspace/my-repository/pullrequests \\\n -u my-username:my-password \\\n --request POST \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"title\": \"My Title\",\n \"source\": {\n \"branch\": {\n \"name\": \"staging\"\n }\n }\n }'\n```\n\nIf the pull request's `destination` is not specified, it will default\nto the `repository.mainbranch`. To open a pull request to a\ndifferent branch, say from a feature branch to a staging branch,\nspecify a `destination` (same format as the `source`):\n\n```\n{\n \"title\": \"My Title\",\n \"source\": {\n \"branch\": {\n \"name\": \"my-feature-branch\"\n }\n },\n \"destination\": {\n \"branch\": {\n \"name\": \"staging\"\n }\n }\n}\n```\n\nReviewers can be specified by adding an array of user objects as the\n`reviewers` property.\n\n```\n{\n \"title\": \"My Title\",\n \"source\": {\n \"branch\": {\n \"name\": \"my-feature-branch\"\n }\n },\n \"reviewers\": [\n {\n \"uuid\": \"{504c3b62-8120-4f0c-a7bc-87800b9d6f70}\"\n }\n ]\n}\n```\n\nOther fields:\n\n* `description` - a string\n* `close_source_branch` - boolean that specifies if the source branch should be closed upon merging" + }, + "parameters": [ + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/pullrequests/activity": { + "get": { + "responses": { + "200": { + "description": "The pull request activity log" + }, + "401": { + "description": "If the repository is private and the request was not authenticated." + }, + "404": { + "description": "If the specified repository does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Pullrequests"], + "summary": "List a pull request activity log", + "security": [ + { + "oauth2": ["pullrequest"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns a paginated list of the pull request's activity log.\n\nThis handler serves both a v20 and internal endpoint. The v20 endpoint\nreturns reviewer comments, updates, approvals and request changes. The internal\nendpoint includes those plus tasks and attachments.\n\nComments created on a file or a line of code have an inline property.\n\nComment example:\n```\n{\n \"pagelen\": 20,\n \"values\": [\n {\n \"comment\": {\n \"links\": {\n \"self\": {\n \"href\": \"https://bitbucket.org/!api/2.0/repositories/atlassian/atlaskit-mk-2/pullrequests/5695/comments/118571088\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/atlassian/atlaskit-mk-2/pull-requests/5695/_/diff#comment-118571088\"\n }\n },\n \"deleted\": false,\n \"pullrequest\": {\n \"type\": \"pullrequest\",\n \"id\": 5695,\n \"links\": {\n \"self\": {\n \"href\": \"https://bitbucket.org/!api/2.0/repositories/atlassian/atlaskit-mk-2/pullrequests/5695\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/atlassian/atlaskit-mk-2/pull-requests/5695\"\n }\n },\n \"title\": \"username/NONE: small change from onFocus to onClick to handle tabbing through the page and not expand the editor unless a click event triggers it\"\n },\n \"content\": {\n \"raw\": \"inline with to a dn from lines\",\n \"markup\": \"markdown\",\n \"html\": \"

inline with to a dn from lines

\",\n \"type\": \"rendered\"\n },\n \"created_on\": \"2019-09-27T00:33:46.039178+00:00\",\n \"user\": {\n \"display_name\": \"Name Lastname\",\n \"uuid\": \"{}\",\n \"links\": {\n \"self\": {\n \"href\": \"https://bitbucket.org/!api/2.0/users/%7B%7D\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/%7B%7D/\"\n },\n \"avatar\": {\n \"href\": \"https://avatar-management--avatars.us-west-2.prod.public.atl-paas.net/:/128\"\n }\n },\n \"type\": \"user\",\n \"nickname\": \"Name\",\n \"account_id\": \"\"\n },\n \"created_on\": \"2019-09-27T00:33:46.039178+00:00\",\n \"user\": {\n \"display_name\": \"Name Lastname\",\n \"uuid\": \"{}\",\n \"links\": {\n \"self\": {\n \"href\": \"https://bitbucket.org/!api/2.0/users/%7B%7D\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/%7B%7D/\"\n },\n \"avatar\": {\n \"href\": \"https://avatar-management--avatars.us-west-2.prod.public.atl-paas.net/:/128\"\n }\n },\n \"type\": \"user\",\n \"nickname\": \"Name\",\n \"account_id\": \"\"\n },\n \"updated_on\": \"2019-09-27T00:33:46.055384+00:00\",\n \"inline\": {\n \"context_lines\": \"\",\n \"to\": null,\n \"path\": \"\",\n \"outdated\": false,\n \"from\": 211\n },\n \"type\": \"pullrequest_comment\",\n \"id\": 118571088\n },\n \"pull_request\": {\n \"type\": \"pullrequest\",\n \"id\": 5695,\n \"links\": {\n \"self\": {\n \"href\": \"https://bitbucket.org/!api/2.0/repositories/atlassian/atlaskit-mk-2/pullrequests/5695\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/atlassian/atlaskit-mk-2/pull-requests/5695\"\n }\n },\n \"title\": \"username/NONE: small change from onFocus to onClick to handle tabbing through the page and not expand the editor unless a click event triggers it\"\n }\n }\n ]\n}\n```\n\nUpdates include a state property of OPEN, MERGED, or DECLINED.\n\nUpdate example:\n```\n{\n \"pagelen\": 20,\n \"values\": [\n {\n \"update\": {\n \"description\": \"\",\n \"title\": \"username/NONE: small change from onFocus to onClick to handle tabbing through the page and not expand the editor unless a click event triggers it\",\n \"destination\": {\n \"commit\": {\n \"type\": \"commit\",\n \"hash\": \"6a2c16e4a152\",\n \"links\": {\n \"self\": {\n \"href\": \"https://bitbucket.org/!api/2.0/repositories/atlassian/atlaskit-mk-2/commit/6a2c16e4a152\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/atlassian/atlaskit-mk-2/commits/6a2c16e4a152\"\n }\n }\n },\n \"branch\": {\n \"name\": \"master\"\n },\n \"repository\": {\n \"name\": \"Atlaskit-MK-2\",\n \"type\": \"repository\",\n \"full_name\": \"atlassian/atlaskit-mk-2\",\n \"links\": {\n \"self\": {\n \"href\": \"https://bitbucket.org/!api/2.0/repositories/atlassian/atlaskit-mk-2\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/atlassian/atlaskit-mk-2\"\n },\n \"avatar\": {\n \"href\": \"https://bytebucket.org/ravatar/%7B%7D?ts=js\"\n }\n },\n \"uuid\": \"{}\"\n }\n },\n \"reason\": \"\",\n \"source\": {\n \"commit\": {\n \"type\": \"commit\",\n \"hash\": \"728c8bad1813\",\n \"links\": {\n \"self\": {\n \"href\": \"https://bitbucket.org/!api/2.0/repositories/atlassian/atlaskit-mk-2/commit/728c8bad1813\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/atlassian/atlaskit-mk-2/commits/728c8bad1813\"\n }\n }\n },\n \"branch\": {\n \"name\": \"username/NONE-add-onClick-prop-for-accessibility\"\n },\n \"repository\": {\n \"name\": \"Atlaskit-MK-2\",\n \"type\": \"repository\",\n \"full_name\": \"atlassian/atlaskit-mk-2\",\n \"links\": {\n \"self\": {\n \"href\": \"https://bitbucket.org/!api/2.0/repositories/atlassian/atlaskit-mk-2\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/atlassian/atlaskit-mk-2\"\n },\n \"avatar\": {\n \"href\": \"https://bytebucket.org/ravatar/%7B%7D?ts=js\"\n }\n },\n \"uuid\": \"{}\"\n }\n },\n \"state\": \"OPEN\",\n \"author\": {\n \"display_name\": \"Name Lastname\",\n \"uuid\": \"{}\",\n \"links\": {\n \"self\": {\n \"href\": \"https://bitbucket.org/!api/2.0/users/%7B%7D\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/%7B%7D/\"\n },\n \"avatar\": {\n \"href\": \"https://avatar-management--avatars.us-west-2.prod.public.atl-paas.net/:/128\"\n }\n },\n \"type\": \"user\",\n \"nickname\": \"Name\",\n \"account_id\": \"\"\n },\n \"date\": \"2019-05-10T06:48:25.305565+00:00\"\n },\n \"pull_request\": {\n \"type\": \"pullrequest\",\n \"id\": 5695,\n \"links\": {\n \"self\": {\n \"href\": \"https://bitbucket.org/!api/2.0/repositories/atlassian/atlaskit-mk-2/pullrequests/5695\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/atlassian/atlaskit-mk-2/pull-requests/5695\"\n }\n },\n \"title\": \"username/NONE: small change from onFocus to onClick to handle tabbing through the page and not expand the editor unless a click event triggers it\"\n }\n }\n ]\n}\n```\n\nApproval example:\n```\n{\n \"pagelen\": 20,\n \"values\": [\n {\n \"approval\": {\n \"date\": \"2019-09-27T00:37:19.849534+00:00\",\n \"pullrequest\": {\n \"type\": \"pullrequest\",\n \"id\": 5695,\n \"links\": {\n \"self\": {\n \"href\": \"https://bitbucket.org/!api/2.0/repositories/atlassian/atlaskit-mk-2/pullrequests/5695\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/atlassian/atlaskit-mk-2/pull-requests/5695\"\n }\n },\n \"title\": \"username/NONE: small change from onFocus to onClick to handle tabbing through the page and not expand the editor unless a click event triggers it\"\n },\n \"user\": {\n \"display_name\": \"Name Lastname\",\n \"uuid\": \"{}\",\n \"links\": {\n \"self\": {\n \"href\": \"https://bitbucket.org/!api/2.0/users/%7B%7D\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/%7B%7D/\"\n },\n \"avatar\": {\n \"href\": \"https://avatar-management--avatars.us-west-2.prod.public.atl-paas.net/:/128\"\n }\n },\n \"type\": \"user\",\n \"nickname\": \"Name\",\n \"account_id\": \"\"\n }\n },\n \"pull_request\": {\n \"type\": \"pullrequest\",\n \"id\": 5695,\n \"links\": {\n \"self\": {\n \"href\": \"https://bitbucket.org/!api/2.0/repositories/atlassian/atlaskit-mk-2/pullrequests/5695\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/atlassian/atlaskit-mk-2/pull-requests/5695\"\n }\n },\n \"title\": \"username/NONE: small change from onFocus to onClick to handle tabbing through the page and not expand the editor unless a click event triggers it\"\n }\n }\n ]\n}\n```" + }, + "parameters": [ + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/pullrequests/{pull_request_id}": { + "get": { + "responses": { + "200": { + "description": "The pull request object", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/pullrequest" + } + } + } + }, + "401": { + "description": "If the repository is private and the request was not authenticated." + }, + "404": { + "description": "If the repository or pull request does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Pullrequests"], + "summary": "Get a pull request", + "security": [ + { + "oauth2": ["pullrequest"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns the specified pull request." + }, + "put": { + "responses": { + "200": { + "description": "The updated pull request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/pullrequest" + } + } + } + }, + "400": { + "description": "If the input document was invalid.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "401": { + "description": "If the request was not authenticated.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "If the repository or pull request id does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/pullrequest" + } + } + }, + "description": "The pull request that is to be updated." + }, + "tags": ["Pullrequests"], + "summary": "Update a pull request", + "security": [ + { + "oauth2": ["pullrequest:write"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Mutates the specified pull request.\n\nThis can be used to change the pull request's branches or description.\n\nOnly open pull requests can be mutated." + }, + "parameters": [ + { + "name": "pull_request_id", + "in": "path", + "description": "The id of the pull request.", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/pullrequests/{pull_request_id}/activity": { + "get": { + "responses": { + "200": { + "description": "The pull request activity log" + }, + "401": { + "description": "If the repository is private and the request was not authenticated." + }, + "404": { + "description": "If the specified repository does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Pullrequests"], + "summary": "List a pull request activity log", + "security": [ + { + "oauth2": ["pullrequest"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns a paginated list of the pull request's activity log.\n\nThis handler serves both a v20 and internal endpoint. The v20 endpoint\nreturns reviewer comments, updates, approvals and request changes. The internal\nendpoint includes those plus tasks and attachments.\n\nComments created on a file or a line of code have an inline property.\n\nComment example:\n```\n{\n \"pagelen\": 20,\n \"values\": [\n {\n \"comment\": {\n \"links\": {\n \"self\": {\n \"href\": \"https://bitbucket.org/!api/2.0/repositories/atlassian/atlaskit-mk-2/pullrequests/5695/comments/118571088\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/atlassian/atlaskit-mk-2/pull-requests/5695/_/diff#comment-118571088\"\n }\n },\n \"deleted\": false,\n \"pullrequest\": {\n \"type\": \"pullrequest\",\n \"id\": 5695,\n \"links\": {\n \"self\": {\n \"href\": \"https://bitbucket.org/!api/2.0/repositories/atlassian/atlaskit-mk-2/pullrequests/5695\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/atlassian/atlaskit-mk-2/pull-requests/5695\"\n }\n },\n \"title\": \"username/NONE: small change from onFocus to onClick to handle tabbing through the page and not expand the editor unless a click event triggers it\"\n },\n \"content\": {\n \"raw\": \"inline with to a dn from lines\",\n \"markup\": \"markdown\",\n \"html\": \"

inline with to a dn from lines

\",\n \"type\": \"rendered\"\n },\n \"created_on\": \"2019-09-27T00:33:46.039178+00:00\",\n \"user\": {\n \"display_name\": \"Name Lastname\",\n \"uuid\": \"{}\",\n \"links\": {\n \"self\": {\n \"href\": \"https://bitbucket.org/!api/2.0/users/%7B%7D\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/%7B%7D/\"\n },\n \"avatar\": {\n \"href\": \"https://avatar-management--avatars.us-west-2.prod.public.atl-paas.net/:/128\"\n }\n },\n \"type\": \"user\",\n \"nickname\": \"Name\",\n \"account_id\": \"\"\n },\n \"created_on\": \"2019-09-27T00:33:46.039178+00:00\",\n \"user\": {\n \"display_name\": \"Name Lastname\",\n \"uuid\": \"{}\",\n \"links\": {\n \"self\": {\n \"href\": \"https://bitbucket.org/!api/2.0/users/%7B%7D\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/%7B%7D/\"\n },\n \"avatar\": {\n \"href\": \"https://avatar-management--avatars.us-west-2.prod.public.atl-paas.net/:/128\"\n }\n },\n \"type\": \"user\",\n \"nickname\": \"Name\",\n \"account_id\": \"\"\n },\n \"updated_on\": \"2019-09-27T00:33:46.055384+00:00\",\n \"inline\": {\n \"context_lines\": \"\",\n \"to\": null,\n \"path\": \"\",\n \"outdated\": false,\n \"from\": 211\n },\n \"type\": \"pullrequest_comment\",\n \"id\": 118571088\n },\n \"pull_request\": {\n \"type\": \"pullrequest\",\n \"id\": 5695,\n \"links\": {\n \"self\": {\n \"href\": \"https://bitbucket.org/!api/2.0/repositories/atlassian/atlaskit-mk-2/pullrequests/5695\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/atlassian/atlaskit-mk-2/pull-requests/5695\"\n }\n },\n \"title\": \"username/NONE: small change from onFocus to onClick to handle tabbing through the page and not expand the editor unless a click event triggers it\"\n }\n }\n ]\n}\n```\n\nUpdates include a state property of OPEN, MERGED, or DECLINED.\n\nUpdate example:\n```\n{\n \"pagelen\": 20,\n \"values\": [\n {\n \"update\": {\n \"description\": \"\",\n \"title\": \"username/NONE: small change from onFocus to onClick to handle tabbing through the page and not expand the editor unless a click event triggers it\",\n \"destination\": {\n \"commit\": {\n \"type\": \"commit\",\n \"hash\": \"6a2c16e4a152\",\n \"links\": {\n \"self\": {\n \"href\": \"https://bitbucket.org/!api/2.0/repositories/atlassian/atlaskit-mk-2/commit/6a2c16e4a152\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/atlassian/atlaskit-mk-2/commits/6a2c16e4a152\"\n }\n }\n },\n \"branch\": {\n \"name\": \"master\"\n },\n \"repository\": {\n \"name\": \"Atlaskit-MK-2\",\n \"type\": \"repository\",\n \"full_name\": \"atlassian/atlaskit-mk-2\",\n \"links\": {\n \"self\": {\n \"href\": \"https://bitbucket.org/!api/2.0/repositories/atlassian/atlaskit-mk-2\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/atlassian/atlaskit-mk-2\"\n },\n \"avatar\": {\n \"href\": \"https://bytebucket.org/ravatar/%7B%7D?ts=js\"\n }\n },\n \"uuid\": \"{}\"\n }\n },\n \"reason\": \"\",\n \"source\": {\n \"commit\": {\n \"type\": \"commit\",\n \"hash\": \"728c8bad1813\",\n \"links\": {\n \"self\": {\n \"href\": \"https://bitbucket.org/!api/2.0/repositories/atlassian/atlaskit-mk-2/commit/728c8bad1813\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/atlassian/atlaskit-mk-2/commits/728c8bad1813\"\n }\n }\n },\n \"branch\": {\n \"name\": \"username/NONE-add-onClick-prop-for-accessibility\"\n },\n \"repository\": {\n \"name\": \"Atlaskit-MK-2\",\n \"type\": \"repository\",\n \"full_name\": \"atlassian/atlaskit-mk-2\",\n \"links\": {\n \"self\": {\n \"href\": \"https://bitbucket.org/!api/2.0/repositories/atlassian/atlaskit-mk-2\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/atlassian/atlaskit-mk-2\"\n },\n \"avatar\": {\n \"href\": \"https://bytebucket.org/ravatar/%7B%7D?ts=js\"\n }\n },\n \"uuid\": \"{}\"\n }\n },\n \"state\": \"OPEN\",\n \"author\": {\n \"display_name\": \"Name Lastname\",\n \"uuid\": \"{}\",\n \"links\": {\n \"self\": {\n \"href\": \"https://bitbucket.org/!api/2.0/users/%7B%7D\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/%7B%7D/\"\n },\n \"avatar\": {\n \"href\": \"https://avatar-management--avatars.us-west-2.prod.public.atl-paas.net/:/128\"\n }\n },\n \"type\": \"user\",\n \"nickname\": \"Name\",\n \"account_id\": \"\"\n },\n \"date\": \"2019-05-10T06:48:25.305565+00:00\"\n },\n \"pull_request\": {\n \"type\": \"pullrequest\",\n \"id\": 5695,\n \"links\": {\n \"self\": {\n \"href\": \"https://bitbucket.org/!api/2.0/repositories/atlassian/atlaskit-mk-2/pullrequests/5695\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/atlassian/atlaskit-mk-2/pull-requests/5695\"\n }\n },\n \"title\": \"username/NONE: small change from onFocus to onClick to handle tabbing through the page and not expand the editor unless a click event triggers it\"\n }\n }\n ]\n}\n```\n\nApproval example:\n```\n{\n \"pagelen\": 20,\n \"values\": [\n {\n \"approval\": {\n \"date\": \"2019-09-27T00:37:19.849534+00:00\",\n \"pullrequest\": {\n \"type\": \"pullrequest\",\n \"id\": 5695,\n \"links\": {\n \"self\": {\n \"href\": \"https://bitbucket.org/!api/2.0/repositories/atlassian/atlaskit-mk-2/pullrequests/5695\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/atlassian/atlaskit-mk-2/pull-requests/5695\"\n }\n },\n \"title\": \"username/NONE: small change from onFocus to onClick to handle tabbing through the page and not expand the editor unless a click event triggers it\"\n },\n \"user\": {\n \"display_name\": \"Name Lastname\",\n \"uuid\": \"{}\",\n \"links\": {\n \"self\": {\n \"href\": \"https://bitbucket.org/!api/2.0/users/%7B%7D\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/%7B%7D/\"\n },\n \"avatar\": {\n \"href\": \"https://avatar-management--avatars.us-west-2.prod.public.atl-paas.net/:/128\"\n }\n },\n \"type\": \"user\",\n \"nickname\": \"Name\",\n \"account_id\": \"\"\n }\n },\n \"pull_request\": {\n \"type\": \"pullrequest\",\n \"id\": 5695,\n \"links\": {\n \"self\": {\n \"href\": \"https://bitbucket.org/!api/2.0/repositories/atlassian/atlaskit-mk-2/pullrequests/5695\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/atlassian/atlaskit-mk-2/pull-requests/5695\"\n }\n },\n \"title\": \"username/NONE: small change from onFocus to onClick to handle tabbing through the page and not expand the editor unless a click event triggers it\"\n }\n }\n ]\n}\n```" + }, + "parameters": [ + { + "name": "pull_request_id", + "in": "path", + "description": "The id of the pull request.", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/pullrequests/{pull_request_id}/approve": { + "delete": { + "responses": { + "204": { + "description": "An empty response indicating the authenticated user's approval has been withdrawn." + }, + "401": { + "description": "The request wasn't authenticated.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "The specified pull request or the repository does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Pullrequests"], + "summary": "Unapprove a pull request", + "security": [ + { + "oauth2": ["pullrequest:write"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Redact the authenticated user's approval of the specified pull\nrequest." + }, + "post": { + "responses": { + "200": { + "description": "The `participant` object recording that the authenticated user approved the pull request.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/participant" + } + } + } + }, + "401": { + "description": "The request wasn't authenticated.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "The specified pull request or the repository does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Pullrequests"], + "summary": "Approve a pull request", + "security": [ + { + "oauth2": ["pullrequest:write"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Approve the specified pull request as the authenticated user." + }, + "parameters": [ + { + "name": "pull_request_id", + "in": "path", + "description": "The id of the pull request.", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/pullrequests/{pull_request_id}/comments": { + "get": { + "responses": { + "200": { + "description": "A paginated list of comments made on the given pull request, in chronological order.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_pullrequest_comments" + } + } + } + }, + "403": { + "description": "If the authenticated user does not have access to the pull request.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "If the pull request does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Pullrequests"], + "summary": "List comments on a pull request", + "security": [ + { + "oauth2": ["pullrequest"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns a paginated list of the pull request's comments.\n\nThis includes both global, inline comments and replies.\n\nThe default sorting is oldest to newest and can be overridden with\nthe `sort` query parameter.\n\nThis endpoint also supports filtering and sorting of the results. See\n[filtering and sorting](/cloud/bitbucket/rest/intro/#filtering) for more\ndetails." + }, + "post": { + "responses": { + "201": { + "description": "The newly created comment.", + "headers": { + "Location": { + "description": "The URL of the new comment", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/pullrequest_comment" + } + } + } + }, + "403": { + "description": "If the authenticated user does not have access to the pull request.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "If the pull request does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/pullrequest_comment" + } + } + }, + "description": "The comment object.", + "required": true + }, + "tags": ["Pullrequests"], + "summary": "Create a comment on a pull request", + "security": [ + { + "oauth2": ["pullrequest"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Creates a new pull request comment.\n\nReturns the newly created pull request comment." + }, + "parameters": [ + { + "name": "pull_request_id", + "in": "path", + "description": "The id of the pull request.", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/pullrequests/{pull_request_id}/comments/{comment_id}": { + "delete": { + "responses": { + "204": { + "description": "Successful deletion." + }, + "403": { + "description": "If the authenticated user does not have access to delete the comment.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "If the comment does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Pullrequests"], + "summary": "Delete a comment on a pull request", + "security": [ + { + "oauth2": ["pullrequest"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Deletes a specific pull request comment." + }, + "get": { + "responses": { + "200": { + "description": "The comment.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/pullrequest_comment" + } + } + } + }, + "403": { + "description": "If the authenticated user does not have access to the pull request.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "If the comment does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Pullrequests"], + "summary": "Get a comment on a pull request", + "security": [ + { + "oauth2": ["pullrequest"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns a specific pull request comment." + }, + "put": { + "responses": { + "200": { + "description": "The updated comment.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/pullrequest_comment" + } + } + } + }, + "403": { + "description": "If the authenticated user does not have access to the comment.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "If the comment does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/pullrequest_comment" + } + } + }, + "description": "The contents of the updated comment.", + "required": true + }, + "tags": ["Pullrequests"], + "summary": "Update a comment on a pull request", + "security": [ + { + "oauth2": ["pullrequest"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Updates a specific pull request comment." + }, + "parameters": [ + { + "name": "comment_id", + "in": "path", + "description": "The id of the comment.", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "name": "pull_request_id", + "in": "path", + "description": "The id of the pull request.", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/pullrequests/{pull_request_id}/commits": { + "get": { + "responses": { + "200": { + "description": "A paginated list of commits made on the given pull request, in chronological order. This list will be empty if the source branch no longer exists." + }, + "403": { + "description": "If the authenticated user does not have access to the pull request.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "If the pull request does not exist or the source branch is from a forked repository which no longer exists.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Pullrequests"], + "summary": "List commits on a pull request", + "security": [ + { + "oauth2": ["pullrequest"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns a paginated list of the pull request's commits.\n\nThese are the commits that are being merged into the destination\nbranch when the pull requests gets accepted." + }, + "parameters": [ + { + "name": "pull_request_id", + "in": "path", + "description": "The id of the pull request.", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/pullrequests/{pull_request_id}/decline": { + "post": { + "responses": { + "200": { + "description": "The pull request was successfully declined.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/pullrequest" + } + } + } + }, + "555": { + "description": "If the decline took too long and timed out.\nIn this case the caller should retry the request later.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Pullrequests"], + "summary": "Decline a pull request", + "security": [ + { + "oauth2": ["pullrequest:write"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Declines the pull request." + }, + "parameters": [ + { + "name": "pull_request_id", + "in": "path", + "description": "The id of the pull request.", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/pullrequests/{pull_request_id}/diff": { + "get": { + "responses": { + "302": { + "description": "Redirects to the [repository diff](/cloud/bitbucket/rest/api-group-commits/#api-repositories-workspace-repo-slug-diff-spec-get) with the\nrevspec that corresponds to the pull request.\n" + } + }, + "tags": ["Pullrequests"], + "summary": "List changes in a pull request", + "security": [ + { + "oauth2": ["pullrequest"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Redirects to the [repository diff](/cloud/bitbucket/rest/api-group-commits/#api-repositories-workspace-repo-slug-diff-spec-get)\nwith the revspec that corresponds to the pull request." + }, + "parameters": [ + { + "name": "pull_request_id", + "in": "path", + "description": "The id of the pull request.", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/pullrequests/{pull_request_id}/diffstat": { + "get": { + "responses": { + "302": { + "description": "Redirects to the [repository diffstat](/cloud/bitbucket/rest/api-group-commits/#api-repositories-workspace-repo-slug-diffstat-spec-get) with\nthe revspec that corresponds to pull request.\n" + } + }, + "tags": ["Pullrequests"], + "summary": "Get the diff stat for a pull request", + "security": [ + { + "oauth2": ["pullrequest"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Redirects to the [repository diffstat](/cloud/bitbucket/rest/api-group-commits/#api-repositories-workspace-repo-slug-diffstat-spec-get)\nwith the revspec that corresponds to the pull request." + }, + "parameters": [ + { + "name": "pull_request_id", + "in": "path", + "description": "The id of the pull request.", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/pullrequests/{pull_request_id}/merge": { + "post": { + "responses": { + "200": { + "description": "The pull request object.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/pullrequest" + } + } + } + }, + "202": { + "description": "In the Location header, the URL to poll for the pull request merge status" + }, + "555": { + "description": "If the merge took too long and timed out.\nIn this case the caller should retry the request later", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "parameters": [ + { + "name": "async", + "in": "query", + "description": "Default value is false.\n\n\nWhen set to true, runs merge asynchronously and\nimmediately returns a 202 with polling link to\nthe task-status API in the Location header.\n\n\nWhen set to false, runs merge and waits for it to\ncomplete, returning 200 when it succeeds. If the\nduration of the merge exceeds a timeout threshold,\nthe API returns a 202 with polling link to the\ntask-status API in the Location header.", + "required": false, + "schema": { + "type": "boolean" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/pullrequest_merge_parameters" + } + } + } + }, + "tags": ["Pullrequests"], + "summary": "Merge a pull request", + "security": [ + { + "oauth2": ["pullrequest:write"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Merges the pull request." + }, + "parameters": [ + { + "name": "pull_request_id", + "in": "path", + "description": "The id of the pull request.", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/pullrequests/{pull_request_id}/merge/task-status/{task_id}": { + "get": { + "responses": { + "200": { + "description": "Returns a task status if the merge is either pending or successful, and if it is successful, a pull request" + }, + "400": { + "description": "If the provided task ID does not relate to this pull request, or if something went wrong during the merge operation" + }, + "403": { + "description": "The user making the request does not have permission to the repo and is different from the user who queued the task" + } + }, + "tags": ["Pullrequests"], + "summary": "Get the merge task status for a pull request", + "security": [ + { + "oauth2": ["pullrequest"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "When merging a pull request takes too long, the client receives a\ntask ID along with a 202 status code. The task ID can be used in a call\nto this endpoint to check the status of a merge task.\n\n```\ncurl -X GET https://api.bitbucket.org/2.0/repositories/atlassian/bitbucket/pullrequests/2286/merge/task-status/\n```\n\nIf the merge task is not yet finished, a PENDING status will be returned.\n\n```\nHTTP/2 200\n{\n \"task_status\": \"PENDING\",\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/atlassian/bitbucket/pullrequests/2286/merge/task-status/\"\n }\n }\n}\n```\n\nIf the merge was successful, a SUCCESS status will be returned.\n\n```\nHTTP/2 200\n{\n \"task_status\": \"SUCCESS\",\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/atlassian/bitbucket/pullrequests/2286/merge/task-status/\"\n }\n },\n \"merge_result\": \n}\n```\n\nIf the merge task failed, an error will be returned.\n\n```\n{\n \"type\": \"error\",\n \"error\": {\n \"message\": \"\"\n }\n}\n```" + }, + "parameters": [ + { + "name": "pull_request_id", + "in": "path", + "description": "The id of the pull request.", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "task_id", + "in": "path", + "description": "ID of the merge task", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/pullrequests/{pull_request_id}/patch": { + "get": { + "responses": { + "302": { + "description": "Redirects to the [repository patch](/cloud/bitbucket/rest/api-group-commits/#api-repositories-workspace-repo-slug-patch-spec-get) with\nthe revspec that corresponds to pull request.\n" + } + }, + "tags": ["Pullrequests"], + "summary": "Get the patch for a pull request", + "security": [ + { + "oauth2": ["pullrequest"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Redirects to the [repository patch](/cloud/bitbucket/rest/api-group-commits/#api-repositories-workspace-repo-slug-patch-spec-get)\nwith the revspec that corresponds to pull request." + }, + "parameters": [ + { + "name": "pull_request_id", + "in": "path", + "description": "The id of the pull request.", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/pullrequests/{pull_request_id}/request-changes": { + "delete": { + "responses": { + "204": { + "description": "An empty response indicating the authenticated user's request for change has been withdrawn." + }, + "401": { + "description": "The request wasn't authenticated.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "The specified pull request or the repository does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Pullrequests"], + "summary": "Remove change request for a pull request", + "security": [ + { + "oauth2": ["pullrequest:write"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "" + }, + "post": { + "responses": { + "200": { + "description": "The `participant` object recording that the authenticated user requested changes on the pull request.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/participant" + } + } + } + }, + "401": { + "description": "The request wasn't authenticated.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "The specified pull request or the repository does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Pullrequests"], + "summary": "Request changes for a pull request", + "security": [ + { + "oauth2": ["pullrequest:write"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "" + }, + "parameters": [ + { + "name": "pull_request_id", + "in": "path", + "description": "The id of the pull request.", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/pullrequests/{pull_request_id}/statuses": { + "get": { + "responses": { + "200": { + "description": "A paginated list of all commit statuses for this pull request.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_commitstatuses" + } + } + } + }, + "401": { + "description": "If the repository is private and the request was not authenticated." + }, + "404": { + "description": "If the specified repository or pull request does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "parameters": [ + { + "name": "q", + "in": "query", + "description": "Query string to narrow down the response as per\n[filtering and sorting](/cloud/bitbucket/rest/intro/#filtering).\n", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "sort", + "in": "query", + "description": "Field by which the results should be sorted as per\n[filtering and sorting](/cloud/bitbucket/rest/intro/#filtering).\nDefaults to `created_on`.\n", + "required": false, + "schema": { + "type": "string" + } + } + ], + "tags": ["Pullrequests", "Commit statuses"], + "summary": "List commit statuses for a pull request", + "security": [ + { + "oauth2": ["pullrequest"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns all statuses (e.g. build results) for the given pull\nrequest." + }, + "parameters": [ + { + "name": "pull_request_id", + "in": "path", + "description": "The id of the pull request.", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/pullrequests/{pullrequest_id}/properties/{app_key}/{property_name}": { + "put": { + "responses": { + "204": { + "description": "An empty response." + } + }, + "parameters": [ + { + "required": true, + "description": "The repository container; either the workspace slug or the UUID in curly braces.", + "in": "path", + "name": "workspace", + "schema": { + "type": "string" + } + }, + { + "required": true, + "description": "The repository.", + "in": "path", + "name": "repo_slug", + "schema": { + "type": "string" + } + }, + { + "required": true, + "description": "The pull request ID.", + "in": "path", + "name": "pullrequest_id", + "schema": { + "type": "string" + } + }, + { + "required": true, + "description": "The key of the Connect app.", + "in": "path", + "name": "app_key", + "schema": { + "type": "string" + } + }, + { + "required": true, + "description": "The name of the property.", + "in": "path", + "name": "property_name", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/application_property" + }, + "tags": ["properties"], + "description": "Update an [application property](/cloud/bitbucket/application-properties/) value stored against a pull request.", + "summary": "Update a pull request application property", + "operationId": "updatePullRequestHostedPropertyValue" + }, + "delete": { + "responses": { + "204": { + "description": "An empty response." + } + }, + "parameters": [ + { + "required": true, + "description": "The repository container; either the workspace slug or the UUID in curly braces.", + "in": "path", + "name": "workspace", + "schema": { + "type": "string" + } + }, + { + "required": true, + "description": "The repository.", + "in": "path", + "name": "repo_slug", + "schema": { + "type": "string" + } + }, + { + "required": true, + "description": "The pull request ID.", + "in": "path", + "name": "pullrequest_id", + "schema": { + "type": "string" + } + }, + { + "required": true, + "description": "The key of the Connect app.", + "in": "path", + "name": "app_key", + "schema": { + "type": "string" + } + }, + { + "required": true, + "description": "The name of the property.", + "in": "path", + "name": "property_name", + "schema": { + "type": "string" + } + } + ], + "tags": ["properties"], + "description": "Delete an [application property](/cloud/bitbucket/application-properties/) value stored against a pull request.", + "summary": "Delete a pull request application property", + "operationId": "deletePullRequestHostedPropertyValue" + }, + "get": { + "responses": { + "200": { + "description": "The value of the property.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/application_property" + } + } + } + } + }, + "parameters": [ + { + "required": true, + "description": "The repository container; either the workspace slug or the UUID in curly braces.", + "in": "path", + "name": "workspace", + "schema": { + "type": "string" + } + }, + { + "required": true, + "description": "The repository.", + "in": "path", + "name": "repo_slug", + "schema": { + "type": "string" + } + }, + { + "required": true, + "description": "The pull request ID.", + "in": "path", + "name": "pullrequest_id", + "schema": { + "type": "string" + } + }, + { + "required": true, + "description": "The key of the Connect app.", + "in": "path", + "name": "app_key", + "schema": { + "type": "string" + } + }, + { + "required": true, + "description": "The name of the property.", + "in": "path", + "name": "property_name", + "schema": { + "type": "string" + } + } + ], + "tags": ["properties"], + "description": "Retrieve an [application property](/cloud/bitbucket/application-properties/) value stored against a pull request.", + "summary": "Get a pull request application property", + "operationId": "getPullRequestHostedPropertyValue" + } + }, + "/repositories/{workspace}/{repo_slug}/refs": { + "get": { + "responses": { + "200": { + "description": "A paginated list of refs matching any filter criteria that were provided.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_refs" + } + } + } + }, + "403": { + "description": "If the repository is private and the authenticated user does not have\naccess to it.\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "The specified repository does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "parameters": [ + { + "name": "q", + "in": "query", + "description": "\nQuery string to narrow down the response as per\n[filtering and sorting](/cloud/bitbucket/rest/intro/#filtering).", + "schema": { + "type": "string" + } + }, + { + "name": "sort", + "in": "query", + "description": "\nField by which the results should be sorted as per\n[filtering and sorting](/cloud/bitbucket/rest/intro/#filtering). The `name`\nfield is handled specially for refs in that, if specified as the sort field, it\nuses a natural sort order instead of the default lexicographical sort order. For example,\nit will return ['1.1', '1.2', '1.10'] instead of ['1.1', '1.10', '1.2'].", + "schema": { + "type": "string" + } + } + ], + "tags": ["Refs"], + "summary": "List branches and tags", + "security": [ + { + "oauth2": ["repository"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns the branches and tags in the repository.\n\nBy default, results will be in the order the underlying source control system returns them and identical to\nthe ordering one sees when running \"$ git show-ref\". Note that this follows simple\nlexical ordering of the ref names.\n\nThis can be undesirable as it does apply any natural sorting semantics, meaning for instance that refs are\nsorted [\"branch1\", \"branch10\", \"branch2\", \"v10\", \"v11\", \"v9\"] instead of [\"branch1\", \"branch2\",\n\"branch10\", \"v9\", \"v10\", \"v11\"].\n\nSorting can be changed using the ?sort= query parameter. When using ?sort=name to explicitly sort on ref name,\nBitbucket will apply natural sorting and interpret numerical values as numbers instead of strings." + }, + "parameters": [ + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/refs/branches": { + "get": { + "responses": { + "200": { + "description": "A paginated list of branches matching any filter criteria that were provided.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_branches" + } + } + } + }, + "403": { + "description": "If the repository is private and the authenticated user does not have\naccess to it.\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "The specified repository does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "parameters": [ + { + "name": "q", + "in": "query", + "description": "\nQuery string to narrow down the response as per\n[filtering and sorting](/cloud/bitbucket/rest/intro/#filtering).", + "schema": { + "type": "string" + } + }, + { + "name": "sort", + "in": "query", + "description": "\nField by which the results should be sorted as per\n[filtering and sorting](/cloud/bitbucket/rest/intro/#filtering). The `name`\nfield is handled specially for branches in that, if specified as the sort field, it\nuses a natural sort order instead of the default lexicographical sort order. For example,\nit will return ['branch1', 'branch2', 'branch10'] instead of ['branch1', 'branch10', 'branch2'].", + "schema": { + "type": "string" + } + } + ], + "tags": ["Refs"], + "summary": "List open branches", + "security": [ + { + "oauth2": ["repository"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns a list of all open branches within the specified repository.\n Results will be in the order the source control manager returns them.\n\n ```\n $ curl -s https://api.bitbucket.org/2.0/repositories/atlassian/aui/refs/branches?pagelen=1 | jq .\n {\n \"pagelen\": 1,\n \"size\": 187,\n \"values\": [\n {\n \"name\": \"issue-9.3/AUI-5343-assistive-class\",\n \"links\": {\n \"commits\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/atlassian/aui/commits/issue-9.3/AUI-5343-assistive-class\"\n },\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/atlassian/aui/refs/branches/issue-9.3/AUI-5343-assistive-class\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/atlassian/aui/branch/issue-9.3/AUI-5343-assistive-class\"\n }\n },\n \"default_merge_strategy\": \"squash\",\n \"merge_strategies\": [\n \"merge_commit\",\n \"squash\",\n \"fast_forward\"\n ],\n \"type\": \"branch\",\n \"target\": {\n \"hash\": \"e5d1cde9069fcb9f0af90403a4de2150c125a148\",\n \"repository\": {\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/atlassian/aui\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/atlassian/aui\"\n },\n \"avatar\": {\n \"href\": \"https://bytebucket.org/ravatar/%7B585074de-7b60-4fd1-81ed-e0bc7fafbda5%7D?ts=86317\"\n }\n },\n \"type\": \"repository\",\n \"name\": \"aui\",\n \"full_name\": \"atlassian/aui\",\n \"uuid\": \"{585074de-7b60-4fd1-81ed-e0bc7fafbda5}\"\n },\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/atlassian/aui/commit/e5d1cde9069fcb9f0af90403a4de2150c125a148\"\n },\n \"comments\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/atlassian/aui/commit/e5d1cde9069fcb9f0af90403a4de2150c125a148/comments\"\n },\n \"patch\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/atlassian/aui/patch/e5d1cde9069fcb9f0af90403a4de2150c125a148\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/atlassian/aui/commits/e5d1cde9069fcb9f0af90403a4de2150c125a148\"\n },\n \"diff\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/atlassian/aui/diff/e5d1cde9069fcb9f0af90403a4de2150c125a148\"\n },\n \"approve\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/atlassian/aui/commit/e5d1cde9069fcb9f0af90403a4de2150c125a148/approve\"\n },\n \"statuses\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/atlassian/aui/commit/e5d1cde9069fcb9f0af90403a4de2150c125a148/statuses\"\n }\n },\n \"author\": {\n \"raw\": \"Marcin Konopka \",\n \"type\": \"author\",\n \"user\": {\n \"display_name\": \"Marcin Konopka\",\n \"uuid\": \"{47cc24f4-2a05-4420-88fe-0417535a110a}\",\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/users/%7B47cc24f4-2a05-4420-88fe-0417535a110a%7D\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/%7B47cc24f4-2a05-4420-88fe-0417535a110a%7D/\"\n },\n \"avatar\": {\n \"href\": \"https://avatar-management--avatars.us-west-2.prod.public.atl-paas.net/initials/MK-1.png\"\n }\n },\n \"nickname\": \"Marcin Konopka\",\n \"type\": \"user\",\n \"account_id\": \"60113d2b47a9540069f4de03\"\n }\n },\n \"parents\": [\n {\n \"hash\": \"87f7fc92b00464ae47b13ef65c91884e4ac9be51\",\n \"type\": \"commit\",\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/atlassian/aui/commit/87f7fc92b00464ae47b13ef65c91884e4ac9be51\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/atlassian/aui/commits/87f7fc92b00464ae47b13ef65c91884e4ac9be51\"\n }\n }\n }\n ],\n \"date\": \"2021-04-13T13:44:49+00:00\",\n \"message\": \"wip\n\",\n \"type\": \"commit\"\n }\n }\n ],\n \"page\": 1,\n \"next\": \"https://api.bitbucket.org/2.0/repositories/atlassian/aui/refs/branches?pagelen=1&page=2\"\n }\n ```\n\n Branches support [filtering and sorting](/cloud/bitbucket/rest/intro/#filtering)\n that can be used to search for specific branches. For instance, to find\n all branches that have \"stab\" in their name:\n\n ```\n curl -s https://api.bitbucket.org/2.0/repositories/atlassian/aui/refs/branches -G --data-urlencode 'q=name ~ \"stab\"'\n ```\n\n By default, results will be in the order the underlying source control system returns them and identical to\n the ordering one sees when running \"$ git branch --list\". Note that this follows simple\n lexical ordering of the ref names.\n\n This can be undesirable as it does apply any natural sorting semantics, meaning for instance that tags are\n sorted [\"v10\", \"v11\", \"v9\"] instead of [\"v9\", \"v10\", \"v11\"].\n\n Sorting can be changed using the ?q= query parameter. When using ?q=name to explicitly sort on ref name,\n Bitbucket will apply natural sorting and interpret numerical values as numbers instead of strings." + }, + "post": { + "responses": { + "201": { + "description": "The newly created branch object.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/branch" + } + } + } + }, + "403": { + "description": "If the repository is private and the authenticated user does not have\naccess to it.\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "The specified repository or branch does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Refs"], + "summary": "Create a branch", + "security": [ + { + "oauth2": ["repository:write"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Creates a new branch in the specified repository.\n\nThe payload of the POST should consist of a JSON document that\ncontains the name of the tag and the target hash.\n\n```\ncurl https://api.bitbucket.org/2.0/repositories/seanfarley/hg/refs/branches \\\n-s -u seanfarley -X POST -H \"Content-Type: application/json\" \\\n-d '{\n \"name\" : \"smf/create-feature\",\n \"target\" : {\n \"hash\" : \"default\",\n }\n}'\n```\n\nThis call requires authentication. Private repositories require the\ncaller to authenticate with an account that has appropriate\nauthorization.\n\nThe branch name should not include any prefixes (e.g.\nrefs/heads). This endpoint does support using short hash prefixes for\nthe commit hash, but it may return a 400 response if the provided\nprefix is ambiguous. Using a full commit hash is the preferred\napproach." + }, + "parameters": [ + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/refs/branches/{name}": { + "delete": { + "responses": { + "204": { + "description": "Indicates that the specified branch was successfully deleted." + }, + "403": { + "description": "If the repository is private and the authenticated user does not have\naccess to it.\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "The specified repository or branch does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Refs"], + "summary": "Delete a branch", + "security": [ + { + "oauth2": ["repository:write"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Delete a branch in the specified repository.\n\nThe main branch is not allowed to be deleted and will return a 400\nresponse.\n\nThe branch name should not include any prefixes (e.g.\nrefs/heads)." + }, + "get": { + "responses": { + "200": { + "description": "The branch object.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/branch" + } + } + } + }, + "403": { + "description": "If the repository is private and the authenticated user does not have\naccess to it.\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "The specified repository or branch does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Refs"], + "summary": "Get a branch", + "security": [ + { + "oauth2": ["repository"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns a branch object within the specified repository.\n\n ```\n $ curl -s https://api.bitbucket.org/2.0/repositories/atlassian/aui/refs/branches/master | jq .\n {\n \"name\": \"master\",\n \"links\": {\n \"commits\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/atlassian/aui/commits/master\"\n },\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/atlassian/aui/refs/branches/master\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/atlassian/aui/branch/master\"\n }\n },\n \"default_merge_strategy\": \"squash\",\n \"merge_strategies\": [\n \"merge_commit\",\n \"squash\",\n \"fast_forward\"\n ],\n \"type\": \"branch\",\n \"target\": {\n \"hash\": \"e7d158ff7ed5538c28f94cd97a9ad569680fc94e\",\n \"repository\": {\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/atlassian/aui\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/atlassian/aui\"\n },\n \"avatar\": {\n \"href\": \"https://bytebucket.org/ravatar/%7B585074de-7b60-4fd1-81ed-e0bc7fafbda5%7D?ts=86317\"\n }\n },\n \"type\": \"repository\",\n \"name\": \"aui\",\n \"full_name\": \"atlassian/aui\",\n \"uuid\": \"{585074de-7b60-4fd1-81ed-e0bc7fafbda5}\"\n },\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/atlassian/aui/commit/e7d158ff7ed5538c28f94cd97a9ad569680fc94e\"\n },\n \"comments\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/atlassian/aui/commit/e7d158ff7ed5538c28f94cd97a9ad569680fc94e/comments\"\n },\n \"patch\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/atlassian/aui/patch/e7d158ff7ed5538c28f94cd97a9ad569680fc94e\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/atlassian/aui/commits/e7d158ff7ed5538c28f94cd97a9ad569680fc94e\"\n },\n \"diff\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/atlassian/aui/diff/e7d158ff7ed5538c28f94cd97a9ad569680fc94e\"\n },\n \"approve\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/atlassian/aui/commit/e7d158ff7ed5538c28f94cd97a9ad569680fc94e/approve\"\n },\n \"statuses\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/atlassian/aui/commit/e7d158ff7ed5538c28f94cd97a9ad569680fc94e/statuses\"\n }\n },\n \"author\": {\n \"raw\": \"psre-renovate-bot \",\n \"type\": \"author\",\n \"user\": {\n \"display_name\": \"psre-renovate-bot\",\n \"uuid\": \"{250a442a-3ab3-4fcb-87c3-3c8f3df65ec7}\",\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/users/%7B250a442a-3ab3-4fcb-87c3-3c8f3df65ec7%7D\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/%7B250a442a-3ab3-4fcb-87c3-3c8f3df65ec7%7D/\"\n },\n \"avatar\": {\n \"href\": \"https://secure.gravatar.com/avatar/6972ee037c9f36360170a86f544071a2?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FP-3.png\"\n }\n },\n \"nickname\": \"Renovate Bot\",\n \"type\": \"user\",\n \"account_id\": \"5d5355e8c6b9320d9ea5b28d\"\n }\n },\n \"parents\": [\n {\n \"hash\": \"eab868a309e75733de80969a7bed1ec6d4651e06\",\n \"type\": \"commit\",\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/atlassian/aui/commit/eab868a309e75733de80969a7bed1ec6d4651e06\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/atlassian/aui/commits/eab868a309e75733de80969a7bed1ec6d4651e06\"\n }\n }\n }\n ],\n \"date\": \"2021-04-12T06:44:38+00:00\",\n \"message\": \"Merged in issue/NONE-renovate-master-babel-monorepo (pull request #2883)\n\nchore(deps): update babel monorepo to v7.13.15 (master)\n\nApproved-by: Chris \"Daz\" Darroch\n\",\n \"type\": \"commit\"\n }\n }\n ```\n\n This call requires authentication. Private repositories require the\n caller to authenticate with an account that has appropriate\n authorization.\n\n For Git, the branch name should not include any prefixes (e.g.\n refs/heads)." + }, + "parameters": [ + { + "name": "name", + "in": "path", + "description": "The name of the branch.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/refs/tags": { + "get": { + "responses": { + "200": { + "description": "A paginated list of tags matching any filter criteria that were provided.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_tags" + } + } + } + }, + "403": { + "description": "If the repository is private and the authenticated user does not have\naccess to it.\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "The specified repository does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "parameters": [ + { + "name": "q", + "in": "query", + "description": "\nQuery string to narrow down the response as per\n[filtering and sorting](/cloud/bitbucket/rest/intro/#filtering).", + "schema": { + "type": "string" + } + }, + { + "name": "sort", + "in": "query", + "description": "\nField by which the results should be sorted as per\n[filtering and sorting](/cloud/bitbucket/rest/intro/#filtering). The `name`\nfield is handled specially for tags in that, if specified as the sort field, it\nuses a natural sort order instead of the default lexicographical sort order. For example,\nit will return ['1.1', '1.2', '1.10'] instead of ['1.1', '1.10', '1.2'].", + "schema": { + "type": "string" + } + } + ], + "tags": ["Refs"], + "summary": "List tags", + "security": [ + { + "oauth2": ["repository"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns the tags in the repository.\n\nBy default, results will be in the order the underlying source control system returns them and identical to\nthe ordering one sees when running \"$ git tag --list\". Note that this follows simple\nlexical ordering of the ref names.\n\nThis can be undesirable as it does apply any natural sorting semantics, meaning for instance that tags are\nsorted [\"v10\", \"v11\", \"v9\"] instead of [\"v9\", \"v10\", \"v11\"].\n\nSorting can be changed using the ?sort= query parameter. When using ?sort=name to explicitly sort on ref name,\nBitbucket will apply natural sorting and interpret numerical values as numbers instead of strings." + }, + "post": { + "responses": { + "201": { + "description": "The newly created tag.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/tag" + } + } + } + }, + "400": { + "description": "If the target hash is missing, ambiguous, or invalid, or if the name is not provided.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/tag" + } + } + }, + "required": true + }, + "tags": ["Refs"], + "summary": "Create a tag", + "security": [ + { + "oauth2": ["repository:write"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Creates a new tag in the specified repository.\n\nThe payload of the POST should consist of a JSON document that\ncontains the name of the tag and the target hash.\n\n```\ncurl https://api.bitbucket.org/2.0/repositories/jdoe/myrepo/refs/tags \\\n-s -u jdoe -X POST -H \"Content-Type: application/json\" \\\n-d '{\n \"name\" : \"new-tag-name\",\n \"target\" : {\n \"hash\" : \"a1b2c3d4e5f6\",\n }\n}'\n```\n\nThis endpoint does support using short hash prefixes for the commit\nhash, but it may return a 400 response if the provided prefix is\nambiguous. Using a full commit hash is the preferred approach." + }, + "parameters": [ + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/refs/tags/{name}": { + "delete": { + "responses": { + "204": { + "description": "Indicates the specified tag was successfully deleted." + }, + "403": { + "description": "If the repository is private and the authenticated user does not have\naccess to it.\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "The specified repository or tag does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Refs"], + "summary": "Delete a tag", + "security": [ + { + "oauth2": ["repository:write"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Delete a tag in the specified repository.\n\nThe tag name should not include any prefixes (e.g. refs/tags)." + }, + "get": { + "responses": { + "200": { + "description": "The tag object.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/tag" + } + } + } + }, + "403": { + "description": "If the repository is private and the authenticated user does not have\naccess to it.\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "The specified repository or tag does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Refs"], + "summary": "Get a tag", + "security": [ + { + "oauth2": ["repository"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns the specified tag.\n\n```\n$ curl -s https://api.bitbucket.org/2.0/repositories/seanfarley/hg/refs/tags/3.8 -G | jq .\n{\n \"name\": \"3.8\",\n \"links\": {\n \"commits\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/seanfarley/hg/commits/3.8\"\n },\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/seanfarley/hg/refs/tags/3.8\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/seanfarley/hg/commits/tag/3.8\"\n }\n },\n \"tagger\": {\n \"raw\": \"Matt Mackall \",\n \"type\": \"author\",\n \"user\": {\n \"username\": \"mpmselenic\",\n \"nickname\": \"mpmselenic\",\n \"display_name\": \"Matt Mackall\",\n \"type\": \"user\",\n \"uuid\": \"{a4934530-db4c-419c-a478-9ab4964c2ee7}\",\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/users/mpmselenic\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/mpmselenic/\"\n },\n \"avatar\": {\n \"href\": \"https://bitbucket.org/account/mpmselenic/avatar/32/\"\n }\n }\n }\n },\n \"date\": \"2016-05-01T18:52:25+00:00\",\n \"message\": \"Added tag 3.8 for changeset f85de28eae32\",\n \"type\": \"tag\",\n \"target\": {\n \"hash\": \"f85de28eae32e7d3064b1a1321309071bbaaa069\",\n \"repository\": {\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/seanfarley/hg\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/seanfarley/hg\"\n },\n \"avatar\": {\n \"href\": \"https://bitbucket.org/seanfarley/hg/avatar/32/\"\n }\n },\n \"type\": \"repository\",\n \"name\": \"hg\",\n \"full_name\": \"seanfarley/hg\",\n \"uuid\": \"{c75687fb-e99d-4579-9087-190dbd406d30}\"\n },\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/seanfarley/hg/commit/f85de28eae32e7d3064b1a1321309071bbaaa069\"\n },\n \"comments\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/seanfarley/hg/commit/f85de28eae32e7d3064b1a1321309071bbaaa069/comments\"\n },\n \"patch\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/seanfarley/hg/patch/f85de28eae32e7d3064b1a1321309071bbaaa069\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/seanfarley/hg/commits/f85de28eae32e7d3064b1a1321309071bbaaa069\"\n },\n \"diff\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/seanfarley/hg/diff/f85de28eae32e7d3064b1a1321309071bbaaa069\"\n },\n \"approve\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/seanfarley/hg/commit/f85de28eae32e7d3064b1a1321309071bbaaa069/approve\"\n },\n \"statuses\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/seanfarley/hg/commit/f85de28eae32e7d3064b1a1321309071bbaaa069/statuses\"\n }\n },\n \"author\": {\n \"raw\": \"Sean Farley \",\n \"type\": \"author\",\n \"user\": {\n \"username\": \"seanfarley\",\n \"nickname\": \"seanfarley\",\n \"display_name\": \"Sean Farley\",\n \"type\": \"user\",\n \"uuid\": \"{a295f8a8-5876-4d43-89b5-3ad8c6c3c51d}\",\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/users/seanfarley\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/seanfarley/\"\n },\n \"avatar\": {\n \"href\": \"https://bitbucket.org/account/seanfarley/avatar/32/\"\n }\n }\n }\n },\n \"parents\": [\n {\n \"hash\": \"9a98d0e5b07fc60887f9d3d34d9ac7d536f470d2\",\n \"type\": \"commit\",\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/seanfarley/hg/commit/9a98d0e5b07fc60887f9d3d34d9ac7d536f470d2\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/seanfarley/hg/commits/9a98d0e5b07fc60887f9d3d34d9ac7d536f470d2\"\n }\n }\n }\n ],\n \"date\": \"2016-05-01T04:21:17+00:00\",\n \"message\": \"debian: alphabetize build deps\",\n \"type\": \"commit\"\n }\n}\n```" + }, + "parameters": [ + { + "name": "name", + "in": "path", + "description": "The name of the tag.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/src": { + "get": { + "responses": { + "200": { + "description": "If the path matches a file, then the raw contents of the file are\nreturned (unless the `format=meta` query parameter was provided,\nin which case a json document containing the file's meta data is\nreturned). If the path matches a directory, then a paginated\nlist of file and directory entries is returned (if the\n`format=meta` query parameter was provided, then the json document\ncontaining the directory's meta data is returned).\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_treeentries" + } + } + } + }, + "404": { + "description": "If the path or commit in the URL does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "parameters": [ + { + "name": "format", + "in": "query", + "description": "Instead of returning the file's contents, return the (json) meta data for it.", + "required": false, + "schema": { + "type": "string", + "enum": ["meta"] + } + } + ], + "tags": ["Source", "Repositories"], + "summary": "Get the root directory of the main branch", + "security": [ + { + "oauth2": ["repository"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "This endpoint redirects the client to the directory listing of the\nroot directory on the main branch.\n\nThis is equivalent to directly hitting\n[/2.0/repositories/{username}/{repo_slug}/src/{commit}/{path}](src/%7Bcommit%7D/%7Bpath%7D)\nwithout having to know the name or SHA1 of the repo's main branch.\n\nTo create new commits, [POST to this endpoint](#post)" + }, + "post": { + "responses": { + "201": { + "description": "\n" + }, + "403": { + "description": "If the authenticated user does not have write or admin access", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "If the repository does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "parameters": [ + { + "name": "message", + "in": "query", + "description": "The commit message. When omitted, Bitbucket uses a canned string.", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "author", + "in": "query", + "description": "\nThe raw string to be used as the new commit's author.\nThis string follows the format\n`Erik van Zijst `.\n\nWhen omitted, Bitbucket uses the authenticated user's\nfull/display name and primary email address. Commits cannot\nbe created anonymously.", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "parents", + "in": "query", + "description": "\nA comma-separated list of SHA1s of the commits that should\nbe the parents of the newly created commit.\n\nWhen omitted, the new commit will inherit from and become\na child of the main branch's tip/HEAD commit.\n\nWhen more than one SHA1 is provided, the first SHA1\nidentifies the commit from which the content will be\ninherited.\".", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "files", + "in": "query", + "description": "\nOptional field that declares the files that the request is\nmanipulating. When adding a new file to a repo, or when\noverwriting an existing file, the client can just upload\nthe full contents of the file in a normal form field and\nthe use of this `files` meta data field is redundant.\nHowever, when the `files` field contains a file path that\ndoes not have a corresponding, identically-named form\nfield, then Bitbucket interprets that as the client wanting\nto replace the named file with the null set and the file is\ndeleted instead.\n\nPaths in the repo that are referenced in neither files nor\nan individual file field, remain unchanged and carry over\nfrom the parent to the new commit.\n\nThis API does not support renaming as an explicit feature.\nTo rename a file, simply delete it and recreate it under\nthe new name in the same commit.\n", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "branch", + "in": "query", + "description": "\nThe name of the branch that the new commit should be\ncreated on. When omitted, the commit will be created on top\nof the main branch and will become the main branch's new\nhead.\n\nWhen a branch name is provided that already exists in the\nrepo, then the commit will be created on top of that\nbranch. In this case, *if* a parent SHA1 was also provided,\nthen it is asserted that the parent is the branch's\ntip/HEAD at the time the request is made. When this is not\nthe case, a 409 is returned.\n\nWhen a new branch name is specified (that does not already\nexist in the repo), and no parent SHA1s are provided, then\nthe new commit will inherit from the current main branch's\ntip/HEAD commit, but not advance the main branch. The new\ncommit will be the new branch. When the request *also*\nspecifies a parent SHA1, then the new commit and branch\nare created directly on top of the parent commit,\nregardless of the state of the main branch.\n\nWhen a branch name is not specified, but a parent SHA1 is\nprovided, then Bitbucket asserts that it represents the\nmain branch's current HEAD/tip, or a 409 is returned.\n\nWhen a branch name is not specified and the repo is empty,\nthe new commit will become the repo's root commit and will\nbe on the main branch.\n\nWhen a branch name is specified and the repo is empty, the\nnew commit will become the repo's root commit and also\ndefine the repo's main branch going forward.\n\nThis API cannot be used to create additional root commits\nin non-empty repos.\n\nThe branch field cannot be repeated.\n\nAs a side effect, this API can be used to create a new\nbranch without modifying any files, by specifying a new\nbranch name in this field, together with `parents`, but\nomitting the `files` fields, while not sending any files.\nThis will create a new commit and branch with the same\ncontents as the first parent. The diff of this commit\nagainst its first parent will be empty.\n", + "required": false, + "schema": { + "type": "string" + } + } + ], + "tags": ["Source", "Repositories"], + "summary": "Create a commit by uploading a file", + "security": [ + { + "oauth2": ["repository:write"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "This endpoint is used to create new commits in the repository by\nuploading files.\n\nTo add a new file to a repository:\n\n```\n$ curl https://api.bitbucket.org/2.0/repositories/username/slug/src \\\n -F /repo/path/to/image.png=@image.png\n```\n\nThis will create a new commit on top of the main branch, inheriting the\ncontents of the main branch, but adding (or overwriting) the\n`image.png` file to the repository in the `/repo/path/to` directory.\n\nTo create a commit that deletes files, use the `files` parameter:\n\n```\n$ curl https://api.bitbucket.org/2.0/repositories/username/slug/src \\\n -F files=/file/to/delete/1.txt \\\n -F files=/file/to/delete/2.txt\n```\n\nYou can add/modify/delete multiple files in a request. Rename/move a\nfile by deleting the old path and adding the content at the new path.\n\nThis endpoint accepts `multipart/form-data` (as in the examples above),\nas well as `application/x-www-form-urlencoded`.\n\n#### multipart/form-data\n\nA `multipart/form-data` post contains a series of \"form fields\" that\nidentify both the individual files that are being uploaded, as well as\nadditional, optional meta data.\n\nFiles are uploaded in file form fields (those that have a\n`Content-Disposition` parameter) whose field names point to the remote\npath in the repository where the file should be stored. Path field\nnames are always interpreted to be absolute from the root of the\nrepository, regardless whether the client uses a leading slash (as the\nabove `curl` example did).\n\nFile contents are treated as bytes and are not decoded as text.\n\nThe commit message, as well as other non-file meta data for the\nrequest, is sent along as normal form field elements. Meta data fields\nshare the same namespace as the file objects. For `multipart/form-data`\nbodies that should not lead to any ambiguity, as the\n`Content-Disposition` header will contain the `filename` parameter to\ndistinguish between a file named \"message\" and the commit message field.\n\n#### application/x-www-form-urlencoded\n\nIt is also possible to upload new files using a simple\n`application/x-www-form-urlencoded` POST. This can be convenient when\nuploading pure text files:\n\n```\n$ curl https://api.bitbucket.org/2.0/repositories/atlassian/bbql/src \\\n --data-urlencode \"/path/to/me.txt=Lorem ipsum.\" \\\n --data-urlencode \"message=Initial commit\" \\\n --data-urlencode \"author=Erik van Zijst \"\n```\n\nThere could be a field name clash if a client were to upload a file\nnamed \"message\", as this filename clashes with the meta data property\nfor the commit message. To avoid this and to upload files whose names\nclash with the meta data properties, use a leading slash for the files,\ne.g. `curl --data-urlencode \"/message=file contents\"`.\n\nWhen an explicit slash is omitted for a file whose path matches that of\na meta data parameter, then it is interpreted as meta data, not as a\nfile.\n\n#### Executables and links\n\nWhile this API aims to facilitate the most common use cases, it is\npossible to perform some more advanced operations like creating a new\nsymlink in the repository, or creating an executable file.\n\nFiles can be supplied with a `x-attributes` value in the\n`Content-Disposition` header. For example, to upload an executable\nfile, as well as create a symlink from `README.txt` to `README`:\n\n```\n--===============1438169132528273974==\nContent-Type: text/plain; charset=\"us-ascii\"\nMIME-Version: 1.0\nContent-Transfer-Encoding: 7bit\nContent-ID: \"bin/shutdown.sh\"\nContent-Disposition: attachment; filename=\"shutdown.sh\"; x-attributes:\"executable\"\n\n#!/bin/sh\nhalt\n\n--===============1438169132528273974==\nContent-Type: text/plain; charset=\"us-ascii\"\nMIME-Version: 1.0\nContent-Transfer-Encoding: 7bit\nContent-ID: \"/README.txt\"\nContent-Disposition: attachment; filename=\"README.txt\"; x-attributes:\"link\"\n\nREADME\n--===============1438169132528273974==--\n```\n\nLinks are files that contain the target path and have\n`x-attributes:\"link\"` set.\n\nWhen overwriting links with files, or vice versa, the newly uploaded\nfile determines both the new contents, as well as the attributes. That\nmeans uploading a file without specifying `x-attributes=\"link\"` will\ncreate a regular file, even if the parent commit hosted a symlink at\nthe same path.\n\nThe same applies to executables. When modifying an existing executable\nfile, the form-data file element must include\n`x-attributes=\"executable\"` in order to preserve the executable status\nof the file.\n\nNote that this API does not support the creation or manipulation of\nsubrepos / submodules." + }, + "parameters": [ + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/src/{commit}/{path}": { + "get": { + "responses": { + "200": { + "description": "If the path matches a file, then the raw contents of the file are\nreturned. If the `format=meta` query parameter is provided,\na json document containing the file's meta data is\nreturned. If the `format=rendered` query parameter is provided,\nthe contents of the file in HTML-formated rendered markup is returned.\nIf the path matches a directory, then a paginated\nlist of file and directory entries is returned (if the\n`format=meta` query parameter was provided, then the json document\ncontaining the directory's meta data is returned.)\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_treeentries" + } + } + } + }, + "404": { + "description": "If the path or commit in the URL does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "555": { + "description": "If the call times out, possibly because the specifiedrecursion depth is too large.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "parameters": [ + { + "name": "format", + "in": "query", + "description": "If 'meta' is provided, returns the (json) meta data for the contents of the file. If 'rendered' is provided, returns the contents of a non-binary file in HTML-formatted rendered markup. Since Git does not generally track what text encoding scheme is used, this endpoint attempts to detect the most appropriate character encoding. While usually correct, determining the character encoding can be ambiguous which in exceptional cases can lead to misinterpretation of the characters. As such, the raw element in the response object should not be treated as equivalent to the file's actual contents.", + "required": false, + "schema": { + "type": "string", + "enum": ["meta", "rendered"] + } + }, + { + "name": "q", + "in": "query", + "description": "Optional filter expression as per [filtering and sorting](/cloud/bitbucket/rest/intro/#filtering).", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "sort", + "in": "query", + "description": "Optional sorting parameter as per [filtering and sorting](/cloud/bitbucket/rest/intro/#sorting-query-results).", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "max_depth", + "in": "query", + "description": "If provided, returns the contents of the repository and its subdirectories recursively until the specified max_depth of nested directories. When omitted, this defaults to 1.", + "required": false, + "schema": { + "type": "integer" + } + } + ], + "tags": ["Source", "Repositories"], + "summary": "Get file or directory contents", + "security": [ + { + "oauth2": ["repository"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "This endpoints is used to retrieve the contents of a single file,\nor the contents of a directory at a specified revision.\n\n#### Raw file contents\n\nWhen `path` points to a file, this endpoint returns the raw contents.\nThe response's Content-Type is derived from the filename\nextension (not from the contents). The file contents are not processed\nand no character encoding/recoding is performed and as a result no\ncharacter encoding is included as part of the Content-Type.\n\nThe `Content-Disposition` header will be \"attachment\" to prevent\nbrowsers from running executable files.\n\nIf the file is managed by LFS, then a 301 redirect pointing to\nAtlassian's media services platform is returned.\n\nThe response includes an ETag that is based on the contents of the file\nand its attributes. This means that an empty `__init__.py` always\nreturns the same ETag, regardless on the directory it lives in, or the\ncommit it is on.\n\n#### File meta data\n\nWhen the request for a file path includes the query parameter\n`?format=meta`, instead of returning the file's raw contents, Bitbucket\ninstead returns the JSON object describing the file's properties:\n\n```javascript\n$ curl https://api.bitbucket.org/2.0/repositories/atlassian/bbql/src/eefd5ef/tests/__init__.py?format=meta\n{\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/atlassian/bbql/src/eefd5ef5d3df01aed629f650959d6706d54cd335/tests/__init__.py\"\n },\n \"meta\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/atlassian/bbql/src/eefd5ef5d3df01aed629f650959d6706d54cd335/tests/__init__.py?format=meta\"\n }\n },\n \"path\": \"tests/__init__.py\",\n \"commit\": {\n \"type\": \"commit\",\n \"hash\": \"eefd5ef5d3df01aed629f650959d6706d54cd335\",\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/atlassian/bbql/commit/eefd5ef5d3df01aed629f650959d6706d54cd335\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/atlassian/bbql/commits/eefd5ef5d3df01aed629f650959d6706d54cd335\"\n }\n }\n },\n \"attributes\": [],\n \"type\": \"commit_file\",\n \"size\": 0\n}\n```\n\nFile objects contain an `attributes` element that contains a list of\npossible modifiers. Currently defined values are:\n\n* `link` -- indicates that the entry is a symbolic link. The contents\n of the file represent the path the link points to.\n* `executable` -- indicates that the file has the executable bit set.\n* `subrepository` -- indicates that the entry points to a submodule or\n subrepo. The contents of the file is the SHA1 of the repository\n pointed to.\n* `binary` -- indicates whether Bitbucket thinks the file is binary.\n\nThis endpoint can provide an alternative to how a HEAD request can be\nused to check for the existence of a file, or a file's size without\nincurring the overhead of receiving its full contents.\n\n\n#### Directory listings\n\nWhen `path` points to a directory instead of a file, the response is a\npaginated list of directory and file objects in the same order as the\nunderlying SCM system would return them.\n\nFor example:\n\n```javascript\n$ curl https://api.bitbucket.org/2.0/repositories/atlassian/bbql/src/eefd5ef/tests\n{\n \"pagelen\": 10,\n \"values\": [\n {\n \"path\": \"tests/test_project\",\n \"type\": \"commit_directory\",\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/atlassian/bbql/src/eefd5ef5d3df01aed629f650959d6706d54cd335/tests/test_project/\"\n },\n \"meta\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/atlassian/bbql/src/eefd5ef5d3df01aed629f650959d6706d54cd335/tests/test_project/?format=meta\"\n }\n },\n \"commit\": {\n \"type\": \"commit\",\n \"hash\": \"eefd5ef5d3df01aed629f650959d6706d54cd335\",\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/atlassian/bbql/commit/eefd5ef5d3df01aed629f650959d6706d54cd335\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/atlassian/bbql/commits/eefd5ef5d3df01aed629f650959d6706d54cd335\"\n }\n }\n }\n },\n {\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/atlassian/bbql/src/eefd5ef5d3df01aed629f650959d6706d54cd335/tests/__init__.py\"\n },\n \"meta\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/atlassian/bbql/src/eefd5ef5d3df01aed629f650959d6706d54cd335/tests/__init__.py?format=meta\"\n }\n },\n \"path\": \"tests/__init__.py\",\n \"commit\": {\n \"type\": \"commit\",\n \"hash\": \"eefd5ef5d3df01aed629f650959d6706d54cd335\",\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/atlassian/bbql/commit/eefd5ef5d3df01aed629f650959d6706d54cd335\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/atlassian/bbql/commits/eefd5ef5d3df01aed629f650959d6706d54cd335\"\n }\n }\n },\n \"attributes\": [],\n \"type\": \"commit_file\",\n \"size\": 0\n }\n ],\n \"page\": 1,\n \"size\": 2\n}\n```\n\nWhen listing the contents of the repo's root directory, the use of a\ntrailing slash at the end of the URL is required.\n\nThe response by default is not recursive, meaning that only the direct contents of\na path are returned. The response does not recurse down into\nsubdirectories. In order to \"walk\" the entire directory tree, the\nclient can either parse each response and follow the `self` links of each\n`commit_directory` object, or can specify a `max_depth` to recurse to.\n\nThe max_depth parameter will do a breadth-first search to return the contents of the subdirectories\nup to the depth specified. Breadth-first search was chosen as it leads to the least amount of\nfile system operations for git. If the `max_depth` parameter is specified to be too\nlarge, the call will time out and return a 555.\n\nEach returned object is either a `commit_file`, or a `commit_directory`,\nboth of which contain a `path` element. This path is the absolute path\nfrom the root of the repository. Each object also contains a `commit`\nobject which embeds the commit the file is on. Note that this is merely\nthe commit that was used in the URL. It is *not* the commit that last\nmodified the file.\n\nDirectory objects have 2 representations. Their `self` link returns the\npaginated contents of the directory. The `meta` link on the other hand\nreturns the actual `directory` object itself, e.g.:\n\n```javascript\n{\n \"path\": \"tests/test_project\",\n \"type\": \"commit_directory\",\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/atlassian/bbql/src/eefd5ef5d3df01aed629f650959d6706d54cd335/tests/test_project/\"\n },\n \"meta\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/atlassian/bbql/src/eefd5ef5d3df01aed629f650959d6706d54cd335/tests/test_project/?format=meta\"\n }\n },\n \"commit\": { ... }\n}\n```\n\n#### Querying, filtering and sorting\n\nLike most API endpoints, this API supports the Bitbucket\nquerying/filtering syntax and so you could filter a directory listing\nto only include entries that match certain criteria. For instance, to\nlist all binary files over 1kb use the expression:\n\n`size > 1024 and attributes = \"binary\"`\n\nwhich after urlencoding yields the query string:\n\n`?q=size%3E1024+and+attributes%3D%22binary%22`\n\nTo change the ordering of the response, use the `?sort` parameter:\n\n`.../src/eefd5ef/?sort=-size`\n\nSee [filtering and sorting](/cloud/bitbucket/rest/intro/#filtering) for more\ndetails." + }, + "parameters": [ + { + "name": "commit", + "in": "path", + "description": "The commit's SHA1.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "path", + "in": "path", + "description": "Path to the file.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/versions": { + "get": { + "responses": { + "200": { + "description": "The versions that have been defined in the issue tracker.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_versions" + } + } + } + }, + "404": { + "description": "The specified repository does not exist or does not have the issue tracker enabled.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Issue tracker"], + "summary": "List defined versions for issues", + "security": [ + { + "oauth2": ["issue"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns the versions that have been defined in the issue tracker.\n\nThis resource is only available on repositories that have the issue\ntracker enabled." + }, + "parameters": [ + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/versions/{version_id}": { + "get": { + "responses": { + "200": { + "description": "The specified version object.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/version" + } + } + } + }, + "404": { + "description": "The specified repository or version does not exist or does not have the issue tracker enabled.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Issue tracker"], + "summary": "Get a defined version for issues", + "security": [ + { + "oauth2": ["issue"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns the specified issue tracker version object." + }, + "parameters": [ + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "version_id", + "in": "path", + "description": "The version's id", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/repositories/{workspace}/{repo_slug}/watchers": { + "get": { + "responses": { + "200": { + "description": "A paginated list of all the watchers on the specified repository." + } + }, + "tags": ["Repositories"], + "summary": "List repositories watchers", + "security": [ + { + "oauth2": ["repository"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns a paginated list of all the watchers on the specified\nrepository." + }, + "parameters": [ + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/snippets": { + "get": { + "responses": { + "200": { + "description": "A paginated list of snippets.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_snippets" + } + } + } + }, + "404": { + "description": "If the snippet does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "parameters": [ + { + "name": "role", + "in": "query", + "description": "Filter down the result based on the authenticated user's role (`owner`, `contributor`, or `member`).", + "required": false, + "schema": { + "type": "string", + "enum": ["owner", "contributor", "member"] + } + } + ], + "tags": ["Snippets"], + "summary": "List snippets", + "security": [ + { + "oauth2": ["snippet"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns all snippets. Like pull requests, repositories and workspaces, the\nfull set of snippets is defined by what the current user has access to.\n\nThis includes all snippets owned by any of the workspaces the user is a member of,\nor snippets by other users that the current user is either watching or has collaborated\non (for instance by commenting on it).\n\nTo limit the set of returned snippets, apply the\n`?role=[owner|contributor|member]` query parameter where the roles are\ndefined as follows:\n\n* `owner`: all snippets owned by the current user\n* `contributor`: all snippets owned by, or watched by the current user\n* `member`: created in a workspaces or watched by the current user\n\nWhen no role is specified, all public snippets are returned, as well as all\nprivately owned snippets watched or commented on.\n\nThe returned response is a normal paginated JSON list. This endpoint\nonly supports `application/json` responses and no\n`multipart/form-data` or `multipart/related`. As a result, it is not\npossible to include the file contents." + }, + "post": { + "responses": { + "201": { + "description": "The newly created snippet object.", + "headers": { + "Location": { + "description": "The URL of the newly created snippet.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/snippet" + } + } + } + }, + "401": { + "description": "If the request was not authenticated", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "requestBody": { + "$ref": "#/components/requestBodies/snippet" + }, + "tags": ["Snippets"], + "summary": "Create a snippet", + "security": [ + { + "oauth2": ["snippet:write"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Creates a new snippet under the authenticated user's account.\n\nSnippets can contain multiple files. Both text and binary files are\nsupported.\n\nThe simplest way to create a new snippet from a local file:\n\n $ curl -u username:password -X POST https://api.bitbucket.org/2.0/snippets -F file=@image.png\n\nCreating snippets through curl has a few limitations and so let's look\nat a more complicated scenario.\n\nSnippets are created with a multipart POST. Both `multipart/form-data`\nand `multipart/related` are supported. Both allow the creation of\nsnippets with both meta data (title, etc), as well as multiple text\nand binary files.\n\nThe main difference is that `multipart/related` can use rich encoding\nfor the meta data (currently JSON).\n\n\nmultipart/related (RFC-2387)\n----------------------------\n\nThis is the most advanced and efficient way to create a paste.\n\n POST /2.0/snippets/evzijst HTTP/1.1\n Content-Length: 1188\n Content-Type: multipart/related; start=\"snippet\"; boundary=\"===============1438169132528273974==\"\n MIME-Version: 1.0\n\n --===============1438169132528273974==\n Content-Type: application/json; charset=\"utf-8\"\n MIME-Version: 1.0\n Content-ID: snippet\n\n {\n \"title\": \"My snippet\",\n \"is_private\": true,\n \"scm\": \"git\",\n \"files\": {\n \"foo.txt\": {},\n \"image.png\": {}\n }\n }\n\n --===============1438169132528273974==\n Content-Type: text/plain; charset=\"us-ascii\"\n MIME-Version: 1.0\n Content-Transfer-Encoding: 7bit\n Content-ID: \"foo.txt\"\n Content-Disposition: attachment; filename=\"foo.txt\"\n\n foo\n\n --===============1438169132528273974==\n Content-Type: image/png\n MIME-Version: 1.0\n Content-Transfer-Encoding: base64\n Content-ID: \"image.png\"\n Content-Disposition: attachment; filename=\"image.png\"\n\n iVBORw0KGgoAAAANSUhEUgAAABQAAAAoCAYAAAD+MdrbAAABD0lEQVR4Ae3VMUoDQRTG8ccUaW2m\n TKONFxArJYJamCvkCnZTaa+VnQdJSBFl2SMsLFrEWNjZBZs0JgiL/+KrhhVmJRbCLPx4O+/DT2TB\n cbblJxf+UWFVVRNsEGAtgvJxnLm2H+A5RQ93uIl+3632PZyl/skjfOn9Gvdwmlcw5aPUwimG+NT5\n EnNN036IaZePUuIcK533NVfal7/5yjWeot2z9ta1cAczHEf7I+3J0ws9Cgx0fsOFpmlfwKcWPuBQ\n 73Oc4FHzBaZ8llq4q1mr5B2mOUCt815qYR8eB1hG2VJ7j35q4RofaH7IG+Xrf/PfJhfmwtfFYoIN\n AqxFUD6OMxcvkO+UfKfkOyXfKdsv/AYCHMLVkHAFWgAAAABJRU5ErkJggg==\n --===============1438169132528273974==--\n\nThe request contains multiple parts and is structured as follows.\n\nThe first part is the JSON document that describes the snippet's\nproperties or meta data. It either has to be the first part, or the\nrequest's `Content-Type` header must contain the `start` parameter to\npoint to it.\n\nThe remaining parts are the files of which there can be zero or more.\nEach file part should contain the `Content-ID` MIME header through\nwhich the JSON meta data's `files` element addresses it. The value\nshould be the name of the file.\n\n`Content-Disposition` is an optional MIME header. The header's\noptional `filename` parameter can be used to specify the file name\nthat Bitbucket should use when writing the file to disk. When present,\n`filename` takes precedence over the value of `Content-ID`.\n\nWhen the JSON body omits the `files` element, the remaining parts are\nnot ignored. Instead, each file is added to the new snippet as if its\nname was explicitly linked (the use of the `files` elements is\nmandatory for some operations like deleting or renaming files).\n\n\nmultipart/form-data\n-------------------\n\nThe use of JSON for the snippet's meta data is optional. Meta data can\nalso be supplied as regular form fields in a more conventional\n`multipart/form-data` request:\n\n $ curl -X POST -u credentials https://api.bitbucket.org/2.0/snippets -F title=\"My snippet\" -F file=@foo.txt -F file=@image.png\n\n POST /2.0/snippets HTTP/1.1\n Content-Length: 951\n Content-Type: multipart/form-data; boundary=----------------------------63a4b224c59f\n\n ------------------------------63a4b224c59f\n Content-Disposition: form-data; name=\"file\"; filename=\"foo.txt\"\n Content-Type: text/plain\n\n foo\n\n ------------------------------63a4b224c59f\n Content-Disposition: form-data; name=\"file\"; filename=\"image.png\"\n Content-Type: application/octet-stream\n\n ?PNG\n\n IHDR?1??I.....\n ------------------------------63a4b224c59f\n Content-Disposition: form-data; name=\"title\"\n\n My snippet\n ------------------------------63a4b224c59f--\n\nHere the meta data properties are included as flat, top-level form\nfields. The file attachments use the `file` field name. To attach\nmultiple files, simply repeat the field.\n\nThe advantage of `multipart/form-data` over `multipart/related` is\nthat it can be easier to build clients.\n\nEssentially all properties are optional, `title` and `files` included.\n\n\nSharing and Visibility\n----------------------\n\nSnippets can be either public (visible to anyone on Bitbucket, as well\nas anonymous users), or private (visible only to members of the workspace).\nThis is controlled through the snippet's `is_private` element:\n\n* **is_private=false** -- everyone, including anonymous users can view\n the snippet\n* **is_private=true** -- only workspace members can view the snippet\n\nTo create the snippet under a workspace, just append the workspace ID\nto the URL. See [`/2.0/snippets/{workspace}`](/cloud/bitbucket/rest/api-group-snippets/#api-snippets-workspace-post)." + }, + "parameters": [] + }, + "/snippets/{workspace}": { + "get": { + "responses": { + "200": { + "description": "A paginated list of snippets.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_snippets" + } + } + } + }, + "404": { + "description": "If the user does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "parameters": [ + { + "name": "role", + "in": "query", + "description": "Filter down the result based on the authenticated user's role (`owner`, `contributor`, or `member`).", + "required": false, + "schema": { + "type": "string", + "enum": ["owner", "contributor", "member"] + } + } + ], + "tags": ["Snippets"], + "summary": "List snippets in a workspace", + "security": [ + { + "oauth2": ["snippet"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Identical to [`/snippets`](/cloud/bitbucket/rest/api-group-snippets/#api-snippets-get), except that the result is further filtered\nby the snippet owner and only those that are owned by `{workspace}` are\nreturned." + }, + "post": { + "responses": { + "201": { + "description": "The newly created snippet object.", + "headers": { + "Location": { + "description": "The URL of the newly created snippet.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/snippet" + } + } + } + }, + "401": { + "description": "If the request was not authenticated", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "403": { + "description": "If the authenticated user does not have permission to create snippets in the specified workspace.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "requestBody": { + "$ref": "#/components/requestBodies/snippet" + }, + "tags": ["Snippets"], + "summary": "Create a snippet for a workspace", + "security": [ + { + "oauth2": ["snippet:write"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Identical to [`/snippets`](/cloud/bitbucket/rest/api-group-snippets/#api-snippets-post), except that the new snippet will be\ncreated under the workspace specified in the path parameter\n`{workspace}`." + }, + "parameters": [ + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/snippets/{workspace}/{encoded_id}": { + "delete": { + "responses": { + "204": { + "description": "If the snippet was deleted successfully." + }, + "401": { + "description": "If the snippet is private and the request was not authenticated.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "403": { + "description": "If authenticated user does not have permission to delete the private snippet.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "If the snippet does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Snippets"], + "summary": "Delete a snippet", + "security": [ + { + "oauth2": ["snippet:write"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Deletes a snippet and returns an empty response." + }, + "get": { + "responses": { + "200": { + "description": "The snippet object.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/snippet" + } + }, + "multipart/related": { + "schema": { + "$ref": "#/components/schemas/snippet" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/snippet" + } + } + } + }, + "401": { + "description": "If the snippet is private and the request was not authenticated.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + }, + "multipart/related": { + "schema": { + "$ref": "#/components/schemas/error" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "403": { + "description": "If authenticated user does not have access to the private snippet.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + }, + "multipart/related": { + "schema": { + "$ref": "#/components/schemas/error" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "If the snippet does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + }, + "multipart/related": { + "schema": { + "$ref": "#/components/schemas/error" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "410": { + "description": "If the snippet marked as spam.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + }, + "multipart/related": { + "schema": { + "$ref": "#/components/schemas/error" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Snippets"], + "summary": "Get a snippet", + "security": [ + { + "oauth2": ["snippet"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Retrieves a single snippet.\n\nSnippets support multiple content types:\n\n* application/json\n* multipart/related\n* multipart/form-data\n\n\napplication/json\n----------------\n\nThe default content type of the response is `application/json`.\nSince JSON is always `utf-8`, it cannot reliably contain file contents\nfor files that are not text. Therefore, JSON snippet documents only\ncontain the filename and links to the file contents.\n\nThis means that in order to retrieve all parts of a snippet, N+1\nrequests need to be made (where N is the number of files in the\nsnippet).\n\n\nmultipart/related\n-----------------\n\nTo retrieve an entire snippet in a single response, use the\n`Accept: multipart/related` HTTP request header.\n\n $ curl -H \"Accept: multipart/related\" https://api.bitbucket.org/2.0/snippets/evzijst/1\n\nResponse:\n\n HTTP/1.1 200 OK\n Content-Length: 2214\n Content-Type: multipart/related; start=\"snippet\"; boundary=\"===============1438169132528273974==\"\n MIME-Version: 1.0\n\n --===============1438169132528273974==\n Content-Type: application/json; charset=\"utf-8\"\n MIME-Version: 1.0\n Content-ID: snippet\n\n {\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/snippets/evzijst/kypj\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/snippets/evzijst/kypj\"\n },\n \"comments\": {\n \"href\": \"https://api.bitbucket.org/2.0/snippets/evzijst/kypj/comments\"\n },\n \"watchers\": {\n \"href\": \"https://api.bitbucket.org/2.0/snippets/evzijst/kypj/watchers\"\n },\n \"commits\": {\n \"href\": \"https://api.bitbucket.org/2.0/snippets/evzijst/kypj/commits\"\n }\n },\n \"id\": kypj,\n \"title\": \"My snippet\",\n \"created_on\": \"2014-12-29T22:22:04.790331+00:00\",\n \"updated_on\": \"2014-12-29T22:22:04.790331+00:00\",\n \"is_private\": false,\n \"files\": {\n \"foo.txt\": {\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/snippets/evzijst/kypj/files/367ab19/foo.txt\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/snippets/evzijst/kypj#file-foo.txt\"\n }\n }\n },\n \"image.png\": {\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/snippets/evzijst/kypj/files/367ab19/image.png\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/snippets/evzijst/kypj#file-image.png\"\n }\n }\n }\n ],\n \"owner\": {\n \"username\": \"evzijst\",\n \"nickname\": \"evzijst\",\n \"display_name\": \"Erik van Zijst\",\n \"uuid\": \"{d301aafa-d676-4ee0-88be-962be7417567}\",\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/users/evzijst\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/evzijst\"\n },\n \"avatar\": {\n \"href\": \"https://bitbucket-staging-assetroot.s3.amazonaws.com/c/photos/2013/Jul/31/erik-avatar-725122544-0_avatar.png\"\n }\n }\n },\n \"creator\": {\n \"username\": \"evzijst\",\n \"nickname\": \"evzijst\",\n \"display_name\": \"Erik van Zijst\",\n \"uuid\": \"{d301aafa-d676-4ee0-88be-962be7417567}\",\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/users/evzijst\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/evzijst\"\n },\n \"avatar\": {\n \"href\": \"https://bitbucket-staging-assetroot.s3.amazonaws.com/c/photos/2013/Jul/31/erik-avatar-725122544-0_avatar.png\"\n }\n }\n }\n }\n\n --===============1438169132528273974==\n Content-Type: text/plain; charset=\"us-ascii\"\n MIME-Version: 1.0\n Content-Transfer-Encoding: 7bit\n Content-ID: \"foo.txt\"\n Content-Disposition: attachment; filename=\"foo.txt\"\n\n foo\n\n --===============1438169132528273974==\n Content-Type: image/png\n MIME-Version: 1.0\n Content-Transfer-Encoding: base64\n Content-ID: \"image.png\"\n Content-Disposition: attachment; filename=\"image.png\"\n\n iVBORw0KGgoAAAANSUhEUgAAABQAAAAoCAYAAAD+MdrbAAABD0lEQVR4Ae3VMUoDQRTG8ccUaW2m\n TKONFxArJYJamCvkCnZTaa+VnQdJSBFl2SMsLFrEWNjZBZs0JgiL/+KrhhVmJRbCLPx4O+/DT2TB\n cbblJxf+UWFVVRNsEGAtgvJxnLm2H+A5RQ93uIl+3632PZyl/skjfOn9Gvdwmlcw5aPUwimG+NT5\n EnNN036IaZePUuIcK533NVfal7/5yjWeot2z9ta1cAczHEf7I+3J0ws9Cgx0fsOFpmlfwKcWPuBQ\n 73Oc4FHzBaZ8llq4q1mr5B2mOUCt815qYR8eB1hG2VJ7j35q4RofaH7IG+Xrf/PfJhfmwtfFYoIN\n AqxFUD6OMxcvkO+UfKfkOyXfKdsv/AYCHMLVkHAFWgAAAABJRU5ErkJggg==\n --===============1438169132528273974==--\n\nmultipart/form-data\n-------------------\n\nAs with creating new snippets, `multipart/form-data` can be used as an\nalternative to `multipart/related`. However, the inherently flat\nstructure of form-data means that only basic, root-level properties\ncan be returned, while nested elements like `links` are omitted:\n\n $ curl -H \"Accept: multipart/form-data\" https://api.bitbucket.org/2.0/snippets/evzijst/kypj\n\nResponse:\n\n HTTP/1.1 200 OK\n Content-Length: 951\n Content-Type: multipart/form-data; boundary=----------------------------63a4b224c59f\n\n ------------------------------63a4b224c59f\n Content-Disposition: form-data; name=\"title\"\n Content-Type: text/plain; charset=\"utf-8\"\n\n My snippet\n ------------------------------63a4b224c59f--\n Content-Disposition: attachment; name=\"file\"; filename=\"foo.txt\"\n Content-Type: text/plain\n\n foo\n\n ------------------------------63a4b224c59f\n Content-Disposition: attachment; name=\"file\"; filename=\"image.png\"\n Content-Transfer-Encoding: base64\n Content-Type: application/octet-stream\n\n iVBORw0KGgoAAAANSUhEUgAAABQAAAAoCAYAAAD+MdrbAAABD0lEQVR4Ae3VMUoDQRTG8ccUaW2m\n TKONFxArJYJamCvkCnZTaa+VnQdJSBFl2SMsLFrEWNjZBZs0JgiL/+KrhhVmJRbCLPx4O+/DT2TB\n cbblJxf+UWFVVRNsEGAtgvJxnLm2H+A5RQ93uIl+3632PZyl/skjfOn9Gvdwmlcw5aPUwimG+NT5\n EnNN036IaZePUuIcK533NVfal7/5yjWeot2z9ta1cAczHEf7I+3J0ws9Cgx0fsOFpmlfwKcWPuBQ\n 73Oc4FHzBaZ8llq4q1mr5B2mOUCt815qYR8eB1hG2VJ7j35q4RofaH7IG+Xrf/PfJhfmwtfFYoIN\n AqxFUD6OMxcvkO+UfKfkOyXfKdsv/AYCHMLVkHAFWgAAAABJRU5ErkJggg==\n ------------------------------5957323a6b76--" + }, + "put": { + "responses": { + "200": { + "description": "The updated snippet object.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/snippet" + } + }, + "multipart/related": { + "schema": { + "$ref": "#/components/schemas/snippet" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/snippet" + } + } + } + }, + "401": { + "description": "If the snippet is private and the request was not authenticated.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + }, + "multipart/related": { + "schema": { + "$ref": "#/components/schemas/error" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "403": { + "description": "If authenticated user does not have permission to update the private snippet.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + }, + "multipart/related": { + "schema": { + "$ref": "#/components/schemas/error" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "If the snippet does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + }, + "multipart/related": { + "schema": { + "$ref": "#/components/schemas/error" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Snippets"], + "summary": "Update a snippet", + "security": [ + { + "oauth2": ["snippet:write"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Used to update a snippet. Use this to add and delete files and to\nchange a snippet's title.\n\nTo update a snippet, one can either PUT a full snapshot, or only the\nparts that need to be changed.\n\nThe contract for PUT on this API is that properties missing from the\nrequest remain untouched so that snippets can be efficiently\nmanipulated with differential payloads.\n\nTo delete a property (e.g. the title, or a file), include its name in\nthe request, but omit its value (use `null`).\n\nAs in Git, explicit renaming of files is not supported. Instead, to\nrename a file, delete it and add it again under another name. This can\nbe done atomically in a single request. Rename detection is left to\nthe SCM.\n\nPUT supports three different content types for both request and\nresponse bodies:\n\n* `application/json`\n* `multipart/related`\n* `multipart/form-data`\n\nThe content type used for the request body can be different than that\nused for the response. Content types are specified using standard HTTP\nheaders.\n\nUse the `Content-Type` and `Accept` headers to select the desired\nrequest and response format.\n\n\napplication/json\n----------------\n\nAs with creation and retrieval, the content type determines what\nproperties can be manipulated. `application/json` does not support\nfile contents and is therefore limited to a snippet's meta data.\n\nTo update the title, without changing any of its files:\n\n $ curl -X POST -H \"Content-Type: application/json\" https://api.bitbucket.org/2.0/snippets/evzijst/kypj -d '{\"title\": \"Updated title\"}'\n\n\nTo delete the title:\n\n $ curl -X POST -H \"Content-Type: application/json\" https://api.bitbucket.org/2.0/snippets/evzijst/kypj -d '{\"title\": null}'\n\nNot all parts of a snippet can be manipulated. The owner and creator\nfor instance are immutable.\n\n\nmultipart/related\n-----------------\n\n`multipart/related` can be used to manipulate all of a snippet's\nproperties. The body is identical to a POST. properties omitted from\nthe request are left unchanged. Since the `start` part contains JSON,\nthe mechanism for manipulating the snippet's meta data is identical\nto `application/json` requests.\n\nTo update one of a snippet's file contents, while also changing its\ntitle:\n\n PUT /2.0/snippets/evzijst/kypj HTTP/1.1\n Content-Length: 288\n Content-Type: multipart/related; start=\"snippet\"; boundary=\"===============1438169132528273974==\"\n MIME-Version: 1.0\n\n --===============1438169132528273974==\n Content-Type: application/json; charset=\"utf-8\"\n MIME-Version: 1.0\n Content-ID: snippet\n\n {\n \"title\": \"My updated snippet\",\n \"files\": {\n \"foo.txt\": {}\n }\n }\n\n --===============1438169132528273974==\n Content-Type: text/plain; charset=\"us-ascii\"\n MIME-Version: 1.0\n Content-Transfer-Encoding: 7bit\n Content-ID: \"foo.txt\"\n Content-Disposition: attachment; filename=\"foo.txt\"\n\n Updated file contents.\n\n --===============1438169132528273974==--\n\nHere only the parts that are changed are included in the body. The\nother files remain untouched.\n\nNote the use of the `files` list in the JSON part. This list contains\nthe files that are being manipulated. This list should have\ncorresponding multiparts in the request that contain the new contents\nof these files.\n\nIf a filename in the `files` list does not have a corresponding part,\nit will be deleted from the snippet, as shown below:\n\n PUT /2.0/snippets/evzijst/kypj HTTP/1.1\n Content-Length: 188\n Content-Type: multipart/related; start=\"snippet\"; boundary=\"===============1438169132528273974==\"\n MIME-Version: 1.0\n\n --===============1438169132528273974==\n Content-Type: application/json; charset=\"utf-8\"\n MIME-Version: 1.0\n Content-ID: snippet\n\n {\n \"files\": {\n \"image.png\": {}\n }\n }\n\n --===============1438169132528273974==--\n\nTo simulate a rename, delete a file and add the same file under\nanother name:\n\n PUT /2.0/snippets/evzijst/kypj HTTP/1.1\n Content-Length: 212\n Content-Type: multipart/related; start=\"snippet\"; boundary=\"===============1438169132528273974==\"\n MIME-Version: 1.0\n\n --===============1438169132528273974==\n Content-Type: application/json; charset=\"utf-8\"\n MIME-Version: 1.0\n Content-ID: snippet\n\n {\n \"files\": {\n \"foo.txt\": {},\n \"bar.txt\": {}\n }\n }\n\n --===============1438169132528273974==\n Content-Type: text/plain; charset=\"us-ascii\"\n MIME-Version: 1.0\n Content-Transfer-Encoding: 7bit\n Content-ID: \"bar.txt\"\n Content-Disposition: attachment; filename=\"bar.txt\"\n\n foo\n\n --===============1438169132528273974==--\n\n\nmultipart/form-data\n-----------------\n\nAgain, one can also use `multipart/form-data` to manipulate file\ncontents and meta data atomically.\n\n $ curl -X PUT http://localhost:12345/2.0/snippets/evzijst/kypj -F title=\"My updated snippet\" -F file=@foo.txt\n\n PUT /2.0/snippets/evzijst/kypj HTTP/1.1\n Content-Length: 351\n Content-Type: multipart/form-data; boundary=----------------------------63a4b224c59f\n\n ------------------------------63a4b224c59f\n Content-Disposition: form-data; name=\"file\"; filename=\"foo.txt\"\n Content-Type: text/plain\n\n foo\n\n ------------------------------63a4b224c59f\n Content-Disposition: form-data; name=\"title\"\n\n My updated snippet\n ------------------------------63a4b224c59f\n\nTo delete a file, omit its contents while including its name in the\n`files` field:\n\n $ curl -X PUT https://api.bitbucket.org/2.0/snippets/evzijst/kypj -F files=image.png\n\n PUT /2.0/snippets/evzijst/kypj HTTP/1.1\n Content-Length: 149\n Content-Type: multipart/form-data; boundary=----------------------------ef8871065a86\n\n ------------------------------ef8871065a86\n Content-Disposition: form-data; name=\"files\"\n\n image.png\n ------------------------------ef8871065a86--\n\nThe explicit use of the `files` element in `multipart/related` and\n`multipart/form-data` is only required when deleting files.\nThe default mode of operation is for file parts to be processed,\nregardless of whether or not they are listed in `files`, as a\nconvenience to the client." + }, + "parameters": [ + { + "name": "encoded_id", + "in": "path", + "description": "The snippet id.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/snippets/{workspace}/{encoded_id}/comments": { + "get": { + "responses": { + "200": { + "description": "A paginated list of snippet comments, ordered by creation date.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_snippet_comments" + } + } + } + }, + "403": { + "description": "If the authenticated user does not have access to the snippet.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "If the snippet does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Snippets"], + "summary": "List comments on a snippet", + "security": [ + { + "oauth2": ["snippet"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Used to retrieve a paginated list of all comments for a specific\nsnippet.\n\nThis resource works identical to commit and pull request comments.\n\nThe default sorting is oldest to newest and can be overridden with\nthe `sort` query parameter." + }, + "post": { + "responses": { + "201": { + "description": "The newly created comment.", + "headers": { + "Location": { + "description": "The URL of the new comment", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/snippet" + } + } + } + }, + "403": { + "description": "If the authenticated user does not have access to the snippet.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "If the snippet does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/snippet" + } + } + }, + "description": "The contents of the new comment.", + "required": true + }, + "tags": ["Snippets"], + "summary": "Create a comment on a snippet", + "security": [ + { + "oauth2": ["snippet"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Creates a new comment.\n\nThe only required field in the body is `content.raw`.\n\nTo create a threaded reply to an existing comment, include `parent.id`." + }, + "parameters": [ + { + "name": "encoded_id", + "in": "path", + "description": "The snippet id.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/snippets/{workspace}/{encoded_id}/comments/{comment_id}": { + "delete": { + "responses": { + "204": { + "description": "Indicates the comment was deleted successfully." + }, + "403": { + "description": "If the authenticated user is not the author of the comment.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "If the comment or the snippet does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Snippets"], + "summary": "Delete a comment on a snippet", + "security": [ + { + "oauth2": ["snippet"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Deletes a snippet comment.\n\nComments can only be removed by the comment author, snippet creator, or workspace admin." + }, + "get": { + "responses": { + "200": { + "description": "The specified comment.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/snippet_comment" + } + } + } + }, + "403": { + "description": "If the authenticated user does not have access to the snippet.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "If the comment or snippet does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Snippets"], + "summary": "Get a comment on a snippet", + "security": [ + { + "oauth2": ["snippet"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns the specific snippet comment." + }, + "put": { + "responses": { + "200": { + "description": "The updated comment object." + }, + "403": { + "description": "If the authenticated user does not have access to the snippet.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "If the comment or snippet does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Snippets"], + "summary": "Update a comment on a snippet", + "security": [ + { + "oauth2": ["snippet"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Updates a comment.\n\nComments can only be updated by their author." + }, + "parameters": [ + { + "name": "comment_id", + "in": "path", + "description": "The id of the comment.", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "name": "encoded_id", + "in": "path", + "description": "The snippet id.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/snippets/{workspace}/{encoded_id}/commits": { + "get": { + "responses": { + "200": { + "description": "The paginated list of snippet commits.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_snippet_commits" + } + } + } + }, + "403": { + "description": "If the authenticated user does not have access to the snippet.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "If the snippet does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Snippets"], + "summary": "List snippet changes", + "security": [ + { + "oauth2": ["snippet"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns the changes (commits) made on this snippet." + }, + "parameters": [ + { + "name": "encoded_id", + "in": "path", + "description": "The snippet id.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/snippets/{workspace}/{encoded_id}/commits/{revision}": { + "get": { + "responses": { + "200": { + "description": "The specified snippet commit.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/snippet_commit" + } + } + } + }, + "403": { + "description": "If the authenticated user does not have access to the snippet.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "If the commit or the snippet does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Snippets"], + "summary": "Get a previous snippet change", + "security": [ + { + "oauth2": ["snippet"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns the changes made on this snippet in this commit." + }, + "parameters": [ + { + "name": "encoded_id", + "in": "path", + "description": "The snippet id.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "revision", + "in": "path", + "description": "The commit's SHA1.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/snippets/{workspace}/{encoded_id}/files/{path}": { + "get": { + "responses": { + "302": { + "description": "A redirect to the most recent revision of the specified file.", + "headers": { + "Location": { + "description": "The URL of the most recent file revision.", + "schema": { + "type": "string" + } + } + } + }, + "403": { + "description": "If the authenticated user does not have access to the snippet.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "If the snippet does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Snippets"], + "summary": "Get a snippet's raw file at HEAD", + "security": [ + { + "oauth2": ["snippet"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Convenience resource for getting to a snippet's raw files without the\nneed for first having to retrieve the snippet itself and having to pull\nout the versioned file links." + }, + "parameters": [ + { + "name": "encoded_id", + "in": "path", + "description": "The snippet id.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "path", + "in": "path", + "description": "Path to the file.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/snippets/{workspace}/{encoded_id}/watch": { + "delete": { + "responses": { + "204": { + "description": "Indicates the user stopped watching the snippet successfully.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_users" + } + } + } + }, + "401": { + "description": "If the request was not authenticated.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "If the snippet does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Snippets"], + "summary": "Stop watching a snippet", + "security": [ + { + "oauth2": ["snippet:write"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Used to stop watching a specific snippet. Returns 204 (No Content)\nto indicate success." + }, + "get": { + "responses": { + "204": { + "description": "If the authenticated user is watching the snippet.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_users" + } + } + } + }, + "404": { + "description": "If the snippet does not exist, or if the authenticated user is not watching the snippet.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Snippets"], + "summary": "Check if the current user is watching a snippet", + "security": [ + { + "oauth2": ["snippet"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Used to check if the current user is watching a specific snippet.\n\nReturns 204 (No Content) if the user is watching the snippet and 404 if\nnot.\n\nHitting this endpoint anonymously always returns a 404." + }, + "put": { + "responses": { + "204": { + "description": "Indicates the authenticated user is now watching the snippet.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_users" + } + } + } + }, + "401": { + "description": "If the request was not authenticated.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "If the snippet does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Snippets"], + "summary": "Watch a snippet", + "security": [ + { + "oauth2": ["snippet:write"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Used to start watching a specific snippet. Returns 204 (No Content)." + }, + "parameters": [ + { + "name": "encoded_id", + "in": "path", + "description": "The snippet id.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/snippets/{workspace}/{encoded_id}/watchers": { + "get": { + "responses": { + "200": { + "description": "The paginated list of users watching this snippet", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_users" + } + } + } + }, + "404": { + "description": "If the snippet does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Snippets"], + "summary": "List users watching a snippet", + "security": [ + { + "oauth2": ["snippet"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns a paginated list of all users watching a specific snippet.", + "deprecated": true + }, + "parameters": [ + { + "name": "encoded_id", + "in": "path", + "description": "The snippet id.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/snippets/{workspace}/{encoded_id}/{node_id}": { + "delete": { + "responses": { + "204": { + "description": "If the snippet was deleted successfully." + }, + "401": { + "description": "If the snippet is private and the request was not authenticated.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "403": { + "description": "If authenticated user does not have permission to delete the private snippet.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "If the snippet does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "405": { + "description": "If `{node_id}` is not the latest revision.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Snippets"], + "summary": "Delete a previous revision of a snippet", + "security": [ + { + "oauth2": ["snippet:write"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Deletes the snippet.\n\nNote that this only works for versioned URLs that point to the latest\ncommit of the snippet. Pointing to an older commit results in a 405\nstatus code.\n\nTo delete a snippet, regardless of whether or not concurrent changes\nare being made to it, use `DELETE /snippets/{encoded_id}` instead." + }, + "get": { + "responses": { + "200": { + "description": "The snippet object.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/snippet" + } + }, + "multipart/related": { + "schema": { + "$ref": "#/components/schemas/snippet" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/snippet" + } + } + } + }, + "401": { + "description": "If the snippet is private and the request was not authenticated.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + }, + "multipart/related": { + "schema": { + "$ref": "#/components/schemas/error" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "403": { + "description": "If authenticated user does not have access to the private snippet.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + }, + "multipart/related": { + "schema": { + "$ref": "#/components/schemas/error" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "If the snippet, or the revision does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + }, + "multipart/related": { + "schema": { + "$ref": "#/components/schemas/error" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Snippets"], + "summary": "Get a previous revision of a snippet", + "security": [ + { + "oauth2": ["snippet"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Identical to `GET /snippets/encoded_id`, except that this endpoint\ncan be used to retrieve the contents of the snippet as it was at an\nolder revision, while `/snippets/encoded_id` always returns the\nsnippet's current revision.\n\nNote that only the snippet's file contents are versioned, not its\nmeta data properties like the title.\n\nOther than that, the two endpoints are identical in behavior." + }, + "put": { + "responses": { + "200": { + "description": "The updated snippet object.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/snippet" + } + }, + "multipart/related": { + "schema": { + "$ref": "#/components/schemas/snippet" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/snippet" + } + } + } + }, + "401": { + "description": "If the snippet is private and the request was not authenticated.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + }, + "multipart/related": { + "schema": { + "$ref": "#/components/schemas/error" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "403": { + "description": "If authenticated user does not have permission to update the private snippet.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + }, + "multipart/related": { + "schema": { + "$ref": "#/components/schemas/error" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "If the snippet or the revision does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + }, + "multipart/related": { + "schema": { + "$ref": "#/components/schemas/error" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "405": { + "description": "If `{node_id}` is not the latest revision.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + }, + "multipart/related": { + "schema": { + "$ref": "#/components/schemas/error" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Snippets"], + "summary": "Update a previous revision of a snippet", + "security": [ + { + "oauth2": ["snippet:write"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Identical to `UPDATE /snippets/encoded_id`, except that this endpoint\ntakes an explicit commit revision. Only the snippet's \"HEAD\"/\"tip\"\n(most recent) version can be updated and requests on all other,\nolder revisions fail by returning a 405 status.\n\nUsage of this endpoint over the unrestricted `/snippets/encoded_id`\ncould be desired if the caller wants to be sure no concurrent\nmodifications have taken place between the moment of the UPDATE\nrequest and the original GET.\n\nThis can be considered a so-called \"Compare And Swap\", or CAS\noperation.\n\nOther than that, the two endpoints are identical in behavior." + }, + "parameters": [ + { + "name": "encoded_id", + "in": "path", + "description": "The snippet id.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "node_id", + "in": "path", + "description": "A commit revision (SHA1).", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/snippets/{workspace}/{encoded_id}/{node_id}/files/{path}": { + "get": { + "responses": { + "200": { + "description": "Returns the contents of the specified file.", + "headers": { + "Content-Type": { + "description": "The mime type as derived from the filename", + "schema": { + "type": "string" + } + }, + "Content-Disposition": { + "description": "attachment", + "schema": { + "type": "string" + } + } + } + }, + "403": { + "description": "If the authenticated user does not have access to the snippet.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "If the file or snippet does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Snippets"], + "summary": "Get a snippet's raw file", + "security": [ + { + "oauth2": ["snippet"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Retrieves the raw contents of a specific file in the snippet. The\n`Content-Disposition` header will be \"attachment\" to avoid issues with\nmalevolent executable files.\n\nThe file's mime type is derived from its filename and returned in the\n`Content-Type` header.\n\nNote that for text files, no character encoding is included as part of\nthe content type." + }, + "parameters": [ + { + "name": "encoded_id", + "in": "path", + "description": "The snippet id.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "node_id", + "in": "path", + "description": "A commit revision (SHA1).", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "path", + "in": "path", + "description": "Path to the file.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/snippets/{workspace}/{encoded_id}/{revision}/diff": { + "get": { + "responses": { + "200": { + "description": "The raw diff contents." + }, + "403": { + "description": "If the authenticated user does not have access to the snippet.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "If the snippet does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "parameters": [ + { + "name": "path", + "in": "query", + "description": "When used, only one the diff of the specified file will be returned.", + "schema": { + "type": "string" + } + } + ], + "tags": ["Snippets"], + "summary": "Get snippet changes between versions", + "security": [ + { + "oauth2": ["snippet"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns the diff of the specified commit against its first parent.\n\nNote that this resource is different in functionality from the `patch`\nresource.\n\nThe differences between a diff and a patch are:\n\n* patches have a commit header with the username, message, etc\n* diffs support the optional `path=foo/bar.py` query param to filter the\n diff to just that one file diff (not supported for patches)\n* for a merge, the diff will show the diff between the merge commit and\n its first parent (identical to how PRs work), while patch returns a\n response containing separate patches for each commit on the second\n parent's ancestry, up to the oldest common ancestor (identical to\n its reachability).\n\nNote that the character encoding of the contents of the diff is\nunspecified as Git does not track this, making it hard for\nBitbucket to reliably determine this." + }, + "parameters": [ + { + "name": "encoded_id", + "in": "path", + "description": "The snippet id.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "revision", + "in": "path", + "description": "A revspec expression. This can simply be a commit SHA1, a ref name, or a compare expression like `staging..production`.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/snippets/{workspace}/{encoded_id}/{revision}/patch": { + "get": { + "responses": { + "200": { + "description": "The raw patch contents." + }, + "403": { + "description": "If the authenticated user does not have access to the snippet.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "If the snippet does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Snippets"], + "summary": "Get snippet patch between versions", + "security": [ + { + "oauth2": ["snippet"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns the patch of the specified commit against its first\nparent.\n\nNote that this resource is different in functionality from the `diff`\nresource.\n\nThe differences between a diff and a patch are:\n\n* patches have a commit header with the username, message, etc\n* diffs support the optional `path=foo/bar.py` query param to filter the\n diff to just that one file diff (not supported for patches)\n* for a merge, the diff will show the diff between the merge commit and\n its first parent (identical to how PRs work), while patch returns a\n response containing separate patches for each commit on the second\n parent's ancestry, up to the oldest common ancestor (identical to\n its reachability).\n\nNote that the character encoding of the contents of the patch is\nunspecified as Git does not track this, making it hard for\nBitbucket to reliably determine this." + }, + "parameters": [ + { + "name": "encoded_id", + "in": "path", + "description": "The snippet id.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "revision", + "in": "path", + "description": "A revspec expression. This can simply be a commit SHA1, a ref name, or a compare expression like `staging..production`.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/teams": { + "get": { + "responses": { + "200": { + "description": "A paginated list of teams.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_teams" + } + } + } + }, + "401": { + "description": "When the request wasn't authenticated.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "parameters": [ + { + "name": "role", + "in": "query", + "description": "\nFilters the teams based on the authenticated user's role on each team.\n\n* **member**: returns a list of all the teams which the caller is a member of\n at least one team group or repository owned by the team\n* **contributor**: returns a list of teams which the caller has write access\n to at least one repository owned by the team\n* **admin**: returns a list teams which the caller has team administrator access\n", + "required": false, + "schema": { + "type": "string", + "enum": ["admin", "contributor", "member"] + } + } + ], + "tags": ["Teams"], + "summary": "List teams a user is part of", + "security": [ + { + "oauth2": ["team"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns all the teams that the authenticated user is associated\nwith.\n\n**This endpoint has been removed.\nYou should use the [workspaces](/cloud/bitbucket/rest/api-group-workspaces/#api-workspaces-get) endpoint instead.\nFor more information, see [this post](https://developer.atlassian.com/cloud/bitbucket/bitbucket-api-teams-deprecation/).**", + "deprecated": true + }, + "parameters": [] + }, + "/teams/{username}": { + "get": { + "responses": { + "200": { + "description": "The team object", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/team" + } + } + } + }, + "404": { + "description": "If no team exists for the specified name or UUID, or if the specified account is a personal account, not a team account.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Teams"], + "summary": "Get a team", + "security": [ + { + "oauth2": [] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Gets the public information associated with a team.\n\nIf the team's profile is private, `location`, `website` and\n`created_on` elements are omitted.\n\n**This endpoint has been removed.\nYou should use the [workspace](/cloud/bitbucket/rest/api-group-workspaces/#api-workspaces-get) endpoint instead.\nFor more information, see [this post](https://developer.atlassian.com/cloud/bitbucket/bitbucket-api-teams-deprecation/).**", + "deprecated": true + }, + "parameters": [ + { + "name": "username", + "in": "path", + "description": "This can either be the username or the UUID of the account,\nsurrounded by curly-braces, for example: `{account UUID}`. An account\nis either a team or user.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/teams/{username}/followers": { + "get": { + "responses": { + "200": { + "description": "A paginated list of user objects.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_users" + } + } + } + }, + "404": { + "description": "If no team exists for the specified name, or if the specified account is a personal account, not a team account.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Teams"], + "summary": "List team followers", + "security": [ + { + "oauth2": [] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns the list of accounts that are following this team.\n\n**This endpoint has been removed. There is no replacement endpoint.\nFor more information, see [this post](https://developer.atlassian.com/cloud/bitbucket/bitbucket-api-teams-deprecation/).**", + "deprecated": true + }, + "parameters": [ + { + "name": "username", + "in": "path", + "description": "This can either be the username or the UUID of the account,\nsurrounded by curly-braces, for example: `{account UUID}`. An account\nis either a team or user.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/teams/{username}/following": { + "get": { + "responses": { + "200": { + "description": "A paginated list of user objects.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_users" + } + } + } + }, + "404": { + "description": "If no team exists for the specified name, or if the specified account is a personal account, not a team account.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Teams"], + "summary": "List accounts a team is following", + "security": [ + { + "oauth2": ["account"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns the list of accounts this team is following.\n\n**This endpoint has been removed. There is no replacement endpoint.\nFor more information, see [this post](https://developer.atlassian.com/cloud/bitbucket/bitbucket-api-teams-deprecation/).**", + "deprecated": true + }, + "parameters": [ + { + "name": "username", + "in": "path", + "description": "This can either be the username or the UUID of the account,\nsurrounded by curly-braces, for example: `{account UUID}`. An account\nis either a team or user.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/teams/{username}/members": { + "get": { + "responses": { + "200": { + "description": "All members", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/user" + } + } + } + }, + "404": { + "description": "When the team does not exist, or multiple teams with the same name exist that differ only in casing and the URL did not match the exact casing of a particular one.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Teams"], + "summary": "List team members", + "security": [ + { + "oauth2": ["account"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns all members of the specified team. Any member of any of the\nteam's groups is considered a member of the team. This includes users\nin groups that may not actually have access to any of the team's\nrepositories.\n\n**This operation has been removed due to privacy changes.\nSee the [announcement](https://developer.atlassian.com/cloud/bitbucket/bitbucket-api-changes-gdpr/)\nfor details.\nYou should use the [workspaces](/cloud/bitbucket/rest/api-group-workspaces/#api-workspaces-workspace-members-get) endpoint as a replacement.**", + "deprecated": true + }, + "parameters": [ + { + "name": "username", + "in": "path", + "description": "This can either be the username or the UUID of the account,\nsurrounded by curly-braces, for example: `{account UUID}`. An account\nis either a team or user.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/teams/{username}/permissions": { + "get": { + "responses": { + "200": { + "description": "Repositories owned by a team.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_team_permissions" + } + } + } + }, + "403": { + "description": "The requesting user isn't an admin of the team.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "parameters": [ + { + "name": "q", + "in": "query", + "description": "\nQuery string to narrow down the response as per\n[filtering and sorting](/cloud/bitbucket/rest/intro/#filtering).", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "sort", + "in": "query", + "description": "\nName of a response property sort the result by as per\n[filtering and sorting](/cloud/bitbucket/rest/intro/#sorting-query-results).\n", + "required": false, + "schema": { + "type": "string" + } + } + ], + "tags": ["Teams"], + "summary": "List team permissions for a user ", + "security": [ + { + "oauth2": ["team"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns an object for each team permission a user on the team has.\n\n**This endpoint has been removed.\nYou should use the [workspace permissions](/cloud/bitbucket/rest/api-group-workspaces/#api-workspaces-workspace-members-member-get) endpoint instead.\nFor more information, see [the announcement](https://developer.atlassian.com/cloud/bitbucket/bitbucket-api-teams-deprecation/).**\n\nPermissions returned are effective permissions โ€” if a user is a member of\nmultiple groups with distinct roles, only the highest level is returned.\n\nPermissions can be:\n\n* `admin`\n* `collaborator`\n\nOnly users with admin permission for the team may access this resource.\n\nExample:\n\n```\n$ curl https://api.bitbucket.org/2.0/teams/atlassian_tutorial/permissions\n\n{\n \"pagelen\": 10,\n \"values\": [\n {\n \"permission\": \"admin\",\n \"type\": \"team_permission\",\n \"user\": {\n \"type\": \"user\",\n \"nickname\": \"evzijst\",\n \"display_name\": \"Erik van Zijst\",\n \"uuid\": \"{d301aafa-d676-4ee0-88be-962be7417567}\"\n },\n \"team\": {\n \"display_name\": \"Atlassian Bitbucket\",\n \"uuid\": \"{4cc6108a-a241-4db0-96a5-64347ac04f87}\"\n }\n },\n {\n \"permission\": \"collaborator\",\n \"type\": \"team_permission\",\n \"user\": {\n \"type\": \"user\",\n \"nickname\": \"seanaty\",\n \"display_name\": \"Sean Conaty\",\n \"uuid\": \"{504c3b62-8120-4f0c-a7bc-87800b9d6f70}\"\n },\n \"team\": {\n \"display_name\": \"Atlassian Bitbucket\",\n \"uuid\": \"{4cc6108a-a241-4db0-96a5-64347ac04f87}\"\n }\n }\n ],\n \"page\": 1,\n \"size\": 2\n}\n```\n\nResults may be further [filtered or sorted](/cloud/bitbucket/rest/intro/#filtering) by\nteam, user, or permission by adding the following query string\nparameters:\n\n* `q=user.uuid=\"{d301aafa-d676-4ee0-88be-962be7417567}\"` or `q=permission=\"admin\"`\n* `sort=team.display_name`\n\nNote that the query parameter values need to be URL escaped so that `=`\nwould become `%3D`.", + "deprecated": true + }, + "parameters": [ + { + "name": "username", + "in": "path", + "description": "This can either be the username or the UUID of the account,\nsurrounded by curly-braces, for example: `{account UUID}`. An account\nis either a team or user.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/teams/{username}/permissions/repositories": { + "get": { + "responses": { + "200": { + "description": "List of team's repository permissions.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_repository_permissions" + } + } + } + }, + "403": { + "description": "The requesting user isn't an admin of the team.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "parameters": [ + { + "name": "q", + "in": "query", + "description": "\nQuery string to narrow down the response as per\n[filtering and sorting](/cloud/bitbucket/rest/intro/#filtering).", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "sort", + "in": "query", + "description": "\nName of a response property sort the result by as per\n[filtering and sorting](/cloud/bitbucket/rest/intro/#sorting-query-results).\n", + "required": false, + "schema": { + "type": "string" + } + } + ], + "tags": ["Teams"], + "summary": "List repository permissions for a team", + "security": [ + { + "oauth2": ["repository", "team"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns an object for each repository permission for all of a\nteamโ€™s repositories.\n\n**This endpoint has been removed.\nYou should use the [workspace repository permissions](/cloud/bitbucket/rest/api-group-workspaces/#api-workspaces-workspace-permissions-repositories-get) endpoint instead.\nFor more information, see [the announcement](https://developer.atlassian.com/cloud/bitbucket/bitbucket-api-teams-deprecation/).**\n\nIf the username URL parameter refers to a user account instead of\na team account, an object containing the repository permissions\nof all the username's repositories will be returned.\n\nPermissions returned are effective permissions โ€” the highest level of\npermission the user has. This does not include public repositories that\nusers are not granted any specific permission in, and does not\ndistinguish between explicit and implicit privileges.\n\nOnly users with admin permission for the team may access this resource.\n\nPermissions can be:\n\n* `admin`\n* `write`\n* `read`\n\nExample:\n\n```\n$ curl https://api.bitbucket.org/2.0/teams/atlassian_tutorial/permissions/repositories\n\n{\n \"pagelen\": 10,\n \"values\": [\n {\n \"type\": \"repository_permission\",\n \"user\": {\n \"type\": \"user\",\n \"display_name\": \"Erik van Zijst\",\n \"uuid\": \"{d301aafa-d676-4ee0-88be-962be7417567}\"\n },\n \"repository\": {\n \"type\": \"repository\",\n \"name\": \"geordi\",\n \"full_name\": \"bitbucket/geordi\",\n \"uuid\": \"{85d08b4e-571d-44e9-a507-fa476535aa98}\"\n },\n \"permission\": \"admin\"\n },\n {\n \"type\": \"repository_permission\",\n \"user\": {\n \"type\": \"user\",\n \"display_name\": \"Sean Conaty\",\n \"uuid\": \"{504c3b62-8120-4f0c-a7bc-87800b9d6f70}\"\n },\n \"repository\": {\n \"type\": \"repository\",\n \"name\": \"geordi\",\n \"full_name\": \"bitbucket/geordi\",\n \"uuid\": \"{85d08b4e-571d-44e9-a507-fa476535aa98}\"\n },\n \"permission\": \"write\"\n }\n ],\n \"page\": 1,\n \"size\": 2\n}\n```\n\nResults may be further [filtered or sorted](/cloud/bitbucket/rest/intro/#filtering)\nby repository, user, or permission by adding the following query string\nparameters:\n\n* `q=repository.name=\"geordi\"` or `q=permission>\"read\"`\n* `sort=user.display_name`\n\nNote that the query parameter values need to be URL escaped so that `=`\nwould become `%3D`.", + "deprecated": true + }, + "parameters": [ + { + "name": "username", + "in": "path", + "description": "This can either be the username or the UUID of the account,\nsurrounded by curly-braces, for example: `{account UUID}`. An account\nis either a team or user.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/teams/{username}/permissions/repositories/{repo_slug}": { + "get": { + "responses": { + "200": { + "description": "List of repository's repository permissions.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_repository_permissions" + } + } + } + }, + "403": { + "description": "The requesting user isn't an admin of the repository.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "parameters": [ + { + "name": "q", + "in": "query", + "description": "\nQuery string to narrow down the response as per\n[filtering and sorting](/cloud/bitbucket/rest/intro/#filtering).", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "sort", + "in": "query", + "description": "\nName of a response property sort the result by as per\n[filtering and sorting](/cloud/bitbucket/rest/intro/#sorting-query-results).\n", + "required": false, + "schema": { + "type": "string" + } + } + ], + "tags": ["Teams"], + "summary": "List repository permissions for a team", + "security": [ + { + "oauth2": ["repository"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns an object for each repository permission of a given repository.\n\n**This endpoint has been removed.\nYou should use the [workspace repository permissions](/cloud/bitbucket/rest/api-group-workspaces/#api-workspaces-workspace-permissions-repositories-repo-slug-get) endpoint instead.\nFor more information, see [the announcement](https://developer.atlassian.com/cloud/bitbucket/bitbucket-api-teams-deprecation/).**\n\nIf the username URL parameter refers to a user account instead of\na team account, an object containing the repository permissions\nof the username's repository will be returned.\n\nPermissions returned are effective permissions โ€” the highest level of\npermission the user has. This does not include public repositories that\nusers are not granted any specific permission in, and does not\ndistinguish between explicit and implicit privileges.\n\nOnly users with admin permission for the repository may access this resource.\n\nPermissions can be:\n\n* `admin`\n* `write`\n* `read`\n\nExample:\n\n```\n$ curl https://api.bitbucket.org/2.0/teams/atlassian_tutorial/permissions/repositories/geordi\n\n{\n \"pagelen\": 10,\n \"values\": [\n {\n \"type\": \"repository_permission\",\n \"user\": {\n \"type\": \"user\",\n \"display_name\": \"Erik van Zijst\",\n \"uuid\": \"{d301aafa-d676-4ee0-88be-962be7417567}\"\n },\n \"repository\": {\n \"type\": \"repository\",\n \"name\": \"geordi\",\n \"full_name\": \"bitbucket/geordi\",\n \"uuid\": \"{85d08b4e-571d-44e9-a507-fa476535aa98}\"\n },\n \"permission\": \"admin\"\n },\n {\n \"type\": \"repository_permission\",\n \"user\": {\n \"type\": \"user\",\n \"display_name\": \"Sean Conaty\",\n \"uuid\": \"{504c3b62-8120-4f0c-a7bc-87800b9d6f70}\"\n },\n \"repository\": {\n \"type\": \"repository\",\n \"name\": \"geordi\",\n \"full_name\": \"bitbucket/geordi\",\n \"uuid\": \"{85d08b4e-571d-44e9-a507-fa476535aa98}\"\n },\n \"permission\": \"write\"\n }\n ],\n \"page\": 1,\n \"size\": 2\n}\n```\n\nResults may be further [filtered or sorted](/cloud/bitbucket/rest/intro/#filtering)\nby user, or permission by adding the following query string parameters:\n\n* `q=permission>\"read\"`\n* `sort=user.display_name`\n\nNote that the query parameter values need to be URL escaped so that `=`\nwould become `%3D`.", + "deprecated": true + }, + "parameters": [ + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "username", + "in": "path", + "description": "This can either be the username or the UUID of the account,\nsurrounded by curly-braces, for example: `{account UUID}`. An account\nis either a team or user.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/teams/{username}/pipelines_config/variables/": { + "post": { + "responses": { + "201": { + "headers": { + "Location": { + "description": "The URL of the newly created pipeline variable.", + "schema": { + "type": "string" + } + } + }, + "description": "The created variable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/pipeline_variable" + } + } + } + }, + "404": { + "description": "The account does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "409": { + "description": "A variable with the provided key already exists.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "parameters": [ + { + "description": "The account.", + "required": true, + "name": "username", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/pipeline_variable2" + }, + "tags": ["Pipelines"], + "deprecated": true, + "summary": "Create a variable for a user", + "operationId": "createPipelineVariableForTeam", + "description": "Create an account level variable.\nThis endpoint has been deprecated, and you should use the new workspaces endpoint. For more information, see [the announcement](https://developer.atlassian.com/cloud/bitbucket/bitbucket-api-teams-deprecation/)." + }, + "get": { + "responses": { + "200": { + "description": "The found account level variables.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_pipeline_variables" + } + } + } + } + }, + "parameters": [ + { + "description": "The account.", + "required": true, + "name": "username", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "tags": ["Pipelines"], + "deprecated": true, + "summary": "List variables for an account", + "operationId": "getPipelineVariablesForTeam", + "description": "Find account level variables.\nThis endpoint has been deprecated, and you should use the new workspaces endpoint. For more information, see [the announcement](https://developer.atlassian.com/cloud/bitbucket/bitbucket-api-teams-deprecation/)." + } + }, + "/teams/{username}/pipelines_config/variables/{variable_uuid}": { + "put": { + "responses": { + "200": { + "description": "The variable was updated.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/pipeline_variable" + } + } + } + }, + "404": { + "description": "The account or the variable was not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "parameters": [ + { + "description": "The account.", + "required": true, + "name": "username", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The UUID of the variable.", + "required": true, + "name": "variable_uuid", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/pipeline_variable" + }, + "tags": ["Pipelines"], + "deprecated": true, + "summary": "Update a variable for a team", + "operationId": "updatePipelineVariableForTeam", + "description": "Update a team level variable.\nThis endpoint has been deprecated, and you should use the new workspaces endpoint. For more information, see [the announcement](https://developer.atlassian.com/cloud/bitbucket/bitbucket-api-teams-deprecation/)." + }, + "get": { + "responses": { + "200": { + "description": "The variable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/pipeline_variable" + } + } + } + }, + "404": { + "description": "The account or variable with the given UUID was not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "parameters": [ + { + "description": "The account.", + "required": true, + "name": "username", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The UUID of the variable to retrieve.", + "required": true, + "name": "variable_uuid", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "tags": ["Pipelines"], + "deprecated": true, + "summary": "Get a variable for a team", + "operationId": "getPipelineVariableForTeam", + "description": "Retrieve a team level variable.\nThis endpoint has been deprecated, and you should use the new workspaces endpoint. For more information, see [the announcement](https://developer.atlassian.com/cloud/bitbucket/bitbucket-api-teams-deprecation/)." + }, + "delete": { + "responses": { + "204": { + "description": "The variable was deleted" + }, + "404": { + "description": "The account or the variable with the provided UUID does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "parameters": [ + { + "description": "The account.", + "required": true, + "name": "username", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The UUID of the variable to delete.", + "required": true, + "name": "variable_uuid", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "tags": ["Pipelines"], + "deprecated": true, + "summary": "Delete a variable for a team", + "operationId": "deletePipelineVariableForTeam", + "description": "Delete a team level variable.\nThis endpoint has been deprecated, and you should use the new workspaces endpoint. For more information, see [the announcement](https://developer.atlassian.com/cloud/bitbucket/bitbucket-api-teams-deprecation/)." + } + }, + "/teams/{username}/projects/": { + "get": { + "responses": { + "200": { + "description": "A paginated list of projects that belong to the specified team.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_projects" + } + } + } + }, + "403": { + "description": "The requesting user isn't authorized to read the list of projects for the specified team.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "A team doesn't exist at this location.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Projects"], + "summary": "List a projects for a team", + "security": [ + { + "oauth2": ["project"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "**This endpoint has been removed.\nYou should use the [workspace projects](/cloud/bitbucket/rest/api-group-projects/#api-workspaces-workspace-projects-project-key-get) endpoint instead.\nFor more information, see [the announcement](https://developer.atlassian.com/cloud/bitbucket/bitbucket-api-teams-deprecation/).**", + "deprecated": true + }, + "post": { + "responses": { + "201": { + "description": "A new project has been created.", + "headers": { + "Location": { + "description": "The location of the newly created project", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/project" + } + } + } + }, + "403": { + "description": "The requesting user isn't authorized to create the project.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "A team doesn't exist at this location.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "requestBody": { + "$ref": "#/components/requestBodies/project" + }, + "tags": ["Projects"], + "summary": "Create a project for a team", + "security": [ + { + "oauth2": ["project:write"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Creates a new project.\n\n**This endpoint has been removed.\nYou should use the [workspace projects](/cloud/bitbucket/rest/api-group-projects/#api-workspaces-workspace-projects-post) endpoint instead.\nFor more information, see [the announcement](https://developer.atlassian.com/cloud/bitbucket/bitbucket-api-teams-deprecation/).**\n\nNote that the avatar has to be embedded as either a data-url\nor a URL to an external image as shown in the examples below:\n\n```\n$ body=$(cat << EOF\n{\n \"name\": \"Mars Project\",\n \"key\": \"MARS\",\n \"description\": \"Software for colonizing mars.\",\n \"links\": {\n \"avatar\": {\n \"href\": \"data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/...\"\n }\n },\n \"is_private\": false\n}\nEOF\n)\n$ curl -H \"Content-Type: application/json\" \\\n -X POST \\\n -d \"$body\" \\\n https://api.bitbucket.org/2.0/teams/teams-in-space/projects/ | jq .\n{\n // Serialized project document\n}\n```\n\nor even:\n\n```\n$ body=$(cat << EOF\n{\n \"name\": \"Mars Project\",\n \"key\": \"MARS\",\n \"description\": \"Software for colonizing mars.\",\n \"links\": {\n \"avatar\": {\n \"href\": \"http://i.imgur.com/72tRx4w.gif\"\n }\n },\n \"is_private\": false\n}\nEOF\n)\n$ curl -H \"Content-Type: application/json\" \\\n -X POST \\\n -d \"$body\" \\\n https://api.bitbucket.org/2.0/teams/teams-in-space/projects/ | jq .\n{\n // Serialized project document\n}\n```", + "deprecated": true + }, + "parameters": [ + { + "name": "username", + "in": "path", + "description": "This can either be the username or the UUID of the account,\nsurrounded by curly-braces, for example: `{account UUID}`. An account\nis either a team or user.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/teams/{username}/projects/{project_key}": { + "delete": { + "responses": { + "204": { + "description": "Successful deletion." + }, + "403": { + "description": "The requesting user isn't authorized to delete the project or the project isn't empty.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "A project isn't hosted at this location.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Projects"], + "summary": "Delete a project", + "security": [ + { + "oauth2": ["project:write"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "**This endpoint has been removed.\nYou should use the [workspace project](/cloud/bitbucket/rest/api-group-projects/#api-workspaces-workspace-projects-project-key-delete) endpoint instead.\nFor more information, see [the announcement](https://developer.atlassian.com/cloud/bitbucket/bitbucket-api-teams-deprecation/).**", + "deprecated": true + }, + "get": { + "responses": { + "200": { + "description": "The project object.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/project" + } + } + } + }, + "403": { + "description": "The requesting user isn't authorized to access the project.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "A project isn't hosted at this location.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Projects"], + "summary": "Get a project", + "security": [ + { + "oauth2": ["project"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "**This endpoint has been removed.\nYou should use the [workspace project](/cloud/bitbucket/rest/api-group-workspaces/#api-workspaces-workspace-projects-get) endpoint instead.\nFor more information, see [the announcement](https://developer.atlassian.com/cloud/bitbucket/bitbucket-api-teams-deprecation/).**", + "deprecated": true + }, + "put": { + "responses": { + "200": { + "description": "The existing project is has been updated.", + "headers": { + "Location": { + "description": "The location of the project. This header is only provided\nwhen the project key is updated.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/project" + } + } + } + }, + "201": { + "description": "A new project has been created.", + "headers": { + "Location": { + "description": "The location of the newly created project", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/project" + } + } + } + }, + "403": { + "description": "The requesting user isn't authorized to update or create the project.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "A team doesn't exist at the location. Note that the project's absence from this location doesn't raise a 404, since a PUT at a non-existent location can be used to create a new project.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "requestBody": { + "$ref": "#/components/requestBodies/project" + }, + "tags": ["Projects"], + "summary": "Update a project", + "security": [ + { + "oauth2": ["project:write"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Since this endpoint can be used to both update and to create a\nproject, the request body depends on the intent.\n\n**This endpoint has been removed.\nYou should use the [workspace project](/cloud/bitbucket/rest/api-group-projects/#api-workspaces-workspace-projects-project-key-put) endpoint instead.\nFor more information, see [the announcement](https://developer.atlassian.com/cloud/bitbucket/bitbucket-api-teams-deprecation/).**\n\n#### Creation\n\nSee the POST documentation for the project collection for an\nexample of the request body.\n\nNote: The `key` should not be specified in the body of request\n(since it is already present in the URL). The `name` is required,\neverything else is optional.\n\n#### Update\n\nSee the POST documentation for the project collection for an\nexample of the request body.\n\nNote: The key is not required in the body (since it is already in\nthe URL). The key may be specified in the body, if the intent is\nto change the key itself. In such a scenario, the location of the\nproject is changed and is returned in the `Location` header of the\nresponse.", + "deprecated": true + }, + "parameters": [ + { + "name": "project_key", + "in": "path", + "description": "The project in question. This can either be the actual `key` assigned\nto the project or the `UUID` (surrounded by curly-braces (`{}`)).\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "username", + "in": "path", + "description": "This can either be the username or the UUID of the account,\nsurrounded by curly-braces, for example: `{account UUID}`. An account\nis either a team or user.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/teams/{username}/search/code": { + "get": { + "responses": { + "200": { + "description": "Successful search", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/search_result_page" + } + } + } + }, + "400": { + "description": "If the search request was invalid due to one of the\nfollowing reasons:\n\n* the specified type of target account doesn''t match the actual\naccount type;\n\n* malformed pagination properties;\n\n* missing or malformed search query, in the latter case an error\nkey will be returned in `error.data.key` property.\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "Search is not enabled for the requested team, navigate to [https://bitbucket.org/search](https://bitbucket.org/search) to turn it on", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "429": { + "description": "Too many requests, try again later", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "parameters": [ + { + "required": true, + "description": "The account to search in; either the username or the UUID in curly braces", + "in": "path", + "name": "username", + "schema": { + "type": "string" + } + }, + { + "required": true, + "description": "The search query", + "in": "query", + "name": "search_query", + "schema": { + "type": "string" + } + }, + { + "description": "Which page of the search results to retrieve", + "required": false, + "in": "query", + "name": "page", + "schema": { + "type": "integer", + "format": "int32", + "default": 1 + } + }, + { + "description": "How many search results to retrieve per page", + "required": false, + "in": "query", + "name": "pagelen", + "schema": { + "type": "integer", + "format": "int32", + "default": 10 + } + } + ], + "tags": ["Search"], + "summary": "Search for code in a team's repositories", + "operationId": "searchTeam", + "description": "Search for code in the repositories of the specified team.\n\nSearching across all repositories:\n\n```\ncurl 'https://api.bitbucket.org/2.0/teams/team_name/search/code?search_query=foo'\n{\n \"size\": 1,\n \"page\": 1,\n \"pagelen\": 10,\n \"query_substituted\": false,\n \"values\": [\n {\n \"type\": \"code_search_result\",\n \"content_match_count\": 2,\n \"content_matches\": [\n {\n \"lines\": [\n {\n \"line\": 2,\n \"segments\": []\n },\n {\n \"line\": 3,\n \"segments\": [\n {\n \"text\": \"def \"\n },\n {\n \"text\": \"foo\",\n \"match\": true\n },\n {\n \"text\": \"():\"\n }\n ]\n },\n {\n \"line\": 4,\n \"segments\": [\n {\n \"text\": \" print(\\\"snek\\\")\"\n }\n ]\n },\n {\n \"line\": 5,\n \"segments\": []\n }\n ]\n }\n ],\n \"path_matches\": [\n {\n \"text\": \"src/\"\n },\n {\n \"text\": \"foo\",\n \"match\": true\n },\n {\n \"text\": \".py\"\n }\n ],\n \"file\": {\n \"path\": \"src/foo.py\",\n \"type\": \"commit_file\",\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/my-workspace/demo/src/ad6964b5fe2880dbd9ddcad1c89000f1dbcbc24b/src/foo.py\"\n }\n }\n }\n }\n ]\n}\n```\n\nNote that searches can match in the file's text (`content_matches`),\nthe path (`path_matches`), or both as in the example above.\n\nYou can use the same syntax for the search query as in the UI, e.g.\nto only search within a specific repository:\n\n```\ncurl 'https://api.bitbucket.org/2.0/teams/team_name/search/code?search_query=foo+repo:demo'\n# results from the \"demo\" repository\n```\n\nSimilar to other APIs, you can request more fields using a\n`fields` query parameter. E.g. to get some more information about\nthe repository of matched files (the `%2B` is a URL-encoded `+`):\n\n```\ncurl 'https://api.bitbucket.org/2.0/teams/team_name/search/code'\\\n '?search_query=foo&fields=%2Bvalues.file.commit.repository'\n{\n \"size\": 1,\n \"page\": 1,\n \"pagelen\": 10,\n \"query_substituted\": false,\n \"values\": [\n {\n \"type\": \"code_search_result\",\n \"content_match_count\": 1,\n \"content_matches\": [...],\n \"path_matches\": [...],\n \"file\": {\n \"commit\": {\n \"type\": \"commit\",\n \"hash\": \"ad6964b5fe2880dbd9ddcad1c89000f1dbcbc24b\",\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/my-workspace/demo/commit/ad6964b5fe2880dbd9ddcad1c89000f1dbcbc24b\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/my-workspace/demo/commits/ad6964b5fe2880dbd9ddcad1c89000f1dbcbc24b\"\n }\n },\n \"repository\": {\n \"name\": \"demo\",\n \"type\": \"repository\",\n \"full_name\": \"my-workspace/demo\",\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/my-workspace/demo\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/my-workspace/demo\"\n },\n \"avatar\": {\n \"href\": \"https://bytebucket.org/ravatar/%7B850e1749-781a-4115-9316-df39d0600e7a%7D?ts=default\"\n }\n },\n \"uuid\": \"{850e1749-781a-4115-9316-df39d0600e7a}\"\n }\n },\n \"type\": \"commit_file\",\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/my-workspace/demo/src/ad6964b5fe2880dbd9ddcad1c89000f1dbcbc24b/src/foo.py\"\n }\n },\n \"path\": \"src/foo.py\"\n }\n }\n ]\n}\n```\n\nTry `fields=%2Bvalues.*.*.*.*` to get an idea what's possible.\n" + } + }, + "/teams/{workspace}/repositories": { + "get": { + "responses": { + "default": { + "description": "Unexpected error.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Users", "Teams"], + "summary": "List workspace repositories", + "security": [ + { + "oauth2": ["repository"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "All repositories in the given workspace. This includes any private\nrepositories the calling user has access to.\n\n**This endpoint has been removed.\nYou should use the [repository list](/cloud/bitbucket/rest/api-group-repositories/#api-repositories-workspace-get) endpoint instead.\nFor more information, see the [deprecation announcement](https://developer.atlassian.com/cloud/bitbucket/bitbucket-api-teams-deprecation/).**", + "deprecated": true + }, + "parameters": [ + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/user": { + "get": { + "responses": { + "200": { + "description": "The current user.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/user" + } + } + } + }, + "401": { + "description": "When the request wasn't authenticated.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Users"], + "summary": "Get current user", + "security": [ + { + "oauth2": ["account"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns the currently logged in user." + }, + "parameters": [] + }, + "/user/emails": { + "get": { + "responses": { + "default": { + "description": "Unexpected error.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Users"], + "summary": "List email addresses for current user", + "security": [ + { + "oauth2": ["email"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns all the authenticated user's email addresses. Both\nconfirmed and unconfirmed." + }, + "parameters": [] + }, + "/user/emails/{email}": { + "get": { + "responses": { + "default": { + "description": "Unexpected error.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Users"], + "summary": "Get an email address for current user", + "security": [ + { + "oauth2": ["email"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns details about a specific one of the authenticated user's\nemail addresses.\n\nDetails describe whether the address has been confirmed by the user and\nwhether it is the user's primary address or not." + }, + "parameters": [ + { + "name": "email", + "in": "path", + "description": "Email address of the user.", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/user/permissions/repositories": { + "get": { + "responses": { + "200": { + "description": "Repository permissions for the repositories a caller has explicit access to.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_repository_permissions" + } + } + } + } + }, + "parameters": [ + { + "name": "q", + "in": "query", + "description": "\nQuery string to narrow down the response as per\n[filtering and sorting](/cloud/bitbucket/rest/intro/#filtering).", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "sort", + "in": "query", + "description": "\nName of a response property sort the result by as per\n[filtering and sorting](/cloud/bitbucket/rest/intro/#filtering).", + "required": false, + "schema": { + "type": "string" + } + } + ], + "tags": ["Repositories"], + "summary": "List repository permissions for a user", + "security": [ + { + "oauth2": ["account", "repository"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns an object for each repository the caller has explicit access\nto and their effective permission โ€” the highest level of permission the\ncaller has. This does not return public repositories that the user was\nnot granted any specific permission in, and does not distinguish between\nexplicit and implicit privileges.\n\nPermissions can be:\n\n* `admin`\n* `write`\n* `read`\n\nExample:\n\n```\n$ curl https://api.bitbucket.org/2.0/user/permissions/repositories\n\n{\n \"pagelen\": 10,\n \"values\": [\n {\n \"type\": \"repository_permission\",\n \"user\": {\n \"type\": \"user\",\n \"nickname\": \"evzijst\",\n \"display_name\": \"Erik van Zijst\",\n \"uuid\": \"{d301aafa-d676-4ee0-88be-962be7417567}\"\n },\n \"repository\": {\n \"type\": \"repository\",\n \"name\": \"geordi\",\n \"full_name\": \"bitbucket/geordi\",\n \"uuid\": \"{85d08b4e-571d-44e9-a507-fa476535aa98}\"\n },\n \"permission\": \"admin\"\n }\n ],\n \"page\": 1,\n \"size\": 1\n}\n```\n\nResults may be further [filtered or sorted](/cloud/bitbucket/rest/intro/#filtering) by\nrepository or permission by adding the following query string\nparameters:\n\n* `q=repository.name=\"geordi\"` or `q=permission>\"read\"`\n* `sort=repository.name`\n\nNote that the query parameter values need to be URL escaped so that `=`\nwould become `%3D`." + }, + "parameters": [] + }, + "/user/permissions/teams": { + "get": { + "responses": { + "200": { + "description": "Team permissions for the teams a caller is a member of.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_team_permissions" + } + } + } + } + }, + "parameters": [ + { + "name": "q", + "in": "query", + "description": "\nQuery string to narrow down the response as per\n[filtering and sorting](/cloud/bitbucket/rest/intro/#filtering).", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "sort", + "in": "query", + "description": "\nName of a response property sort the result by as per\n[filtering and sorting](/cloud/bitbucket/rest/intro/#sorting-query-results).\n", + "required": false, + "schema": { + "type": "string" + } + } + ], + "tags": ["Teams"], + "summary": "List team permissions for the user", + "security": [ + { + "oauth2": ["account"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns an object for each team the caller is a member of, and their\neffective role โ€” the highest level of privilege the caller has. If a\nuser is a member of multiple groups with distinct roles, only the\nhighest level is returned.\n\n**This endpoint has been removed.\nYou should use the [workspace permissions](/cloud/bitbucket/rest/api-group-workspaces/#api-workspaces-get) endpoint instead.\nFor more information, see [the announcement](https://developer.atlassian.com/cloud/bitbucket/bitbucket-api-teams-deprecation/).**\n\nPermissions can be:\n\n* `admin`\n* `collaborator`\n\nExample:\n\n```\n$ curl https://api.bitbucket.org/2.0/user/permissions/teams\n\n{\n \"pagelen\": 10,\n \"values\": [\n {\n \"permission\": \"admin\",\n \"type\": \"team_permission\",\n \"user\": {\n \"type\": \"user\",\n \"nickname\": \"evzijst\",\n \"display_name\": \"Erik van Zijst\",\n \"uuid\": \"{d301aafa-d676-4ee0-88be-962be7417567}\"\n },\n \"team\": {\n \"display_name\": \"Atlassian Bitbucket\",\n \"uuid\": \"{4cc6108a-a241-4db0-96a5-64347ac04f87}\"\n }\n }\n ],\n \"page\": 1,\n \"size\": 1\n}\n```\n\nResults may be further [filtered or sorted](/cloud/bitbucket/rest/intro/#filtering) by\nteam or permission by adding the following query string parameters:\n\n* `q=team.uuid=\"{4cc6108a-a241-4db0-96a5-64347ac04f87}\"` or `q=permission=\"admin\"`\n* `sort=team.display_name`\n\nNote that the query parameter values need to be URL escaped so that `=`\nwould become `%3D`.", + "deprecated": true + }, + "parameters": [] + }, + "/user/permissions/workspaces": { + "get": { + "responses": { + "200": { + "description": "All of the workspace memberships for the authenticated user.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_workspace_memberships" + } + } + } + }, + "401": { + "description": "The request wasn't authenticated.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "parameters": [ + { + "name": "q", + "in": "query", + "description": "\nQuery string to narrow down the response. See\n[filtering and sorting](/cloud/bitbucket/rest/intro/#filtering) for details.", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "sort", + "in": "query", + "description": "\nName of a response property to sort results. See\n[filtering and sorting](/cloud/bitbucket/rest/intro/#sorting-query-results)\nfor details.\n", + "required": false, + "schema": { + "type": "string" + } + } + ], + "tags": ["Workspaces"], + "summary": "List workspaces for the current user", + "security": [ + { + "oauth2": ["account"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns an object for each workspace the caller is a member of, and\ntheir effective role - the highest level of privilege the caller has.\nIf a user is a member of multiple groups with distinct roles, only the\nhighest level is returned.\n\nPermissions can be:\n\n* `owner`\n* `collaborator`\n* `member`\n\n**The `collaborator` role is being removed from the Bitbucket Cloud API. For more information,\nsee the [deprecation announcement](/cloud/bitbucket/deprecation-notice-collaborator-role/).**\n\nExample:\n\n```\n$ curl https://api.bitbucket.org/2.0/user/permissions/workspaces\n\n{\n \"pagelen\": 10,\n \"page\": 1,\n \"size\": 1,\n \"values\": [\n {\n \"type\": \"workspace_membership\",\n \"permission\": \"owner\",\n \"last_accessed\": \"2019-03-07T12:35:02.900024+00:00\",\n \"added_on\": \"2018-10-11T17:42:02.961424+00:00\",\n \"user\": {\n \"type\": \"user\",\n \"uuid\": \"{470c176d-3574-44ea-bb41-89e8638bcca4}\",\n \"nickname\": \"evzijst\",\n \"display_name\": \"Erik van Zijst\",\n },\n \"workspace\": {\n \"type\": \"workspace\",\n \"uuid\": \"{a15fb181-db1f-48f7-b41f-e1eff06929d6}\",\n \"slug\": \"bbworkspace1\",\n \"name\": \"Atlassian Bitbucket\",\n }\n }\n ]\n}\n```\n\nResults may be further [filtered or sorted](/cloud/bitbucket/rest/intro/#filtering) by\nworkspace or permission by adding the following query string parameters:\n\n* `q=workspace.slug=\"bbworkspace1\"` or `q=permission=\"owner\"`\n* `sort=workspace.slug`\n\nNote that the query parameter values need to be URL escaped so that `=`\nwould become `%3D`." + }, + "parameters": [] + }, + "/users/{selected_user}": { + "get": { + "responses": { + "200": { + "description": "The user object", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/user" + } + } + } + }, + "404": { + "description": "If no user exists for the specified UUID, or if the specified account is a team account, not a personal account.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Users"], + "summary": "Get a user", + "security": [ + { + "oauth2": [] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Gets the public information associated with a user account.\n\nIf the user's profile is private, `location`, `website` and\n`created_on` elements are omitted.\n\nNote that the user object returned by this operation is changing significantly, due to privacy changes.\nSee the [announcement](https://developer.atlassian.com/cloud/bitbucket/bitbucket-api-changes-gdpr/#changes-to-bitbucket-user-objects) for details." + }, + "parameters": [ + { + "name": "selected_user", + "in": "path", + "description": "This can either be the UUID of the account, surrounded by curly-braces, for\nexample: `{account UUID}`, OR an Atlassian Account ID.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/users/{selected_user}/pipelines_config/variables/": { + "post": { + "responses": { + "201": { + "headers": { + "Location": { + "description": "The URL of the newly created pipeline variable.", + "schema": { + "type": "string" + } + } + }, + "description": "The created variable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/pipeline_variable" + } + } + } + }, + "404": { + "description": "The account does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "409": { + "description": "A variable with the provided key already exists.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "parameters": [ + { + "description": "Either the UUID of the account surrounded by curly-braces, for example `{account UUID}`, OR an Atlassian Account ID.", + "required": true, + "name": "selected_user", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/pipeline_variable2" + }, + "tags": ["Pipelines"], + "deprecated": true, + "summary": "Create a variable for a user", + "operationId": "createPipelineVariableForUser", + "description": "Create a user level variable.\nThis endpoint has been deprecated, and you should use the new workspaces endpoint. For more information, see [the announcement](https://developer.atlassian.com/cloud/bitbucket/bitbucket-api-teams-deprecation/)." + }, + "get": { + "responses": { + "200": { + "description": "The found user level variables.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_pipeline_variables" + } + } + } + } + }, + "parameters": [ + { + "description": "Either the UUID of the account surrounded by curly-braces, for example `{account UUID}`, OR an Atlassian Account ID.", + "required": true, + "name": "selected_user", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "tags": ["Pipelines"], + "deprecated": true, + "summary": "List variables for a user", + "operationId": "getPipelineVariablesForUser", + "description": "Find user level variables.\nThis endpoint has been deprecated, and you should use the new workspaces endpoint. For more information, see [the announcement](https://developer.atlassian.com/cloud/bitbucket/bitbucket-api-teams-deprecation/)." + } + }, + "/users/{selected_user}/pipelines_config/variables/{variable_uuid}": { + "put": { + "responses": { + "200": { + "description": "The variable was updated.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/pipeline_variable" + } + } + } + }, + "404": { + "description": "The account or the variable was not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "parameters": [ + { + "description": "Either the UUID of the account surrounded by curly-braces, for example `{account UUID}`, OR an Atlassian Account ID.", + "required": true, + "name": "selected_user", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The UUID of the variable.", + "required": true, + "name": "variable_uuid", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/pipeline_variable" + }, + "tags": ["Pipelines"], + "deprecated": true, + "summary": "Update a variable for a user", + "operationId": "updatePipelineVariableForUser", + "description": "Update a user level variable.\nThis endpoint has been deprecated, and you should use the new workspaces endpoint. For more information, see [the announcement](https://developer.atlassian.com/cloud/bitbucket/bitbucket-api-teams-deprecation/)." + }, + "get": { + "responses": { + "200": { + "description": "The variable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/pipeline_variable" + } + } + } + }, + "404": { + "description": "The account or variable with the given UUID was not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "parameters": [ + { + "description": "Either the UUID of the account surrounded by curly-braces, for example `{account UUID}`, OR an Atlassian Account ID.", + "required": true, + "name": "selected_user", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The UUID of the variable to retrieve.", + "required": true, + "name": "variable_uuid", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "tags": ["Pipelines"], + "deprecated": true, + "summary": "Get a variable for a user", + "operationId": "getPipelineVariableForUser", + "description": "Retrieve a user level variable.\nThis endpoint has been deprecated, and you should use the new workspaces endpoint. For more information, see [the announcement](https://developer.atlassian.com/cloud/bitbucket/bitbucket-api-teams-deprecation/)." + }, + "delete": { + "responses": { + "204": { + "description": "The variable was deleted" + }, + "404": { + "description": "The account or the variable with the provided UUID does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "parameters": [ + { + "description": "Either the UUID of the account surrounded by curly-braces, for example `{account UUID}`, OR an Atlassian Account ID.", + "required": true, + "name": "selected_user", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The UUID of the variable to delete.", + "required": true, + "name": "variable_uuid", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "tags": ["Pipelines"], + "deprecated": true, + "summary": "Delete a variable for a user", + "operationId": "deletePipelineVariableForUser", + "description": "Delete an account level variable.\nThis endpoint has been deprecated, and you should use the new workspaces endpoint. For more information, see [the announcement](https://developer.atlassian.com/cloud/bitbucket/bitbucket-api-teams-deprecation/)." + } + }, + "/users/{selected_user}/properties/{app_key}/{property_name}": { + "put": { + "responses": { + "204": { + "description": "An empty response." + } + }, + "parameters": [ + { + "required": true, + "description": "Either the UUID of the account surrounded by curly-braces, for example `{account UUID}`, OR an Atlassian Account ID.", + "in": "path", + "name": "selected_user", + "schema": { + "type": "string" + } + }, + { + "required": true, + "description": "The key of the Connect app.", + "in": "path", + "name": "app_key", + "schema": { + "type": "string" + } + }, + { + "required": true, + "description": "The name of the property.", + "in": "path", + "name": "property_name", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/application_property" + }, + "tags": ["properties"], + "description": "Update an [application property](/cloud/bitbucket/application-properties/) value stored against a user.", + "summary": "Update a user application property", + "operationId": "updateUserHostedPropertyValue" + }, + "delete": { + "responses": { + "204": { + "description": "An empty response." + } + }, + "parameters": [ + { + "required": true, + "description": "Either the UUID of the account surrounded by curly-braces, for example `{account UUID}`, OR an Atlassian Account ID.", + "in": "path", + "name": "selected_user", + "schema": { + "type": "string" + } + }, + { + "required": true, + "description": "The key of the Connect app.", + "in": "path", + "name": "app_key", + "schema": { + "type": "string" + } + }, + { + "required": true, + "description": "The name of the property.", + "in": "path", + "name": "property_name", + "schema": { + "type": "string" + } + } + ], + "tags": ["properties"], + "description": "Delete an [application property](/cloud/bitbucket/application-properties/) value stored against a user.", + "summary": "Delete a user application property", + "operationId": "deleteUserHostedPropertyValue" + }, + "get": { + "responses": { + "200": { + "description": "The value of the property.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/application_property" + } + } + } + } + }, + "parameters": [ + { + "required": true, + "description": "Either the UUID of the account surrounded by curly-braces, for example `{account UUID}`, OR an Atlassian Account ID.", + "in": "path", + "name": "selected_user", + "schema": { + "type": "string" + } + }, + { + "required": true, + "description": "The key of the Connect app.", + "in": "path", + "name": "app_key", + "schema": { + "type": "string" + } + }, + { + "required": true, + "description": "The name of the property.", + "in": "path", + "name": "property_name", + "schema": { + "type": "string" + } + } + ], + "tags": ["properties"], + "description": "Retrieve an [application property](/cloud/bitbucket/application-properties/) value stored against a user.", + "summary": "Get a user application property", + "operationId": "retrieveUserHostedPropertyValue" + } + }, + "/users/{selected_user}/search/code": { + "get": { + "responses": { + "200": { + "description": "Successful search", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/search_result_page" + } + } + } + }, + "400": { + "description": "If the search request was invalid due to one of the\nfollowing reasons:\n\n* the specified type of target account doesn''t match the actual\naccount type;\n\n* malformed pagination properties;\n\n* missing or malformed search query, in the latter case an error\nkey will be returned in `error.data.key` property.\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "Search is not enabled for the requested user, navigate to [https://bitbucket.org/search](https://bitbucket.org/search) to turn it on", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "429": { + "description": "Too many requests, try again later", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "parameters": [ + { + "required": true, + "description": "Either the UUID of the account surrounded by curly-braces, for example `{account UUID}`, OR an Atlassian Account ID.", + "in": "path", + "name": "selected_user", + "schema": { + "type": "string" + } + }, + { + "required": true, + "description": "The search query", + "in": "query", + "name": "search_query", + "schema": { + "type": "string" + } + }, + { + "description": "Which page of the search results to retrieve", + "required": false, + "in": "query", + "name": "page", + "schema": { + "type": "integer", + "format": "int32", + "default": 1 + } + }, + { + "description": "How many search results to retrieve per page", + "required": false, + "in": "query", + "name": "pagelen", + "schema": { + "type": "integer", + "format": "int32", + "default": 10 + } + } + ], + "tags": ["Search"], + "summary": "Search for code in a user's repositories", + "operationId": "searchAccount", + "description": "Search for code in the repositories of the specified user.\n\nSearching across all repositories:\n\n```\ncurl 'https://api.bitbucket.org/2.0/users/{ed08f5e1-605b-4f4a-aee4-6c97628a673e}/search/code?search_query=foo'\n{\n \"size\": 1,\n \"page\": 1,\n \"pagelen\": 10,\n \"query_substituted\": false,\n \"values\": [\n {\n \"type\": \"code_search_result\",\n \"content_match_count\": 2,\n \"content_matches\": [\n {\n \"lines\": [\n {\n \"line\": 2,\n \"segments\": []\n },\n {\n \"line\": 3,\n \"segments\": [\n {\n \"text\": \"def \"\n },\n {\n \"text\": \"foo\",\n \"match\": true\n },\n {\n \"text\": \"():\"\n }\n ]\n },\n {\n \"line\": 4,\n \"segments\": [\n {\n \"text\": \" print(\\\"snek\\\")\"\n }\n ]\n },\n {\n \"line\": 5,\n \"segments\": []\n }\n ]\n }\n ],\n \"path_matches\": [\n {\n \"text\": \"src/\"\n },\n {\n \"text\": \"foo\",\n \"match\": true\n },\n {\n \"text\": \".py\"\n }\n ],\n \"file\": {\n \"path\": \"src/foo.py\",\n \"type\": \"commit_file\",\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/my-workspace/demo/src/ad6964b5fe2880dbd9ddcad1c89000f1dbcbc24b/src/foo.py\"\n }\n }\n }\n }\n ]\n}\n```\n\nNote that searches can match in the file's text (`content_matches`),\nthe path (`path_matches`), or both as in the example above.\n\nYou can use the same syntax for the search query as in the UI, e.g.\nto only search within a specific repository:\n\n```\ncurl 'https://api.bitbucket.org/2.0/users/{ed08f5e1-605b-4f4a-aee4-6c97628a673e}/search/code?search_query=foo+repo:demo'\n# results from the \"demo\" repository\n```\n\nSimilar to other APIs, you can request more fields using a\n`fields` query parameter. E.g. to get some more information about\nthe repository of matched files (the `%2B` is a URL-encoded `+`):\n\n```\ncurl 'https://api.bitbucket.org/2.0/users/{ed08f5e1-605b-4f4a-aee4-6c97628a673e}/search/code'\\\n '?search_query=foo&fields=%2Bvalues.file.commit.repository'\n{\n \"size\": 1,\n \"page\": 1,\n \"pagelen\": 10,\n \"query_substituted\": false,\n \"values\": [\n {\n \"type\": \"code_search_result\",\n \"content_match_count\": 1,\n \"content_matches\": [...],\n \"path_matches\": [...],\n \"file\": {\n \"commit\": {\n \"type\": \"commit\",\n \"hash\": \"ad6964b5fe2880dbd9ddcad1c89000f1dbcbc24b\",\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/my-workspace/demo/commit/ad6964b5fe2880dbd9ddcad1c89000f1dbcbc24b\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/my-workspace/demo/commits/ad6964b5fe2880dbd9ddcad1c89000f1dbcbc24b\"\n }\n },\n \"repository\": {\n \"name\": \"demo\",\n \"type\": \"repository\",\n \"full_name\": \"my-workspace/demo\",\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/my-workspace/demo\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/my-workspace/demo\"\n },\n \"avatar\": {\n \"href\": \"https://bytebucket.org/ravatar/%7B850e1749-781a-4115-9316-df39d0600e7a%7D?ts=default\"\n }\n },\n \"uuid\": \"{850e1749-781a-4115-9316-df39d0600e7a}\"\n }\n },\n \"type\": \"commit_file\",\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/my-workspace/demo/src/ad6964b5fe2880dbd9ddcad1c89000f1dbcbc24b/src/foo.py\"\n }\n },\n \"path\": \"src/foo.py\"\n }\n }\n ]\n}\n```\n\nTry `fields=%2Bvalues.*.*.*.*` to get an idea what's possible.\n" + } + }, + "/users/{selected_user}/ssh-keys": { + "get": { + "responses": { + "200": { + "description": "A list of the SSH keys associated with the account.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_ssh_user_keys" + } + } + } + }, + "403": { + "description": "If the specified user's keys are not accessible to the current user" + }, + "404": { + "description": "If the specified user does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Ssh"], + "summary": "List SSH keys", + "security": [ + { + "oauth2": ["account"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns a paginated list of the user's SSH public keys.\n\nExample:\n\n```\n$ curl https://api.bitbucket.org/2.0/users/{ed08f5e1-605b-4f4a-aee4-6c97628a673e}/ssh-keys\n{\n \"page\": 1,\n \"pagelen\": 10,\n \"size\": 1,\n \"values\": [\n {\n \"comment\": \"user@myhost\",\n \"created_on\": \"2018-03-14T13:17:05.196003+00:00\",\n \"key\": \"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKqP3Cr632C2dNhhgKVcon4ldUSAeKiku2yP9O9/bDtY\",\n \"label\": \"\",\n \"last_used\": \"2018-03-20T13:18:05.196003+00:00\",\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/users/{ed08f5e1-605b-4f4a-aee4-6c97628a673e}/ssh-keys/b15b6026-9c02-4626-b4ad-b905f99f763a\"\n }\n },\n \"owner\": {\n \"display_name\": \"Mark Adams\",\n \"links\": {\n \"avatar\": {\n \"href\": \"https://bitbucket.org/account/markadams-atl/avatar/32/\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/markadams-atl/\"\n },\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/users/{ed08f5e1-605b-4f4a-aee4-6c97628a673e}\"\n }\n },\n \"type\": \"user\",\n \"username\": \"markadams-atl\",\n \"nickname\": \"markadams-atl\",\n \"uuid\": \"{d7dd0e2d-3994-4a50-a9ee-d260b6cefdab}\"\n },\n \"type\": \"ssh_key\",\n \"uuid\": \"{b15b6026-9c02-4626-b4ad-b905f99f763a}\"\n }\n ]\n}\n```" + }, + "post": { + "responses": { + "201": { + "description": "The newly created SSH key.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ssh_account_key" + } + } + } + }, + "400": { + "description": "If the submitted key or related value is invalid", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "403": { + "description": "If the current user does not have permission to add a key for the specified user" + }, + "404": { + "description": "If the specified user does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ssh_account_key" + } + } + }, + "description": "The new SSH key object. Note that the username property has been deprecated due to [privacy changes](https://developer.atlassian.com/cloud/bitbucket/bitbucket-api-changes-gdpr/#removal-of-usernames-from-user-referencing-apis)." + }, + "tags": ["Ssh"], + "summary": "Add a new SSH key", + "security": [ + { + "oauth2": ["account:write"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Adds a new SSH public key to the specified user account and returns the resulting key.\n\nExample:\n```\n$ curl -X POST -H \"Content-Type: application/json\" -d '{\"key\": \"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKqP3Cr632C2dNhhgKVcon4ldUSAeKiku2yP9O9/bDtY user@myhost\"}' https://api.bitbucket.org/2.0/users/{ed08f5e1-605b-4f4a-aee4-6c97628a673e}/ssh-keys\n\n{\n \"comment\": \"user@myhost\",\n \"created_on\": \"2018-03-14T13:17:05.196003+00:00\",\n \"key\": \"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKqP3Cr632C2dNhhgKVcon4ldUSAeKiku2yP9O9/bDtY\",\n \"label\": \"\",\n \"last_used\": \"2018-03-20T13:18:05.196003+00:00\",\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/users/{ed08f5e1-605b-4f4a-aee4-6c97628a673e}/ssh-keys/b15b6026-9c02-4626-b4ad-b905f99f763a\"\n }\n },\n \"owner\": {\n \"display_name\": \"Mark Adams\",\n \"links\": {\n \"avatar\": {\n \"href\": \"https://bitbucket.org/account/markadams-atl/avatar/32/\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/markadams-atl/\"\n },\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/users/{ed08f5e1-605b-4f4a-aee4-6c97628a673e}\"\n }\n },\n \"type\": \"user\",\n \"username\": \"markadams-atl\",\n \"nickname\": \"markadams-atl\",\n \"uuid\": \"{d7dd0e2d-3994-4a50-a9ee-d260b6cefdab}\"\n },\n \"type\": \"ssh_key\",\n \"uuid\": \"{b15b6026-9c02-4626-b4ad-b905f99f763a}\"\n}\n```" + }, + "parameters": [ + { + "name": "selected_user", + "in": "path", + "description": "This can either be the UUID of the account, surrounded by curly-braces, for\nexample: `{account UUID}`, OR an Atlassian Account ID.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/users/{selected_user}/ssh-keys/{key_id}": { + "delete": { + "responses": { + "204": { + "description": "The key has been deleted" + }, + "400": { + "description": "If the submitted key or related value is invalid", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "403": { + "description": "If the current user does not have permission to add a key for the specified user" + }, + "404": { + "description": "If the specified user does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Ssh"], + "summary": "Delete a SSH key", + "security": [ + { + "oauth2": ["account:write"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Deletes a specific SSH public key from a user's account\n\nExample:\n```\n$ curl -X DELETE https://api.bitbucket.org/2.0/users/{ed08f5e1-605b-4f4a-aee4-6c97628a673e}/ssh-keys/{b15b6026-9c02-4626-b4ad-b905f99f763a}\n```" + }, + "get": { + "responses": { + "200": { + "description": "The specific SSH key matching the user and UUID", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ssh_account_key" + } + } + } + }, + "403": { + "description": "If the specified user or key is not accessible to the current user" + }, + "404": { + "description": "If the specified user or key does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Ssh"], + "summary": "Get a SSH key", + "security": [ + { + "oauth2": ["account"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns a specific SSH public key belonging to a user.\n\nExample:\n```\n$ curl https://api.bitbucket.org/2.0/users/{ed08f5e1-605b-4f4a-aee4-6c97628a673e}/ssh-keys/{fbe4bbab-f6f7-4dde-956b-5c58323c54b3}\n\n{\n \"comment\": \"user@myhost\",\n \"created_on\": \"2018-03-14T13:17:05.196003+00:00\",\n \"key\": \"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKqP3Cr632C2dNhhgKVcon4ldUSAeKiku2yP9O9/bDtY\",\n \"label\": \"\",\n \"last_used\": \"2018-03-20T13:18:05.196003+00:00\",\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/users/{ed08f5e1-605b-4f4a-aee4-6c97628a673e}/ssh-keys/b15b6026-9c02-4626-b4ad-b905f99f763a\"\n }\n },\n \"owner\": {\n \"display_name\": \"Mark Adams\",\n \"links\": {\n \"avatar\": {\n \"href\": \"https://bitbucket.org/account/markadams-atl/avatar/32/\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/markadams-atl/\"\n },\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/users/{ed08f5e1-605b-4f4a-aee4-6c97628a673e}\"\n }\n },\n \"type\": \"user\",\n \"username\": \"markadams-atl\",\n \"nickname\": \"markadams-atl\",\n \"uuid\": \"{d7dd0e2d-3994-4a50-a9ee-d260b6cefdab}\"\n },\n \"type\": \"ssh_key\",\n \"uuid\": \"{b15b6026-9c02-4626-b4ad-b905f99f763a}\"\n}\n```" + }, + "put": { + "responses": { + "200": { + "description": "The newly updated SSH key.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ssh_account_key" + } + } + } + }, + "400": { + "description": "If the submitted key or related value is invalid", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "403": { + "description": "If the current user does not have permission to add a key for the specified user" + }, + "404": { + "description": "If the specified user does not exist", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ssh_account_key" + } + } + }, + "description": "The updated SSH key object" + }, + "tags": ["Ssh"], + "summary": "Update a SSH key", + "security": [ + { + "oauth2": ["account:write"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Updates a specific SSH public key on a user's account\n\nNote: Only the 'comment' field can be updated using this API. To modify the key or comment values, you must delete and add the key again.\n\nExample:\n```\n$ curl -X PUT -H \"Content-Type: application/json\" -d '{\"label\": \"Work key\"}' https://api.bitbucket.org/2.0/users/{ed08f5e1-605b-4f4a-aee4-6c97628a673e}/ssh-keys/{b15b6026-9c02-4626-b4ad-b905f99f763a}\n\n{\n \"comment\": \"\",\n \"created_on\": \"2018-03-14T13:17:05.196003+00:00\",\n \"key\": \"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKqP3Cr632C2dNhhgKVcon4ldUSAeKiku2yP9O9/bDtY\",\n \"label\": \"Work key\",\n \"last_used\": \"2018-03-20T13:18:05.196003+00:00\",\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/users/{ed08f5e1-605b-4f4a-aee4-6c97628a673e}/ssh-keys/b15b6026-9c02-4626-b4ad-b905f99f763a\"\n }\n },\n \"owner\": {\n \"display_name\": \"Mark Adams\",\n \"links\": {\n \"avatar\": {\n \"href\": \"https://bitbucket.org/account/markadams-atl/avatar/32/\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/markadams-atl/\"\n },\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/users/{ed08f5e1-605b-4f4a-aee4-6c97628a673e}\"\n }\n },\n \"type\": \"user\",\n \"username\": \"markadams-atl\",\n \"nickname\": \"markadams-atl\",\n \"uuid\": \"{d7dd0e2d-3994-4a50-a9ee-d260b6cefdab}\"\n },\n \"type\": \"ssh_key\",\n \"uuid\": \"{b15b6026-9c02-4626-b4ad-b905f99f763a}\"\n}\n```" + }, + "parameters": [ + { + "name": "key_id", + "in": "path", + "description": "The SSH key's UUID value.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "selected_user", + "in": "path", + "description": "This can either be the UUID of the account, surrounded by curly-braces, for\nexample: `{account UUID}`, OR an Atlassian Account ID.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/users/{username}/members": { + "get": { + "responses": { + "200": { + "description": "All members", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/user" + } + } + } + }, + "404": { + "description": "When the team does not exist, or multiple teams with the same name exist that differ only in casing and the URL did not match the exact casing of a particular one.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Users"], + "summary": "List team users", + "security": [ + { + "oauth2": ["account"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "**This endpoint has been removed.\nYou should use the [workspaces](/cloud/bitbucket/rest/api-group-workspaces/#api-workspaces-workspace-members-get) endpoint instead.\nFor more information, see [this post](https://developer.atlassian.com/cloud/bitbucket/bitbucket-api-teams-deprecation/).**", + "deprecated": true + }, + "parameters": [ + { + "name": "username", + "in": "path", + "description": "This can either be the username or the UUID of the account,\nsurrounded by curly-braces, for example: `{account UUID}`. An account\nis either a team or user.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/users/{workspace}/repositories": { + "get": { + "responses": { + "default": { + "description": "Unexpected error.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Users", "Teams"], + "summary": "List workspace repositories", + "security": [ + { + "oauth2": ["repository"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "All repositories in the given workspace. This includes any private\nrepositories the calling user has access to.\n\n**This endpoint has been removed.\nYou should use the [repository list](/cloud/bitbucket/rest/api-group-repositories/#api-repositories-workspace-get) endpoint instead.\nFor more information, see the [deprecation announcement](https://developer.atlassian.com/cloud/bitbucket/bitbucket-api-teams-deprecation/).**", + "deprecated": true + }, + "parameters": [ + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/workspaces": { + "get": { + "responses": { + "200": { + "description": "The list of workspaces accessible by the authenticated user.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_workspaces" + } + } + } + }, + "401": { + "description": "The request wasn't authenticated.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "parameters": [ + { + "name": "role", + "in": "query", + "description": "\n Filters the workspaces based on the authenticated user's role on each workspace.\n\n * **member**: returns a list of all the workspaces which the caller is a member of\n at least one workspace group or repository\n * **collaborator**: returns a list of workspaces which the caller has write access\n to at least one repository in the workspace\n * **owner**: returns a list of workspaces which the caller has administrator access\n ", + "required": false, + "schema": { + "type": "string", + "enum": ["owner", "collaborator", "member"] + } + }, + { + "name": "q", + "in": "query", + "description": "\nQuery string to narrow down the response. See\n[filtering and sorting](/cloud/bitbucket/rest/intro/#filtering) for details.", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "sort", + "in": "query", + "description": "\nName of a response property to sort results. See\n[filtering and sorting](/cloud/bitbucket/rest/intro/#sorting-query-results)\nfor details.\n", + "required": false, + "schema": { + "type": "string" + } + } + ], + "tags": ["Workspaces"], + "summary": "List workspaces for user", + "security": [ + { + "oauth2": ["account"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns a list of workspaces accessible by the authenticated user.\n\nExample:\n\n```\n$ curl https://api.bitbucket.org/2.0/workspaces\n\n{\n \"pagelen\": 10,\n \"page\": 1,\n \"size\": 1,\n \"values\": [\n {\n \"uuid\": \"{a15fb181-db1f-48f7-b41f-e1eff06929d6}\",\n \"links\": {\n \"owners\": {\n \"href\": \"https://api.bitbucket.org/2.0/workspaces/bbworkspace1/members?q=permission%3D%22owner%22\"\n },\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/workspaces/bbworkspace1\"\n },\n \"repositories\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/bbworkspace1\"\n },\n \"snippets\": {\n \"href\": \"https://api.bitbucket.org/2.0/snippets/bbworkspace1\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/bbworkspace1/\"\n },\n \"avatar\": {\n \"href\": \"https://bitbucket.org/workspaces/bbworkspace1/avatar/?ts=1543465801\"\n },\n \"members\": {\n \"href\": \"https://api.bitbucket.org/2.0/workspaces/bbworkspace1/members\"\n },\n \"projects\": {\n \"href\": \"https://api.bitbucket.org/2.0/workspaces/bbworkspace1/projects\"\n }\n },\n \"created_on\": \"2018-11-14T19:15:05.058566+00:00\",\n \"type\": \"workspace\",\n \"slug\": \"bbworkspace1\",\n \"is_private\": true,\n \"name\": \"Atlassian Bitbucket\"\n }\n ]\n}\n```\n\nResults may be further [filtered or sorted](/cloud/bitbucket/rest/intro/#filtering) by\nworkspace or permission by adding the following query string parameters:\n\n* `q=slug=\"bbworkspace1\"` or `q=is_private=true`\n* `sort=created_on`\n\nNote that the query parameter values need to be URL escaped so that `=`\nwould become `%3D`.\n\n**The `collaborator` role is being removed from the Bitbucket Cloud API. For more information,\nsee the [deprecation announcement](/cloud/bitbucket/deprecation-notice-collaborator-role/).**" + }, + "parameters": [] + }, + "/workspaces/{workspace}": { + "get": { + "responses": { + "200": { + "description": "The workspace.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/workspace" + } + } + } + }, + "404": { + "description": "If no workspace exists for the specified name or UUID.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Workspaces"], + "summary": "Get a workspace", + "security": [ + { + "oauth2": [] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns the requested workspace." + }, + "parameters": [ + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/workspaces/{workspace}/hooks": { + "get": { + "responses": { + "200": { + "description": "The paginated list of installed webhooks.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_webhook_subscriptions" + } + } + } + }, + "403": { + "description": "If the authenticated user is not an owner on the specified workspace.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "If the specified workspace does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Workspaces", "Webhooks"], + "summary": "List webhooks for a workspace", + "security": [ + { + "oauth2": ["webhook"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns a paginated list of webhooks installed on this workspace." + }, + "post": { + "responses": { + "201": { + "description": "If the webhook was registered successfully.", + "headers": { + "Location": { + "description": "The URL of new newly created webhook.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/webhook_subscription" + } + } + } + }, + "403": { + "description": "If the authenticated user does not have permission to install webhooks on the specified workspace.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "If the specified workspace does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Workspaces", "Webhooks"], + "summary": "Create a webhook for a workspace", + "security": [ + { + "oauth2": ["webhook"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Creates a new webhook on the specified workspace.\n\nWorkspace webhooks are fired for events from all repositories contained\nby that workspace.\n\nExample:\n\n```\n$ curl -X POST -u credentials -H 'Content-Type: application/json'\n https://api.bitbucket.org/2.0/workspaces/my-workspace/hooks\n -d '\n {\n \"description\": \"Webhook Description\",\n \"url\": \"https://example.com/\",\n \"active\": true,\n \"events\": [\n \"repo:push\",\n \"issue:created\",\n \"issue:updated\"\n ]\n }'\n```\n\nThis call requires the webhook scope, as well as any scope\nthat applies to the events that the webhook subscribes to. In the\nexample above that means: `webhook`, `repository` and `issue`.\n\nThe `url` must properly resolve and cannot be an internal, non-routed address.\n\nOnly workspace owners can install webhooks on workspaces." + }, + "parameters": [ + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/workspaces/{workspace}/hooks/{uid}": { + "delete": { + "responses": { + "204": { + "description": "When the webhook was deleted successfully" + }, + "403": { + "description": "If the authenticated user does not have permission to delete the webhook.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "If the webhook or workspace does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Workspaces", "Webhooks"], + "summary": "Delete a webhook for a workspace", + "security": [ + { + "oauth2": ["webhook"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Deletes the specified webhook subscription from the given workspace." + }, + "get": { + "responses": { + "200": { + "description": "The webhook subscription object.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/webhook_subscription" + } + } + } + }, + "404": { + "description": "If the webhook or workspace does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Workspaces", "Webhooks"], + "summary": "Get a webhook for a workspace", + "security": [ + { + "oauth2": ["webhook"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns the webhook with the specified id installed on the given\nworkspace." + }, + "put": { + "responses": { + "200": { + "description": "The webhook subscription object.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/webhook_subscription" + } + } + } + }, + "403": { + "description": "If the authenticated user does not have permission to update the webhook.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "If the webhook or workspace does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Workspaces", "Webhooks"], + "summary": "Update a webhook for a workspace", + "security": [ + { + "oauth2": ["webhook"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Updates the specified webhook subscription.\n\nThe following properties can be mutated:\n\n* `description`\n* `url`\n* `active`\n* `events`" + }, + "parameters": [ + { + "name": "uid", + "in": "path", + "description": "Installed webhook's ID", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/workspaces/{workspace}/members": { + "get": { + "responses": { + "200": { + "description": "The list of users that are part of a workspace.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_workspace_memberships" + } + } + } + }, + "401": { + "description": "The request wasn't authenticated.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Workspaces"], + "summary": "List users in a workspace", + "security": [ + { + "oauth2": ["account"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns all members of the requested workspace." + }, + "parameters": [ + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/workspaces/{workspace}/members/{member}": { + "get": { + "responses": { + "200": { + "description": "The user that is part of a workspace.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/workspace_membership" + } + } + } + }, + "401": { + "description": "The request wasn't authenticated.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "A workspace cannot be found, or a user cannot be found, or the user is not a a member of the workspace.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Workspaces"], + "summary": "Get user membership for a workspace", + "security": [ + { + "oauth2": ["account"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns the workspace membership, which includes\na `User` object for the member and a `Workspace` object\nfor the requested workspace." + }, + "parameters": [ + { + "name": "member", + "in": "path", + "description": "Member's UUID or Atlassian ID.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/workspaces/{workspace}/permissions": { + "get": { + "responses": { + "200": { + "description": "The list of users that are part of a workspace, along with their permission.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_workspace_memberships" + } + } + } + }, + "401": { + "description": "The request wasn't authenticated.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "parameters": [ + { + "name": "q", + "in": "query", + "description": "\nQuery string to narrow down the response as per\n[filtering and sorting](/cloud/bitbucket/rest/intro/#filtering).", + "required": false, + "schema": { + "type": "string" + } + } + ], + "tags": ["Workspaces"], + "summary": "List user permissions in a workspace", + "security": [ + { + "oauth2": ["account"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns the list of members in a workspace\nand their permission levels.\nPermission can be:\n* `owner`\n* `collaborator`\n* `member`\n\n**The `collaborator` role is being removed from the Bitbucket Cloud API. For more information,\nsee the [deprecation announcement](/cloud/bitbucket/deprecation-notice-collaborator-role/).**\n\nExample:\n\n```\n$ curl -X https://api.bitbucket.org/2.0/workspaces/bbworkspace1/permissions\n\n{\n \"pagelen\": 10,\n \"values\": [\n {\n \"permission\": \"owner\",\n \"type\": \"workspace_membership\",\n \"user\": {\n \"type\": \"user\",\n \"uuid\": \"{470c176d-3574-44ea-bb41-89e8638bcca4}\",\n \"display_name\": \"Erik van Zijst\",\n },\n \"workspace\": {\n \"type\": \"workspace\",\n \"uuid\": \"{a15fb181-db1f-48f7-b41f-e1eff06929d6}\",\n \"slug\": \"bbworkspace1\",\n \"name\": \"Atlassian Bitbucket\",\n }\n },\n {\n \"permission\": \"member\",\n \"type\": \"workspace_membership\",\n \"user\": {\n \"type\": \"user\",\n \"nickname\": \"seanaty\",\n \"display_name\": \"Sean Conaty\",\n \"uuid\": \"{504c3b62-8120-4f0c-a7bc-87800b9d6f70}\"\n },\n \"workspace\": {\n \"type\": \"workspace\",\n \"uuid\": \"{a15fb181-db1f-48f7-b41f-e1eff06929d6}\",\n \"slug\": \"bbworkspace1\",\n \"name\": \"Atlassian Bitbucket\",\n }\n }\n ],\n \"page\": 1,\n \"size\": 2\n}\n```\n\nResults may be further [filtered](/cloud/bitbucket/rest/intro/#filtering) by\npermission by adding the following query string parameters:\n\n* `q=permission=\"owner\"`" + }, + "parameters": [ + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/workspaces/{workspace}/permissions/repositories": { + "get": { + "responses": { + "200": { + "description": "List of workspace's repository permissions.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_repository_permissions" + } + } + } + }, + "403": { + "description": "The requesting user isn't an admin of the workspace.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "parameters": [ + { + "name": "q", + "in": "query", + "description": "\nQuery string to narrow down the response as per\n[filtering and sorting](/cloud/bitbucket/rest/intro/#filtering).", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "sort", + "in": "query", + "description": "\nName of a response property sort the result by as per\n[filtering and sorting](/cloud/bitbucket/rest/intro/#sorting-query-results).\n", + "required": false, + "schema": { + "type": "string" + } + } + ], + "tags": ["Workspaces"], + "summary": "List all repository permissions for a workspace", + "security": [ + { + "oauth2": ["account"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns an object for each repository permission for all of a\nworkspace's repositories.\n\nPermissions returned are effective permissions: the highest level of\npermission the user has. This does not distinguish between direct and\nindirect (group) privileges.\n\nOnly users with admin permission for the team may access this resource.\n\nPermissions can be:\n\n* `admin`\n* `write`\n* `read`\n\nExample:\n\n```\n$ curl https://api.bitbucket.org/2.0/workspaces/atlassian_tutorial/permissions/repositories\n\n{\n \"pagelen\": 10,\n \"values\": [\n {\n \"type\": \"repository_permission\",\n \"user\": {\n \"type\": \"user\",\n \"display_name\": \"Erik van Zijst\",\n \"uuid\": \"{d301aafa-d676-4ee0-88be-962be7417567}\"\n },\n \"repository\": {\n \"type\": \"repository\",\n \"name\": \"geordi\",\n \"full_name\": \"atlassian_tutorial/geordi\",\n \"uuid\": \"{85d08b4e-571d-44e9-a507-fa476535aa98}\"\n },\n \"permission\": \"admin\"\n },\n {\n \"type\": \"repository_permission\",\n \"user\": {\n \"type\": \"user\",\n \"display_name\": \"Sean Conaty\",\n \"uuid\": \"{504c3b62-8120-4f0c-a7bc-87800b9d6f70}\"\n },\n \"repository\": {\n \"type\": \"repository\",\n \"name\": \"geordi\",\n \"full_name\": \"atlassian_tutorial/geordi\",\n \"uuid\": \"{85d08b4e-571d-44e9-a507-fa476535aa98}\"\n },\n \"permission\": \"write\"\n },\n {\n \"type\": \"repository_permission\",\n \"user\": {\n \"type\": \"user\",\n \"display_name\": \"Jeff Zeng\",\n \"uuid\": \"{47f92a9a-c3a3-4d0b-bc4e-782a969c5c72}\"\n },\n \"repository\": {\n \"type\": \"repository\",\n \"name\": \"whee\",\n \"full_name\": \"atlassian_tutorial/whee\",\n \"uuid\": \"{30ba25e9-51ff-4555-8dd0-fc7ee2fa0895}\"\n },\n \"permission\": \"admin\"\n }\n ],\n \"page\": 1,\n \"size\": 3\n}\n```\n\nResults may be further [filtered or sorted](/cloud/bitbucket/rest/intro/#filtering)\nby repository, user, or permission by adding the following query string\nparameters:\n\n* `q=repository.name=\"geordi\"` or `q=permission>\"read\"`\n* `sort=user.display_name`\n\nNote that the query parameter values need to be URL escaped so that `=`\nwould become `%3D`." + }, + "parameters": [ + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/workspaces/{workspace}/permissions/repositories/{repo_slug}": { + "get": { + "responses": { + "200": { + "description": "The repository permission for all users in this repository.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_repository_permissions" + } + } + } + }, + "403": { + "description": "The requesting user isn't an admin of the repository.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "parameters": [ + { + "name": "q", + "in": "query", + "description": "\nQuery string to narrow down the response as per\n[filtering and sorting](/cloud/bitbucket/rest/intro/#filtering).", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "sort", + "in": "query", + "description": "\nName of a response property sort the result by as per\n[filtering and sorting](/cloud/bitbucket/rest/intro/#sorting-query-results).\n", + "required": false, + "schema": { + "type": "string" + } + } + ], + "tags": ["Workspaces"], + "summary": "List a repository permissions for a workspace", + "security": [ + { + "oauth2": ["repository"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns an object for the repository permission of each user in the\nrequested repository.\n\nPermissions returned are effective permissions: the highest level of\npermission the user has. This does not distinguish between direct and\nindirect (group) privileges.\n\nOnly users with admin permission for the repository may access this resource.\n\nPermissions can be:\n\n* `admin`\n* `write`\n* `read`\n\nExample:\n\n```\n$ curl https://api.bitbucket.org/2.0/workspaces/atlassian_tutorial/permissions/repositories/geordi\n\n{\n \"pagelen\": 10,\n \"values\": [\n {\n \"type\": \"repository_permission\",\n \"user\": {\n \"type\": \"user\",\n \"display_name\": \"Erik van Zijst\",\n \"uuid\": \"{d301aafa-d676-4ee0-88be-962be7417567}\"\n },\n \"repository\": {\n \"type\": \"repository\",\n \"name\": \"geordi\",\n \"full_name\": \"atlassian_tutorial/geordi\",\n \"uuid\": \"{85d08b4e-571d-44e9-a507-fa476535aa98}\"\n },\n \"permission\": \"admin\"\n },\n {\n \"type\": \"repository_permission\",\n \"user\": {\n \"type\": \"user\",\n \"display_name\": \"Sean Conaty\",\n \"uuid\": \"{504c3b62-8120-4f0c-a7bc-87800b9d6f70}\"\n },\n \"repository\": {\n \"type\": \"repository\",\n \"name\": \"geordi\",\n \"full_name\": \"atlassian_tutorial/geordi\",\n \"uuid\": \"{85d08b4e-571d-44e9-a507-fa476535aa98}\"\n },\n \"permission\": \"write\"\n }\n ],\n \"page\": 1,\n \"size\": 2\n}\n```\n\nResults may be further [filtered or sorted](/cloud/bitbucket/rest/intro/#filtering)\nby user, or permission by adding the following query string parameters:\n\n* `q=permission>\"read\"`\n* `sort=user.display_name`\n\nNote that the query parameter values need to be URL escaped so that `=`\nwould become `%3D`." + }, + "parameters": [ + { + "name": "repo_slug", + "in": "path", + "description": "This can either be the repository slug or the UUID of the repository,\nsurrounded by curly-braces, for example: `{repository UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/workspaces/{workspace}/pipelines-config/identity/oidc/.well-known/openid-configuration": { + "get": { + "responses": { + "200": { + "description": "The OpenID configuration" + }, + "404": { + "description": "The workspace was not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "parameters": [ + { + "description": "This can either be the workspace ID (slug) or the workspace UUID surrounded by curly-braces, for example `{workspace UUID}`.", + "required": true, + "name": "workspace", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "tags": ["Pipelines"], + "summary": "Get OpenID configuration for OIDC in Pipelines", + "operationId": "getOIDCConfiguration", + "description": "This is part of OpenID Connect for Pipelines, see https://support.atlassian.com/bitbucket-cloud/docs/integrate-pipelines-with-resource-servers-using-oidc/" + } + }, + "/workspaces/{workspace}/pipelines-config/identity/oidc/keys.json": { + "get": { + "responses": { + "200": { + "description": "The keys in JSON web key format" + }, + "404": { + "description": "The workspace was not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "parameters": [ + { + "description": "This can either be the workspace ID (slug) or the workspace UUID surrounded by curly-braces, for example `{workspace UUID}`.", + "required": true, + "name": "workspace", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "tags": ["Pipelines"], + "summary": "Get keys for OIDC in Pipelines", + "operationId": "getOIDCKeys", + "description": "This is part of OpenID Connect for Pipelines, see https://support.atlassian.com/bitbucket-cloud/docs/integrate-pipelines-with-resource-servers-using-oidc/" + } + }, + "/workspaces/{workspace}/pipelines-config/variables": { + "post": { + "responses": { + "201": { + "headers": { + "Location": { + "description": "The URL of the newly created pipeline variable.", + "schema": { + "type": "string" + } + } + }, + "description": "The created variable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/pipeline_variable" + } + } + } + }, + "404": { + "description": "The workspace does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "409": { + "description": "A variable with the provided key already exists.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "description": "Create a workspace level variable.", + "parameters": [ + { + "description": "This can either be the workspace ID (slug) or the workspace UUID surrounded by curly-braces, for example `{workspace UUID}`.", + "required": true, + "name": "workspace", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/pipeline_variable2" + }, + "tags": ["Pipelines"], + "summary": "Create a variable for a workspace", + "operationId": "createPipelineVariableForWorkspace" + }, + "get": { + "responses": { + "200": { + "description": "The found workspace level variables.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_pipeline_variables" + } + } + } + } + }, + "description": "Find workspace level variables.", + "parameters": [ + { + "description": "This can either be the workspace ID (slug) or the workspace UUID surrounded by curly-braces, for example `{workspace UUID}`.", + "required": true, + "name": "workspace", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "tags": ["Pipelines"], + "summary": "List variables for a workspace", + "operationId": "getPipelineVariablesForWorkspace" + } + }, + "/workspaces/{workspace}/pipelines-config/variables/{variable_uuid}": { + "put": { + "responses": { + "200": { + "description": "The variable was updated.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/pipeline_variable" + } + } + } + }, + "404": { + "description": "The workspace or the variable was not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "description": "Update a workspace level variable.", + "parameters": [ + { + "description": "This can either be the workspace ID (slug) or the workspace UUID surrounded by curly-braces, for example `{workspace UUID}`.", + "required": true, + "name": "workspace", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The UUID of the variable.", + "required": true, + "name": "variable_uuid", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "$ref": "#/components/requestBodies/pipeline_variable" + }, + "tags": ["Pipelines"], + "summary": "Update variable for a workspace", + "operationId": "updatePipelineVariableForWorkspace" + }, + "get": { + "responses": { + "200": { + "description": "The variable.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/pipeline_variable" + } + } + } + }, + "404": { + "description": "The workspace or variable with the given UUID was not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "description": "Retrieve a workspace level variable.", + "parameters": [ + { + "description": "This can either be the workspace ID (slug) or the workspace UUID surrounded by curly-braces, for example `{workspace UUID}`.", + "required": true, + "name": "workspace", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The UUID of the variable to retrieve.", + "required": true, + "name": "variable_uuid", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "tags": ["Pipelines"], + "summary": "Get variable for a workspace", + "operationId": "getPipelineVariableForWorkspace" + }, + "delete": { + "responses": { + "204": { + "description": "The variable was deleted" + }, + "404": { + "description": "The workspace or the variable with the provided UUID does not exist.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "description": "Delete a workspace level variable.", + "parameters": [ + { + "description": "This can either be the workspace ID (slug) or the workspace UUID surrounded by curly-braces, for example `{workspace UUID}`.", + "required": true, + "name": "workspace", + "in": "path", + "schema": { + "type": "string" + } + }, + { + "description": "The UUID of the variable to delete.", + "required": true, + "name": "variable_uuid", + "in": "path", + "schema": { + "type": "string" + } + } + ], + "tags": ["Pipelines"], + "summary": "Delete a variable for a workspace", + "operationId": "deletePipelineVariableForWorkspace" + } + }, + "/workspaces/{workspace}/projects": { + "get": { + "responses": { + "200": { + "description": "The list of projects in this workspace.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/paginated_projects" + } + } + } + }, + "404": { + "description": "A workspace doesn't exist at this location.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Workspaces"], + "summary": "List projects in a workspace", + "security": [ + { + "oauth2": ["project"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns the list of projects in this workspace." + }, + "post": { + "responses": { + "201": { + "description": "A new project has been created.", + "headers": { + "Location": { + "description": "The location of the newly created project", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/project" + } + } + } + }, + "403": { + "description": "The requesting user isn't authorized to create the project.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "A workspace doesn't exist at this location.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "requestBody": { + "$ref": "#/components/requestBodies/project" + }, + "tags": ["Projects"], + "summary": "Create a project in a workspace", + "security": [ + { + "oauth2": ["project:write"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Creates a new project.\n\nNote that the avatar has to be embedded as either a data-url\nor a URL to an external image as shown in the examples below:\n\n```\n$ body=$(cat << EOF\n{\n \"name\": \"Mars Project\",\n \"key\": \"MARS\",\n \"description\": \"Software for colonizing mars.\",\n \"links\": {\n \"avatar\": {\n \"href\": \"data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/...\"\n }\n },\n \"is_private\": false\n}\nEOF\n)\n$ curl -H \"Content-Type: application/json\" \\\n -X POST \\\n -d \"$body\" \\\n https://api.bitbucket.org/2.0/teams/teams-in-space/projects/ | jq .\n{\n // Serialized project document\n}\n```\n\nor even:\n\n```\n$ body=$(cat << EOF\n{\n \"name\": \"Mars Project\",\n \"key\": \"MARS\",\n \"description\": \"Software for colonizing mars.\",\n \"links\": {\n \"avatar\": {\n \"href\": \"http://i.imgur.com/72tRx4w.gif\"\n }\n },\n \"is_private\": false\n}\nEOF\n)\n$ curl -H \"Content-Type: application/json\" \\\n -X POST \\\n -d \"$body\" \\\n https://api.bitbucket.org/2.0/teams/teams-in-space/projects/ | jq .\n{\n // Serialized project document\n}\n```" + }, + "parameters": [ + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/workspaces/{workspace}/projects/{project_key}": { + "delete": { + "responses": { + "204": { + "description": "Successful deletion." + }, + "403": { + "description": "The requesting user isn't authorized to delete the project or the project isn't empty.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "A project isn't hosted at this location.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Projects"], + "summary": "Delete a project for a workspace", + "security": [ + { + "oauth2": ["project:write"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Deletes this project. This is an irreversible operation.\n\nYou cannot delete a project that still contains repositories.\nTo delete the project, [delete](/cloud/bitbucket/rest/api-group-repositories/#api-repositories-workspace-repo-slug-delete)\nor transfer the repositories first.\n\nExample:\n```\n$ curl -X DELETE https://api.bitbucket.org/2.0/bbworkspace1/PROJ\n```" + }, + "get": { + "responses": { + "200": { + "description": "The project that is part of a workspace.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/project" + } + } + } + }, + "401": { + "description": "The request wasn't authenticated.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "403": { + "description": "The requesting user isn't authorized to access the project.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "A project isn't hosted at this location.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "tags": ["Projects", "Workspaces"], + "summary": "Get a project for a workspace", + "security": [ + { + "oauth2": ["project"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Returns the requested project." + }, + "put": { + "responses": { + "200": { + "description": "The existing project is has been updated.", + "headers": { + "Location": { + "description": "The location of the project. This header is only provided\nwhen the project key is updated.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/project" + } + } + } + }, + "201": { + "description": "A new project has been created.", + "headers": { + "Location": { + "description": "The location of the newly created project", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/project" + } + } + } + }, + "403": { + "description": "The requesting user isn't authorized to update or create the project.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "A workspace doesn't exist at the location. Note that the project's absence from this location doesn't raise a 404, since a PUT at a non-existent location can be used to create a new project.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "requestBody": { + "$ref": "#/components/requestBodies/project" + }, + "tags": ["Projects"], + "summary": "Update a project for a workspace", + "security": [ + { + "oauth2": ["project:write"] + }, + { + "basic": [] + }, + { + "api_key": [] + } + ], + "description": "Since this endpoint can be used to both update and to create a\nproject, the request body depends on the intent.\n\n#### Creation\n\nSee the POST documentation for the project collection for an\nexample of the request body.\n\nNote: The `key` should not be specified in the body of request\n(since it is already present in the URL). The `name` is required,\neverything else is optional.\n\n#### Update\n\nSee the POST documentation for the project collection for an\nexample of the request body.\n\nNote: The key is not required in the body (since it is already in\nthe URL). The key may be specified in the body, if the intent is\nto change the key itself. In such a scenario, the location of the\nproject is changed and is returned in the `Location` header of the\nresponse." + }, + "parameters": [ + { + "name": "project_key", + "in": "path", + "description": "The project in question. This is the actual `key` assigned\nto the project.\n", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "workspace", + "in": "path", + "description": "This can either be the workspace ID (slug) or the workspace UUID\nsurrounded by curly-braces, for example: `{workspace UUID}`.\n", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/workspaces/{workspace}/search/code": { + "get": { + "responses": { + "200": { + "description": "Successful search", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/search_result_page" + } + } + } + }, + "400": { + "description": "If the search request was invalid due to one of the\nfollowing reasons:\n\n* the specified type of target account doesn''t match the actual\naccount type;\n\n* malformed pagination properties;\n\n* missing or malformed search query, in the latter case an error\nkey will be returned in `error.data.key` property.\n", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "404": { + "description": "Search is not enabled for the requested workspace, navigate to [https://bitbucket.org/search](https://bitbucket.org/search) to turn it on", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "429": { + "description": "Too many requests, try again later", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + }, + "parameters": [ + { + "required": true, + "description": "The workspace to search in; either the slug or the UUID in curly braces", + "in": "path", + "name": "workspace", + "schema": { + "type": "string" + } + }, + { + "required": true, + "description": "The search query", + "in": "query", + "name": "search_query", + "schema": { + "type": "string" + } + }, + { + "description": "Which page of the search results to retrieve", + "required": false, + "in": "query", + "name": "page", + "schema": { + "type": "integer", + "format": "int32", + "default": 1 + } + }, + { + "description": "How many search results to retrieve per page", + "required": false, + "in": "query", + "name": "pagelen", + "schema": { + "type": "integer", + "format": "int32", + "default": 10 + } + } + ], + "tags": ["Search"], + "summary": "Search for code in a workspace", + "operationId": "searchWorkspace", + "description": "Search for code in the repositories of the specified workspace.\n\nSearching across all repositories:\n\n```\ncurl 'https://api.bitbucket.org/2.0/workspaces/workspace_slug_or_uuid/search/code?search_query=foo'\n{\n \"size\": 1,\n \"page\": 1,\n \"pagelen\": 10,\n \"query_substituted\": false,\n \"values\": [\n {\n \"type\": \"code_search_result\",\n \"content_match_count\": 2,\n \"content_matches\": [\n {\n \"lines\": [\n {\n \"line\": 2,\n \"segments\": []\n },\n {\n \"line\": 3,\n \"segments\": [\n {\n \"text\": \"def \"\n },\n {\n \"text\": \"foo\",\n \"match\": true\n },\n {\n \"text\": \"():\"\n }\n ]\n },\n {\n \"line\": 4,\n \"segments\": [\n {\n \"text\": \" print(\\\"snek\\\")\"\n }\n ]\n },\n {\n \"line\": 5,\n \"segments\": []\n }\n ]\n }\n ],\n \"path_matches\": [\n {\n \"text\": \"src/\"\n },\n {\n \"text\": \"foo\",\n \"match\": true\n },\n {\n \"text\": \".py\"\n }\n ],\n \"file\": {\n \"path\": \"src/foo.py\",\n \"type\": \"commit_file\",\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/my-workspace/demo/src/ad6964b5fe2880dbd9ddcad1c89000f1dbcbc24b/src/foo.py\"\n }\n }\n }\n }\n ]\n}\n```\n\nNote that searches can match in the file's text (`content_matches`),\nthe path (`path_matches`), or both as in the example above.\n\nYou can use the same syntax for the search query as in the UI, e.g.\nto only search within a specific repository:\n\n```\ncurl 'https://api.bitbucket.org/2.0/workspaces/my-workspace/search/code?search_query=foo+repo:demo'\n# results from the \"demo\" repository\n```\n\nSimilar to other APIs, you can request more fields using a\n`fields` query parameter. E.g. to get some more information about\nthe repository of matched files (the `%2B` is a URL-encoded `+`):\n\n```\ncurl 'https://api.bitbucket.org/2.0/workspaces/my-workspace/search/code'\\\n '?search_query=foo&fields=%2Bvalues.file.commit.repository'\n{\n \"size\": 1,\n \"page\": 1,\n \"pagelen\": 10,\n \"query_substituted\": false,\n \"values\": [\n {\n \"type\": \"code_search_result\",\n \"content_match_count\": 1,\n \"content_matches\": [...],\n \"path_matches\": [...],\n \"file\": {\n \"commit\": {\n \"type\": \"commit\",\n \"hash\": \"ad6964b5fe2880dbd9ddcad1c89000f1dbcbc24b\",\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/my-workspace/demo/commit/ad6964b5fe2880dbd9ddcad1c89000f1dbcbc24b\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/my-workspace/demo/commits/ad6964b5fe2880dbd9ddcad1c89000f1dbcbc24b\"\n }\n },\n \"repository\": {\n \"name\": \"demo\",\n \"type\": \"repository\",\n \"full_name\": \"my-workspace/demo\",\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/my-workspace/demo\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/my-workspace/demo\"\n },\n \"avatar\": {\n \"href\": \"https://bytebucket.org/ravatar/%7B850e1749-781a-4115-9316-df39d0600e7a%7D?ts=default\"\n }\n },\n \"uuid\": \"{850e1749-781a-4115-9316-df39d0600e7a}\"\n }\n },\n \"type\": \"commit_file\",\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/my-workspace/demo/src/ad6964b5fe2880dbd9ddcad1c89000f1dbcbc24b/src/foo.py\"\n }\n },\n \"path\": \"src/foo.py\"\n }\n }\n ]\n}\n```\n\nTry `fields=%2Bvalues.*.*.*.*` to get an idea what's possible.\n" + } + } + }, + "tags": [ + { + "name": "Addon", + "description": "The addon resource is intended to use used by Bitbucket Cloud Connect\nApps, and only supports JWT authentication.\n" + }, + { + "name": "Branch restrictions", + "description": "Repository owners and administrators can set branch management\nrules on a repository that control what can be pushed by whom.\nThrough these rules, you can enforce a project or team\nworkflow. For example, owners or administrators can:\n\n* Limit push powers\n* Prevent branch deletion\n* Prevent history re-writes (Git only)\n" + }, + { + "name": "Branching model", + "description": "The branching model resource is used to modify the branching model\nfor a repository.\n\nYou can use the branching model to define a branch based workflow\nfor your repositories. When you map your workflow to branch types,\nyou can ensure that branches are named consistently by configuring\nwhich branch types to make available.\n" + }, + { + "name": "Commit statuses", + "description": "Commit statuses provide a way to tag commits with meta data,\nlike automated build results.\n" + }, + { + "name": "Commits", + "description": "These are the repository's commits. They are paginated and returned in\nreverse chronological order, similar to the output of git log.\n" + }, + { + "name": "Deployments", + "description": "Teams are deploying code faster than ever, thanks to continuous\ndelivery practices and tools like Bitbucket Pipelines. Bitbucket\nDeployments gives teams visibility into their deployment\nenvironments and helps teams to track how far changes have\nprogressed in their deployment pipeline.\n" + }, + { + "name": "Downloads", + "description": "Access the list of download links associated with the repository." + }, + { + "name": "Issue tracker", + "description": "The issue resources provide functionality for getting information on\nissues in an issue tracker, creating new issues, updating them and deleting\nthem.\n\nYou can access public issues without authentication, but you can't gain access\nto private repositories' issues. By authenticating, you will get the ability\nto create issues, as well as access to updating data or deleting issues you\nhave access to.\n" + }, + { + "name": "Pipelines", + "description": "Bitbucket Pipelines brings continuous delivery to Bitbucket\nCloud, empowering teams with full branching to deployment\nvisibility and faster feedback loops.\n" + }, + { + "name": "Projects", + "description": "Bitbucket Cloud projects make it easier for teams to focus on\na goal, product, or process by organizing their repositories.\n" + }, + { + "name": "Pullrequests", + "description": "Pull requests are a feature that makes it easier for developers\nto collaborate using Bitbucket. They provide a user-friendly web\ninterface for discussing proposed changes before integrating them\ninto the official project.\n" + }, + { + "name": "Refs", + "description": "The refs resource allows you access branches and tags in a repository.\nBy default, results will be in the order the underlying source control\nsystem returns them and identical to the ordering one sees when running\n\"$ git show-ref\". Note that this follows simple lexical ordering of the\n ref names.\n" + }, + { + "name": "Reports", + "description": "Code insights provides reports, annotations, and metrics to help you\nand your team improve code quality in pull requests throughout the code\nreview process. Some of the available code insights are static analysis\nreports, security scan results, artifact links, unit tests, and build\nstatus.\n" + }, + { + "name": "Repositories", + "description": "A Git repository is a virtual storage of your project. It\nallows you to save versions of your code, which you can access\nwhen needed. The repo resource allows you to access public repos,\nor repos that belong to a specific workspace.\n" + }, + { + "name": "Snippets", + "description": "Snippets allow you share code segments or files with yourself, members of\nyour workspace, or the world.\n\nLike pull requests, repositories and workspaces, the full set of snippets\nis defined by what the current user has access to. This includes all\nsnippets owned by any of the workspaces the user is a member of, or\nsnippets by other users that the current user is either watching or has\n collaborated on (for instance by commenting on it).\n" + }, + { + "name": "Source", + "description": "Browse the source code in the repository and\n create new commits by uploading." + }, + { + "name": "Ssh", + "description": "The SSH resource allows you to manage SSH keys.\n" + }, + { + "name": "Teams", + "description": "The teams resource has been deprecated, and the workspaces\nendpoint should be used instead.\n\nThe teams resource returns all the teams that the authenticated\nuser is associated with.\n" + }, + { + "name": "Users", + "description": "The users resource allows you to access public information\nassociated with a user account. Most resources in the users\nendpoint have been deprecated in favor of workspaces.\n" + }, + { + "name": "Webhooks", + "description": "Webhooks provide a way to configure Bitbucket Cloud to make requests to\nyour server (or another external service) whenever certain events occur in\nBitbucket Cloud.\n\nA webhook consists of:\n\n* A subject -- The resource that generates the events. Currently, this resource\nis the repository, user account, or team where you create the webhook.\n* One or more event -- The default event is a repository push, but you can\nselect multiple events that can trigger the webhook.\n* A URL -- The endpoint where you want Bitbucket to send the event payloads\nwhen a matching event happens.\n\nThere are two parts to getting a webhook to work: creating the webhook and\ntriggering the webhook. After you create a webhook for an event, every time\nthat event occurs, Bitbucket sends a payload request that describes the event\nto the specified URL. Thus, you can think of webhooks as a kind of\nnotification system.\n\nUse webhooks to integrate applications with Bitbucket Cloud. The following\nuse cases provides examples of when you would want to use webhooks:\n\n* Every time a user pushes commits in a repository, you may want to notify\nyour CI server to start a build.\n* Every time a user pushes commits or creates a pull request, you may want to\ndisplay a notification in your application.\n" + }, + { + "name": "Wiki", + "description": "The wiki is a simple place to keep documents. Some people use it\nas their project home page. The wiki is a Git repository, so you\ncan clone it and edit it like any other source files.\n" + }, + { + "name": "Workspaces", + "description": "A workspace is where you create repositories, collaborate on\nyour code, and organize different streams of work in your Bitbucket\nCloud account. Workspaces replace the use of teams and users in API\ncalls.\n" + } + ], + "x-revision": "4ec1005c9aa8", + "x-atlassian-narrative": { + "documents": [ + { + "body": "\nThe purpose of this section is to describe how to authenticate when making API calls using the Bitbucket REST API.\n\n-----\n\n* [Oauth 2](#oauth-2)\n * [Making requests](#making-requests)\n * [Repository cloning](#repository-cloning)\n * [Refresh tokens](#refresh-tokens)\n* [Scopes](#scopes)\n* [Basic auth](#basic-auth)\n* [App passwords](#app-passwords)\n\n---\n\n### OAuth 2.0\n\nOur OAuth 2 implementation is merged in with our existing OAuth 1 in\nsuch a way that existing OAuth 1 consumers automatically become\nvalid OAuth 2 clients. The only thing you need to do is edit your\nexisting consumer and configure a callback URL.\n\nOnce that is in place, you'll have the following 2 URLs:\n\n https://bitbucket.org/site/oauth2/authorize\n https://bitbucket.org/site/oauth2/access_token\n\nFor obtaining access/bearer tokens, we support three of RFC-6749's grant\nflows, plus a custom Bitbucket flow for exchanging JWT tokens for access tokens.\nNote that Resource Owner Password Credentials Grant (4.3) is no longer supported.\n\n\n#### 1. Authorization Code Grant (4.1)\n\nThe full-blown 3-LO flow. Request authorization from the end user by\nsending their browser to:\n\n https://bitbucket.org/site/oauth2/authorize?client_id={client_id}&response_type=code\n\nThe callback includes the `?code={}` query parameter that you can swap\nfor an access token:\n\n $ curl -X POST -u \"client_id:secret\" \\\n https://bitbucket.org/site/oauth2/access_token \\\n -d grant_type=authorization_code -d code={code}\n\n\n#### 2. Implicit Grant (4.2)\n\nThis flow is useful for browser-based add-ons that operate without server-side backends.\n\nRequest the end user for authorization by directing the browser to:\n\n https://bitbucket.org/site/oauth2/authorize?client_id={client_id}&response_type=token\n\nThat will redirect to your preconfigured callback URL with a fragment\ncontaining the access token\n(`#access_token={token}&token_type=bearer`) where your page's js can\npull it out of the URL.\n\n\n#### 3. Client Credentials Grant (4.4)\n\nSomewhat like our existing \"2-LO\" flow for OAuth 1. Obtain an access\ntoken that represents not an end user, but the owner of the\nclient/consumer:\n\n $ curl -X POST -u \"client_id:secret\" \\\n https://bitbucket.org/site/oauth2/access_token \\\n -d grant_type=client_credentials\n\n\n#### 4. Bitbucket Cloud JWT Grant (urn:bitbucket:oauth2:jwt)\n\nIf your Atlassian Connect add-on uses JWT authentication, you can swap a\nJWT for an OAuth access token. The resulting access token represents the\naccount for which the add-on is installed.\n\nMake sure you send the JWT token in the Authorization request header\nusing the \"JWT\" scheme (case sensitive). Note that this custom scheme\nmakes this different from HTTP Basic Auth (and so you cannot use \"curl\n-u\").\n\n $ curl -X POST -H \"Authorization: JWT {jwt_token}\" \\\n https://bitbucket.org/site/oauth2/access_token \\\n -d grant_type=urn:bitbucket:oauth2:jwt\n\n\n#### Making Requests\n\nOnce you have an access token, as per RFC-6750, you can use it in a request in any of\nthe following ways (in decreasing order of desirability):\n\n1. Send it in a request header: `Authorization: Bearer {access_token}`\n2. Include it in a (application/x-www-form-urlencoded) POST body as `access_token={access_token}`\n3. Put it in the query string of a non-POST: `?access_token={access_token}`\n\n\n#### Repository Cloning\n\nSince add-ons will not be able to upload their own SSH keys to clone\nwith, access tokens can be used as Basic HTTP Auth credentials to\nclone securely over HTTPS. This is much like GitHub, yet slightly\ndifferent:\n\n $ git clone https://x-token-auth:{access_token}@bitbucket.org/user/repo.git\n\nThe literal string `x-token-auth` as a substitute for username is\nrequired (note the difference with GitHub where the actual token is in\nthe username field).\n\n\n#### Refresh Tokens\n\nOur access tokens expire in one hour. When this happens you'll get 401\nresponses.\n\nMost access tokens grant responses (Implicit and JWT excluded). Therefore, you should include a\nrefresh token that can then be used to generate a new access token,\nwithout the need for end user participation:\n\n $ curl -X POST -u \"client_id:secret\" \\\n https://bitbucket.org/site/oauth2/access_token \\\n -d grant_type=refresh_token -d refresh_token={refresh_token}\n\n\n### Scopes\n\nBitbucket's API applies a number of privilege scopes to endpoints. In order to access an endpoint, a request will need to have the necessary scopes.\n\nScopes are declared in the descriptor as a list of strings, with each string being the name of a unique scope.\n\nA descriptor lacking the `scopes` element is implicitly assumed to require all scopes and as a result, Bitbucket will require end users authorizing/installing the add-on\nto explicitly accept all scopes.\n\nOur best practice suggests you add the scopes your add-on needs, but no more than it needs.\n\nInvalid scope strings will cause the descriptor to be rejected and the installation to fail.\n\nFollowing is the set of all currently available scopes.\n\n#### repository\n\nGives the add-on read access to all the repositories the authorizing user has access to.\nNote that this scope does not give access to a repository's pull requests.\n\n* access to the repo's source code\n* clone over https\n* access the the file browsing API\n* download zip archives of the repo's contents\n* the ability to view and use the issue tracker on any repo (created issues, comment, vote, etc)\n* the ability to view and use the wiki on any repo (create/edit pages)\n\n#### repository:write\n\nGives the add-on write (not admin) access to all the repositories the authorizing user has access to. No distinction is made between public or private repos. This scope implies `repository`, which does not need to be requested separately.\nThis scope alone does not give access to the pull requests API.\n\n* push access over https\n* fork repos\n\n#### repository:admin\n\nGives the add-on admin access to all the repositories the authorizing user has access to. No distinction is made between public or private repos. This scope does not imply `repository` or `repository:write`. It gives access to the admin features of a repo only, not direct access to its contents. Of course it can be (mis)used to grant read access to another user account who can then clone the repo, but repos that need to read of write source code would also request explicit read or write.\nThis scope comes with access to the following functionality:\n\n* view and manipulate committer mappings\n* list and edit deploy keys\n* ability to delete the repo\n* view and edit repo permissions\n* view and edit branch permissions\n* import and export the issue tracker\n* enable and disable the issue tracker\n* list and edit issue tracker version, milestones and components\n* enable and disable the wiki\n* list and edit default reviewers\n* list and edit repo links (Jira/Bamboo/Custom)\n* list and edit the repository web hooks\n* initiate a repo ownership transfer\n\n#### snippet\n\nGives the add-on read access to all the snippets the authorizing user has access to.\nNo distinction is made between public and private snippets (public snippets are accessible without any form of authentication).\n\n* view any snippet\n* create snippet comments\n\n#### snippet:write\n\nGives the add-on write access to all the snippets the authorizing user can edit.\nNo distinction is made between public and private snippets (public snippets are accessible without any form of authentication).\nThis implies the Snippet Read scope which does not need to be requested separately.\n\n* edit snippets\n* delete snippets\n\n#### issue\n\nAbility to interact with issue trackers the way non-repo members can.\nThis scope does not imply any other scopes and does not give implicit access to the repository the issue is attached to.\n\n* view, list and search issues\n* create new issues\n* comment on issues\n* watch issues\n* vote for issues\n\n#### issue:write\n\nThis implies `issue`, but adds the ability to transition and delete issues.\nThis scope does not imply any other scopes and does not give implicit access to the repository the issue is attached to.\n\n* transition issues\n* delete issues\n\n#### wiki\n\nGives access to wikis. No distinction is made between read and write as wikis are always editable by anyone.\nThis scope does not imply any other scopes and does not give implicit access to the repository the wiki is attached to.\n\n* view wikis\n* create pages\n* edit pages\n* push to wikis\n* clone wikis\n\n#### pullrequest\n\nGives the add-on read access to pull requests.\nThis scope implies `repository`, giving read access to the pull request's destination repository.\n\n* see and list pull requests\n* create and resolve tasks\n\n#### pullrequest:write\n\nImplies `pullrequest` but adds the ability to create, merge and decline pull requests.\nThis scope implies `repository:write`, giving write access to the pull request's destination repository. This is necessary to facilitate merging.\n\n* merge pull requests\n* decline pull requests\n* create pull requests\n* comment on pull requests\n* approve pull requests\n\n#### email\n\nAbility to see the user's primary email address. This should make it easier to use Bitbucket Cloud as a login provider to add-ons or external applications.\n\n#### account\n\nAbility to see all the user's account information. Note that this does not include any ability to mutate any of the data.\n\n* see all email addresses\n* language\n* location\n* website\n* full name\n* SSH keys\n* user groups\n\n#### account:write\n\nAbility to change properties on the user's account.\n\n* delete the authorizing user's account\n* manage the user's groups\n* manupilate a user's email addresses\n* change username, display name and avatar\n\n#### team\n\nThe ability to find out what teams the current user is part of. This is covered by the teams endpoint.\n\n* information about all the groups and teams I am a member or admin of\n\n\n#### team:write\n\nImplies `team`, but adds the ability to manage the teams that the authorizing user is an admin on.\n\n* manage team permissions\n\n#### webhook\n\nGives access to webhooks. This scope is required for any webhook\nrelated operation.\n\nThis scope gives read access to existing webhook subscriptions on all\nresources you can access, without needing further scopes. This means that\na client can list all existing webhook subscriptions on repository\n`foo/bar` (assuming the principal user has access to this repo). The\nadditional `repository` scope is not required for this.\n\nLikewise, existing webhook subscriptions for a repo's issue tracker can be\nretrieved without holding the `issue` scope. All that is required is the\n`webhook` scope.\n\nHowever, to create a webhook for `issue:created`, the client will need to\nhave both the `webhook` as well as `issue` scope.\n\n* list webhook subscriptions on any accessible repository, user, team, or snippet\n* create/update/delete webhook subscriptions\n\n### pipeline\n\nGives read-only access to pipelines, steps, deployment environments and variables.\n\n### pipeline:write\n\nGives write access to pipelines. This scope allows a user to:\n* Stop pipelines\n* Rerun failed pipelines\n* Resume halted pipelines\n* Trigger manual pipelines.\n\nThis scope is not needed to trigger a build via a push. The act to doing push will trigger the build. The token doing the push only needs repository:write scope.\n\nThis does not give write access to create variables.\n\n### pipeline:variable\n\nGives write access to create variables in pipelines at the various levels:\n* Workspace\n* Repository\n* Deployment\n\n### runner\n\nGives read-only access to pipelines runners setup against a workspace or repository.\n\n### runner:write\n\nGives write access to create/edit/disable/delete pipelines runners setup against a workspace or repository.\n\n### Basic auth\n\nBasic HTTP Authentication as per [RFC-2617](https://tools.ietf.org/html/rfc2617) (Digest not supported). Note that Basic Auth is available only with username and [app password](https://bitbucket.org/account/settings/app-passwords/) as credentials.\n\n### App passwords\n\nApp passwords allow users to make API calls to their Bitbucket account through apps such as Sourcetree.\n\nSome important points about app passwords:\n\n* You cannot view an app password or adjust permissions after you create the app password. Because app passwords are encrypted on our database and cannot be viewed by anyone. They are essentially designed to be disposable. If you need to change the scopes or lost the password just create a new one.\n* You cannot use them to log into your Bitbucket account.\n* You cannot use app passwords to manage team actions.\n\n App passwords are tied to an individual account's credentials and should not be shared. If you're sharing your app password you're essentially giving direct, authenticated, access to everything that password has been scoped to do with the Bitbucket API's.\n\n* You can use them for API call authentication, even if you don't have two-step verification enabled.\n* You can set permission scopes (specific access rights) for each app password.\n\n#### Create an app password\n\nTo create an app password:\n\n1. Select **Avatar > Bitbucket settings**.\n2. [Click **App passwords** in the Access management section.](https://bitbucket.org/account/settings/app-passwords/)\n3. Click **Create app password**.\n4. Give the app password a name related to the application that will use the password.\n5. Select the specific access and permissions you want this application password to have.\n6. Copy the generated password and either record or paste it into the application you want to give access. The password is only displayed this one time.\n\nThat's all there is to creating an app password. See your applications documentation for how to apply the app password for a specific application.", + "title": "Authentication methods", + "anchor": "authentication", + "description": "How to authenticate API actions", + "icon": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxOTcuNjQ3MyAxODYuODEzOCI+CiAgPGRlZnM+CiAgICA8c3R5bGU+CiAgICAgIC5jbHMtMSB7CiAgICAgICAgaXNvbGF0aW9uOiBpc29sYXRlOwogICAgICB9CgogICAgICAuY2xzLTIgewogICAgICAgIGZpbGw6ICNkZTM1MGI7CiAgICAgIH0KCiAgICAgIC5jbHMtMyB7CiAgICAgICAgZmlsbDogI2ZmNTYzMDsKICAgICAgfQoKICAgICAgLmNscy00IHsKICAgICAgICBmaWxsOiAjZGZlMWU1OwogICAgICAgIG1peC1ibGVuZC1tb2RlOiBtdWx0aXBseTsKICAgICAgfQoKICAgICAgLmNscy01IHsKICAgICAgICBmaWxsOiAjZmFmYmZjOwogICAgICB9CgogICAgICAuY2xzLTYgewogICAgICAgIGZpbGw6ICNlYmVjZjA7CiAgICAgIH0KCiAgICAgIC5jbHMtNyB7CiAgICAgICAgZmlsbDogbm9uZTsKICAgICAgICBzdHJva2U6ICMwMDY1ZmY7CiAgICAgICAgc3Ryb2tlLW1pdGVybGltaXQ6IDEwOwogICAgICAgIHN0cm9rZS13aWR0aDogMnB4OwogICAgICB9CgogICAgICAuY2xzLTggewogICAgICAgIGZpbGw6ICM1ZTZjODQ7CiAgICAgIH0KCiAgICAgIC5jbHMtOSB7CiAgICAgICAgZmlsbDogIzI1Mzg1ODsKICAgICAgfQoKICAgICAgLmNscy0xMCB7CiAgICAgICAgZmlsbDogIzI2ODRmZjsKICAgICAgfQoKICAgICAgLmNscy0xMSB7CiAgICAgICAgZmlsbDogIzAwNjVmZjsKICAgICAgfQogICAgPC9zdHlsZT4KICA8L2RlZnM+CiAgPHRpdGxlPlNlY3VyaXR5IHdpdGggS2V5PC90aXRsZT4KICA8ZyBjbGFzcz0iY2xzLTEiPgogICAgPGcgaWQ9IkxheWVyXzIiIGRhdGEtbmFtZT0iTGF5ZXIgMiI+CiAgICAgIDxnIGlkPSJPYmplY3RzIj4KICAgICAgICA8cGF0aCBjbGFzcz0iY2xzLTIiIGQ9Ik00Mi4wNjcyLDBoLjYxMTRhOCw4LDAsMCwxLDgsOFYyMy4yMzM4YTAsMCwwLDAsMSwwLDBIMzQuMDY3MmEwLDAsMCwwLDEsMCwwVjhBOCw4LDAsMCwxLDQyLjA2NzIsMFoiLz4KICAgICAgICA8cGF0aCBjbGFzcz0iY2xzLTIiIGQ9Ik0xMDguMjIsMGguNjExNGE4LDgsMCwwLDEsOCw4VjIzLjIzMzhhMCwwLDAsMCwxLDAsMEgxMDAuMjJhMCwwLDAsMCwxLDAsMFY4QTgsOCwwLDAsMSwxMDguMjIsMFoiLz4KICAgICAgICA8cGF0aCBjbGFzcz0iY2xzLTIiIGQ9Ik0xNzQuMzcyMiwwaC42MTE0YTgsOCwwLDAsMSw4LDhWMjMuMjMzOGEwLDAsMCwwLDEsMCwwSDE2Ni4zNzIyYTAsMCwwLDAsMSwwLDBWOEE4LDgsMCwwLDEsMTc0LjM3MjIsMFoiLz4KICAgICAgICA8cmVjdCBjbGFzcz0iY2xzLTIiIHg9IjM0LjA2NzIiIHk9IjIzLjIzMzgiIHdpZHRoPSIxNjMuNTgiIGhlaWdodD0iMTYzLjU4Ii8+CiAgICAgICAgPHBhdGggY2xhc3M9ImNscy0yIiBkPSJNNDIuMDY3MiwwSDU5LjI5YTgsOCwwLDAsMSw4LDhWMjMuMjIyOGEwLDAsMCwwLDEsMCwwSDM0LjA2NzJhMCwwLDAsMCwxLDAsMFY4YTgsOCwwLDAsMSw4LThaIi8+CiAgICAgICAgPHBhdGggY2xhc3M9ImNscy0yIiBkPSJNMTA3LjI0NTgsMGgxNy4yMjI4YTgsOCwwLDAsMSw4LDhWMjMuMjIyOGEwLDAsMCwwLDEsMCwwSDk5LjI0NThhMCwwLDAsMCwxLDAsMFY4YTgsOCwwLDAsMSw4LThaIi8+CiAgICAgICAgPHBhdGggY2xhc3M9ImNscy0yIiBkPSJNMTcyLjQyNDQsMGgxNy4yMjI4YTgsOCwwLDAsMSw4LDhWMjMuMjIyOGEwLDAsMCwwLDEsMCwwSDE2NC40MjQ0YTAsMCwwLDAsMSwwLDBWOGE4LDgsMCwwLDEsOC04WiIvPgogICAgICAgIDxyZWN0IGNsYXNzPSJjbHMtMyIgeD0iMTcuNDU1OCIgeT0iMjMuMjMzOCIgd2lkdGg9IjE2My41OCIgaGVpZ2h0PSIxNjMuNTgiLz4KICAgICAgICA8cGF0aCBjbGFzcz0iY2xzLTMiIGQ9Ik0yNS40NTU4LDBINDIuNjc4NmE4LDgsMCwwLDEsOCw4VjIzLjIyMjhhMCwwLDAsMCwxLDAsMEgxNy40NTU4YTAsMCwwLDAsMSwwLDBWOEE4LDgsMCwwLDEsMjUuNDU1OCwwWiIvPgogICAgICAgIDxwYXRoIGNsYXNzPSJjbHMtMyIgZD0iTTkwLjYzNDQsMGgxNy4yMjI4YTgsOCwwLDAsMSw4LDhWMjMuMjIyOGEwLDAsMCwwLDEsMCwwSDgyLjYzNDRhMCwwLDAsMCwxLDAsMFY4QTgsOCwwLDAsMSw5MC42MzQ0LDBaIi8+CiAgICAgICAgPHBhdGggY2xhc3M9ImNscy0zIiBkPSJNMTU1LjgxMywwaDE3LjIyMjhhOCw4LDAsMCwxLDgsOFYyMy4yMjI4YTAsMCwwLDAsMSwwLDBIMTQ3LjgxM2EwLDAsMCwwLDEsMCwwVjhBOCw4LDAsMCwxLDE1NS44MTMsMFoiLz4KICAgICAgICA8cGF0aCBjbGFzcz0iY2xzLTMiIGQ9Ik0yNS40NTU4LDBINDIuNjc4NmE4LDgsMCwwLDEsOCw4VjIzLjIyMjhhMCwwLDAsMCwxLDAsMEgxNy40NTU4YTAsMCwwLDAsMSwwLDBWOEE4LDgsMCwwLDEsMjUuNDU1OCwwWiIvPgogICAgICAgIDxwYXRoIGNsYXNzPSJjbHMtMyIgZD0iTTkwLjYzNDQsMGgxNy4yMjI4YTgsOCwwLDAsMSw4LDhWMjMuMjIyOGEwLDAsMCwwLDEsMCwwSDgyLjYzNDRhMCwwLDAsMCwxLDAsMFY4QTgsOCwwLDAsMSw5MC42MzQ0LDBaIi8+CiAgICAgICAgPHBhdGggY2xhc3M9ImNscy0zIiBkPSJNMTU1LjgxMywwaDE3LjIyMjhhOCw4LDAsMCwxLDgsOFYyMy4yMjI4YTAsMCwwLDAsMSwwLDBIMTQ3LjgxM2EwLDAsMCwwLDEsMCwwVjhBOCw4LDAsMCwxLDE1NS44MTMsMFoiLz4KICAgICAgICA8cmVjdCBjbGFzcz0iY2xzLTIiIHg9IjM1Ljc1OTYiIHk9IjU2LjgwNjUiIHdpZHRoPSIzMy4yMjI4IiBoZWlnaHQ9IjE1LjYwMzgiLz4KICAgICAgICA8cmVjdCBjbGFzcz0iY2xzLTIiIHg9IjEzMS4yMDE2IiB5PSIxMzYuOTYxNSIgd2lkdGg9IjMzLjIyMjgiIGhlaWdodD0iMTUuNjAzOCIvPgogICAgICAgIDxwYXRoIGNsYXNzPSJjbHMtNCIgZD0iTTU3LjM3MDksNzEuNjAzNmg3MC43NWE5LDksMCwwLDEsOSw5djM1LjM3NDlhNDQuMzc0OCw0NC4zNzQ4LDAsMCwxLTQ0LjM3NDgsNDQuMzc0OGgwYTQ0LjM3NDgsNDQuMzc0OCwwLDAsMS00NC4zNzQ4LTQ0LjM3NDhWODAuNjAzNkE5LDksMCwwLDEsNTcuMzcwOSw3MS42MDM2WiIvPgogICAgICAgIDxwYXRoIGNsYXNzPSJjbHMtNSIgZD0iTTY2LjM3MSw2Ni42NjE3aDcwLjc1YTksOSwwLDAsMSw5LDl2MzUuMzc0OWE0NC4zNzQ4LDQ0LjM3NDgsMCwwLDEtNDQuMzc0OCw0NC4zNzQ4aDBBNDQuMzc0OCw0NC4zNzQ4LDAsMCwxLDU3LjM3MSwxMTEuMDM2NlY3NS42NjE3YTksOSwwLDAsMSw5LTlaIi8+CiAgICAgICAgPHBhdGggaWQ9Il9SZWN0YW5nbGVfIiBkYXRhLW5hbWU9IiZsdDtSZWN0YW5nbGUmZ3Q7IiBjbGFzcz0iY2xzLTYiIGQ9Ik02MS4zNzEsNjYuNjYxN2g3MC43NWE5LDksMCwwLDEsOSw5djM1LjM3NDlhNDQuMzc0OCw0NC4zNzQ4LDAsMCwxLTQ0LjM3NDgsNDQuMzc0OGgwQTQ0LjM3NDgsNDQuMzc0OCwwLDAsMSw1Mi4zNzEsMTExLjAzNjZWNzUuNjYxN0E5LDksMCwwLDEsNjEuMzcxLDY2LjY2MTdaIi8+CiAgICAgICAgPHBhdGggY2xhc3M9ImNscy03IiBkPSJNOTYuNzQ1OSwxNDcuNzQ0MWEzNi43NDg3LDM2Ljc0ODcsMCwwLDEtMzYuNzA3NC0zNi43MDc0Vjc4LjA1ODRhMy43MzMzLDMuNzMzMywwLDAsMSwzLjcyOS0zLjcyOWg2NS45NTYzYTMuNzMzMywzLjczMzMsMCwwLDEsMy43MjksMy43Mjl2MzIuOTc4NEEzNi43NDg2LDM2Ljc0ODYsMCwwLDEsOTYuNzQ1OSwxNDcuNzQ0MVoiLz4KICAgICAgICA8cGF0aCBjbGFzcz0iY2xzLTQiIGQ9Ik0xMDAuNjg5MywxNjMuMzE2N1YxMTEuMDk3M2EzLjk0NDMsMy45NDQzLDAsMCwwLTcuODg4NywwdjUyLjIyYTIyLjUyNTIsMjIuNTI1MiwwLDAsMC0xOC41NDc5LDIyLjE0YzAsLjQ1Ni4wMTc4LjkwNzguMDQ0NywxLjM1NzFIODIuMjFjLS4wNDE0LS40NDc0LS4wNjg4LS44OTktLjA2ODgtMS4zNTcxYTE0LjYyLDE0LjYyLDAsMCwxLDE0LjU5NzQtMTQuNjA0MWwuMDA2MS4wMDA2LjAwNjgtLjAwMDdBMTQuNjIxMSwxNC42MjExLDAsMCwxLDExMS4zNSwxODUuNDU2NmMwLC40NTgxLS4wMjczLjkxLS4wNjg4LDEuMzU3MWg3LjkxMjhjLjAyNjktLjQ0OTMuMDQ0Ny0uOTAxMS4wNDQ3LTEuMzU3MUEyMi41MjU5LDIyLjUyNTksMCwwLDAsMTAwLjY4OTMsMTYzLjMxNjdaIi8+CiAgICAgICAgPHJlY3QgY2xhc3M9ImNscy0yIiB4PSIxNy40NTU4IiB5PSIzNi40NzAyIiB3aWR0aD0iMzMuMjIyOCIgaGVpZ2h0PSIxNS42MDM4Ii8+CiAgICAgICAgPHJlY3QgY2xhc3M9ImNscy0yIiB4PSIxNy40NTU4IiB5PSIxNTguMTIxNyIgd2lkdGg9IjMzLjIyMjgiIGhlaWdodD0iMTUuNjAzOCIvPgogICAgICAgIDxyZWN0IGNsYXNzPSJjbHMtMiIgeD0iMTQ3LjgxMyIgeT0iMzYuNDcwMiIgd2lkdGg9IjMzLjIyMjgiIGhlaWdodD0iMTUuNjAzOCIvPgogICAgICAgIDxyZWN0IGNsYXNzPSJjbHMtMiIgeD0iMTUwLjA2NDMiIHk9IjE1Ny41NTEzIiB3aWR0aD0iMzMuMjIyOCIgaGVpZ2h0PSIxNS42MDM4Ii8+CiAgICAgICAgPHBhdGggaWQ9Il9QYXRoXyIgZGF0YS1uYW1lPSImbHQ7UGF0aCZndDsiIGNsYXNzPSJjbHMtOCIgZD0iTTEwNy41MjU0LDEwMS4wMDI3YTExLjc3OTQsMTEuNzc5NCwwLDEsMC0xOS44Niw4LjU1NDhBNC4wNDE3LDQuMDQxNywwLDAsMSw4OC44NSwxMTMuNjJsLTIuMTA0LDcuMjY4MWEzLDMsMCwwLDAsMi44ODE3LDMuODM0MmgxMi4yMzcxYTMsMywwLDAsMCwyLjg4MTctMy44MzQybC0yLjA5NTktNy4yNGE0LjA3NDMsNC4wNzQzLDAsMCwxLDEuMTgwOC00LjA5NDVBMTEuNzE3MiwxMS43MTcyLDAsMCwwLDEwNy41MjU0LDEwMS4wMDI3WiIvPgogICAgICAgIDxwYXRoIGNsYXNzPSJjbHMtOSIgZD0iTTEwNC43NDYxLDEyMC44ODc3bC0yLjA5NTktNy4yNGE0LjA3NDQsNC4wNzQ0LDAsMCwxLDEuMTgwOC00LjA5NDUsMTEuNzYyOSwxMS43NjI5LDAsMCwwLTUuMDYtMTkuOTMxMywxMS45MSwxMS45MSwwLDAsMC04Ljc5OCwxMC45OTQ5LDExLjcxODUsMTEuNzE4NSwwLDAsMCwzLjY5MjksOC45NDFBNC4wNDE2LDQuMDQxNiwwLDAsMSw5NC44NSwxMTMuNjJsLTMuMjE0LDExLjEwMjNoMTAuMjI4OEEzLDMsMCwwLDAsMTA0Ljc0NjEsMTIwLjg4NzdaIi8+CiAgICAgICAgPHBhdGggY2xhc3M9ImNscy0xMCIgZD0iTTgxLjc5NzUsMTAwLjMxYTMuOTQzOSwzLjk0MzksMCwwLDAtMy45NDQzLTMuOTQ0M0g0MS4wNDE3YTMuOTQ0MywzLjk0NDMsMCwwLDAsMCw3Ljg4ODdINzcuODUzMkEzLjk0MzksMy45NDM5LDAsMCwwLDgxLjc5NzUsMTAwLjMxWiIvPgogICAgICAgIDxwYXRoIGlkPSJfUGF0aF8yIiBkYXRhLW5hbWU9IiZsdDtQYXRoJmd0OyIgY2xhc3M9ImNscy0xMSIgZD0iTTQxLjA0MTYsMTA0LjI1MzlIOTYuODUzMmEzLjk0NDMsMy45NDQzLDAsMCwwLDAtNy44ODg3SDQxLjA0MTZhMy45NDQzLDMuOTQ0MywwLDAsMCwwLDcuODg4N1oiLz4KICAgICAgICA8cGF0aCBjbGFzcz0iY2xzLTEwIiBkPSJNODEuNzk3NSwxMDAuMzFhMy45NDM5LDMuOTQzOSwwLDAsMC0zLjk0NDMtMy45NDQzSDQxLjA0MTdhMy45NDQzLDMuOTQ0MywwLDAsMCwwLDcuODg4N0g3Ny44NTMyQTMuOTQzOSwzLjk0MzksMCwwLDAsODEuNzk3NSwxMDAuMzFaIi8+CiAgICAgICAgPHBhdGggY2xhc3M9ImNscy0xMCIgZD0iTTIyLjQ5MzIsMTIyLjgwMjlBMjIuNDkyOSwyMi40OTI5LDAsMSwxLDQ0Ljk4NTgsMTAwLjMxLDIyLjUxODUsMjIuNTE4NSwwLDAsMSwyMi40OTMyLDEyMi44MDI5Wm0wLTM3LjA5NzJBMTQuNjA0MiwxNC42MDQyLDAsMSwwLDM3LjA5NzIsMTAwLjMxLDE0LjYyMDcsMTQuNjIwNywwLDAsMCwyMi40OTMyLDg1LjcwNTdaIi8+CiAgICAgIDwvZz4KICAgIDwvZz4KICA8L2c+Cjwvc3ZnPgo=" + }, + { + "body": "\nYou can query the 2.0 API for specific objects using a simple language which resembles SQL.\n\nNote that filtering and querying by username has been deprecated, due to privacy changes. \nSee the [announcement](https://developer.atlassian.com/cloud/bitbucket/bitbucket-api-changes-gdpr/#changes-to-querying) \nfor details.\n\n---\n\n* [Supported endpoints](#supported-endpoints)\n* [Operators](#operators)\n* [Data types](#data-types)\n* [Querying](#querying)\n* [Sorting query results](#sorting-query-results)\n\n----\n\n### Supported endpoints\n\nMost 2.0 API resources that return paginated collections of objects support a single, shared, generic querying language that is used to filter down a result set.\n\nThis includes, but is in no way limited to:\n\n /2.0/repositories/{username}\n /2.0/repositories/{username}/{slug}/refs\n /2.0/repositories/{username}/{slug}/refs/branches\n /2.0/repositories/{username}/{slug}/refs/tags\n /2.0/repositories/{username}/{slug}/forks\n /2.0/repositories/{username}/{slug}/src\n /2.0/repositories/{username}/{slug}/issues\n /2.0/repositories/{username}/{slug}/pullrequests\n\nFiltering and sorting supports several distinct operators and data types as well as basic features, like logical operators (AND, OR).\nAs examples, the following queries could be used on the issue tracker endpoint (`/2.0/repositories/{workspace}/{slug}/issues/`):\n\n\t(state = \"open\" OR state = \"new\") AND assignee = null\n\treporter.nickname != \"evzijst\" AND priority >= \"major\"\n\t(title ~ \"unicode\" OR content.raw ~ \"unicode\") AND created_on > 2015-10-04T14:00:00-07:00\n\nFilter queries can be added to the URL using the q= query parameter. To sort the response, add sort=. Note that the entire query string is put in the q parameter and hence needs to be URL-encoded as shown in the following example:\n\n\t/2.0/repositories/foo/bar/issues?q=state=\"new\"&sort=-updated_on\n\n\n### Operators\n\nFiltering and sorting supports the following operators:\n\n| Operator | Definition | Example |\n|----------|--------------------------------|----------------------------|\n| \"=\" | test for equality | `nickname = \"evzijst\"` |\n| \"!=\" | not equal | `is_private != true` |\n| \"~\" | case-insensitive text contains | `description ~ \"beef\"` |\n| \"!~\" | case-insensitive not contains | `description !~ \"fubar\"` |\n| \">\" | greater than | `priority > \"major\"` |\n| \">=\" | greater than or equal | `priority <= \"trivial\"` |\n| \"<\" | less than | `id < 1234` |\n| \"<=\" | less than or equal | `updated_on <= 2015-03-04` |\n\n### Data types\n\nFiltering and sorting supports the following data types:\n\n| Type | Description | Example |\n|--------------|-----------------------------------------|---------------|\n| **String** | any text inside double quotes | `\"foo\"` |\n| **Number** | arbitrary precision integers and floats | `1, -10.302` |\n| **Null** | to test for the absence of a value | `null` |\n| **boolean** | the unquoted strings true or false | `true, false` |\n| **datetime** | an unquoted [ISO-8601][iso-8601] date time string with the timezone offset, milliseconds and entire time component being optional | `2015-03-04T14:08:59.123+02:00`, `2015-03-04T14:08:59` Date time strings are assumed to be in UTC, unless an explicit timezone offset is provided |\n\n[https://en.wikipedia.org/wiki/ISO_8601]: /iso-8601\n\n### Querying\n\nObjects can be filtered based on their properties. In principle, every element in an object's JSON document schema can be used as a filter criterion.\n\nNote that while the array of objects in a paginated response is wrapped in an\nenvelope with a `values` element, this prefix should not be included in the\nquery fields (so use `/2.0/repositories/foo/bar/issues?q=state=\"new\"`, not\n`/2.0/repositories/foo/bar/issues?q=values.state=\"new\"`).\n\n\n### Examples\n\nFields that contain embedded instances of other object types (e.g. owner is an embedded user object, while parent is an embedded repository) can be traversed recursively. For instance:\n\n\tparent.owner.nickname = \"bitbucket\"\n\nTo find pull requests which merge into master, come from a fork of the repo rather than a branch inside the repo, and on which I am a reviewer:\n\n```\nsource.repository.full_name != \"main/repo\" AND state = \"OPEN\" AND reviewers.nickname = \"evzijst\" AND destination.branch.name = \"master\"\n```\n```\n/2.0/repositories/main/repo/pullrequests?q=source.repository.full_name+%21%3D+%22main%2Frepo%22+AND+state+%3D+%22OPEN%22+AND+reviewers.nickname+%3D+%22evzijst%22+AND+destination.branch.name+%3D+%22master%22\n```\n\nTo find new or on-hold issues related to the UI, created or updated in the last day (SF local time), that have not yet been assigned to anyone:\n\n```\n(state = \"new\" OR state = \"on hold\") AND assignee = null AND component = \"UI\" and updated_on > 2015-11-11T00:00:00-07:00\n```\n```\n/2.0/repositories/main/repo/issues?q=%28state+%3D+%22new%22+OR+state+%3D+%22on+hold%22%29+AND+assignee+%3D+null+AND+component+%3D+%22UI%22+and+updated_on+%3E+2015-11-11T00%3A00%3A00-07%3A00\n```\n\nTo find all tags with the string \"2015\" in the name:\n\n```\nname ~ \"2015\"\n```\n```\n/2.0/repositories/{username}/{slug}/refs/tags?q=name+%7E+%222015%22\n```\nOr all my branches:\n\n```\nname ~ \"erik/\"\n```\n```\n/2.0/repositories/{username}/{slug}/refs/?q=name+%7E+%22erik%2F%22\n```\n### Sorting query results\n\nYou can sort result sets using the ?sort= query parameter, available on the same resources that support filtering:\n\n* In principle, every field that can be queried can also be used as a key for sorting.\n* By default the sort order is ascending. To reverse the order, prefix the field name with a hyphen (e.g. ?sort=-updated_on).\n* Only one field can be sorted on. Compound fields (e.g. sort on state first, followed by updated_on) are not supported.\n\n\n", + "title": "Filter and sort API objects", + "anchor": "filtering", + "description": "Query the 2.0 API for specific objects", + "icon": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgMTk0LjE5MTkgMTQ3LjYwOTIiPgogIDxkZWZzPgogICAgPHN0eWxlPgogICAgICAuY2xzLTEgewogICAgICAgIGlzb2xhdGlvbjogaXNvbGF0ZTsKICAgICAgfQoKICAgICAgLmNscy0yIHsKICAgICAgICBmaWxsOiAjY2ZkNGRiOwogICAgICB9CgogICAgICAuY2xzLTMsIC5jbHMtNCB7CiAgICAgICAgZmlsbDogIzg3NzdkOTsKICAgICAgfQoKICAgICAgLmNscy00IHsKICAgICAgICBtaXgtYmxlbmQtbW9kZTogbXVsdGlwbHk7CiAgICAgIH0KCiAgICAgIC5jbHMtNSB7CiAgICAgICAgZmlsbDogIzAwNjVmZjsKICAgICAgfQoKICAgICAgLmNscy02IHsKICAgICAgICBmaWxsOiAjY2NlMGZmOwogICAgICB9CgogICAgICAuY2xzLTcgewogICAgICAgIGZpbGw6IHVybCgjbGluZWFyLWdyYWRpZW50KTsKICAgICAgfQogICAgPC9zdHlsZT4KICAgIDxsaW5lYXJHcmFkaWVudCBpZD0ibGluZWFyLWdyYWRpZW50IiB4MT0iNDE2LjMwODIiIHkxPSI3NS4wNDc5IiB4Mj0iNTg0Ljg1NTYiIHkyPSI3NS4wNDc5IiBncmFkaWVudFRyYW5zZm9ybT0idHJhbnNsYXRlKC00NDMuOTQ2NyAxMjMuMDY4Nikgcm90YXRlKC0xMy43OTc2KSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPgogICAgICA8c3RvcCBvZmZzZXQ9IjAiIHN0b3AtY29sb3I9IiNmZmYiLz4KICAgICAgPHN0b3Agb2Zmc2V0PSIwLjY5MDgiIHN0b3AtY29sb3I9IiNmZmYiIHN0b3Atb3BhY2l0eT0iMC4xIi8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogIDwvZGVmcz4KICA8dGl0bGU+TWFnbmlmeWluZyBHbGFzczwvdGl0bGU+CiAgPGcgY2xhc3M9ImNscy0xIj4KICAgIDxnIGlkPSJMYXllcl8yIiBkYXRhLW5hbWU9IkxheWVyIDIiPgogICAgICA8ZyBpZD0iT2JqZWN0cyI+CiAgICAgICAgPHBhdGggY2xhc3M9ImNscy0yIiBkPSJNMTMxLjExMjUsOTQuOTMwN2wtOS44ODc4LTUuOTg4OC04LjMyOTIsMTMuNzUxOSw5Ljg4NzgsNS45ODg4YTE1LjYwMywxNS42MDMsMCwwLDEsNS44OCw2LjM4MzVoMGExNS42MDMsMTUuNjAzLDAsMCwwLDUuODgsNi4zODM1bDQwLjExNDMsMjQuMjk2NGExMi44NjY0LDEyLjg2NjQsMCwwLDAsMTcuNjcwOS00LjM0aDBhMTIuODY2NCwxMi44NjY0LDAsMCwwLTQuMzQtMTcuNjcwOUwxNDcuODc0OSw5OS40MzkyYTE1LjYwMywxNS42MDMsMCwwLDAtOC4zODEyLTIuMjU0MmgwQTE1LjYwMywxNS42MDMsMCwwLDEsMTMxLjExMjUsOTQuOTMwN1oiLz4KICAgICAgICA8cGF0aCBpZD0iX1BhdGhfIiBkYXRhLW5hbWU9IiZsdDtQYXRoJmd0OyIgY2xhc3M9ImNscy0zIiBkPSJNMTMxLjExMjUsOTQuOTMwN2wtMy4wMTE4LTEuODI0MkE4LjAzODgsOC4wMzg4LDAsMCwwLDExNy4wNiw5NS44MTc4aDBhOC4wMzg4LDguMDM4OCwwLDAsMCwyLjcxMTMsMTEuMDQwNWwzLjAxMTgsMS44MjQyYTE1LjYwMywxNS42MDMsMCwwLDEsNS44OCw2LjM4MzVoMGExNS42MDMsMTUuNjAzLDAsMCwwLDUuODgsNi4zODM1bDQwLjExNDMsMjQuMjk2NGExMi44NjY0LDEyLjg2NjQsMCwwLDAsMTcuNjcwOS00LjM0aDBhMTIuODY2NCwxMi44NjY0LDAsMCwwLTQuMzQtMTcuNjcwOUwxNDcuODc0OSw5OS40MzkyYTE1LjYwMywxNS42MDMsMCwwLDAtOC4zODEyLTIuMjU0M2gwQTE1LjYwMywxNS42MDMsMCwwLDEsMTMxLjExMjUsOTQuOTMwN1oiLz4KICAgICAgICA8cGF0aCBjbGFzcz0iY2xzLTQiIGQ9Ik0xMzkuMTQzNyw5Ny4xNzkyYTE1LjU5NzMsMTUuNTk3MywwLDAsMS04LjAzMTItMi4yNDg1bC0zLjAxMTgtMS44MjQyQTguMDM4OCw4LjAzODgsMCwwLDAsMTE3LjA2LDk1LjgxNzhoMGE4LjAzODgsOC4wMzg4LDAsMCwwLDIuNzExMywxMS4wNDA1bDMuMDExOCwxLjgyNDJhMTUuNTk3LDE1LjU5NywwLDAsMSw1LjcwNjksNi4wNjQ4LDY3Ljg0ODEsNjcuODQ4MSwwLDAsMCwxMC42NTM2LTE3LjU2ODFaIi8+CiAgICAgICAgPHBhdGggY2xhc3M9ImNscy01IiBkPSJNODMuMjUzNywxMzIuNTU2QTY3LjIzNDgsNjcuMjM0OCwwLDAsMSw5LjcxLDMyLjQyOTUsNjYuNzk3NCw2Ni43OTc0LDAsMCwxLDUxLjE4MzcsMS45NjY2bC4wMDA3LDBBNjYuNzk2Miw2Ni43OTYyLDAsMCwxLDEwMi4wNTEsOS43NTI1aDBBNjcuMjM0Niw2Ny4yMzQ2LDAsMCwxLDgzLjI1MzcsMTMyLjU1NloiLz4KICAgICAgICA8cGF0aCBpZD0iX1BhdGhfMiIgZGF0YS1uYW1lPSImbHQ7UGF0aCZndDsiIGNsYXNzPSJjbHMtNiIgZD0iTTIzLjQzOSw0MC43NDgyQTUxLjE5MDgsNTEuMTkwOCwwLDAsMCwxMTEuMDEsOTMuNzg4OSw1MS4xOTA4LDUxLjE5MDgsMCwwLDAsMjMuNDM5LDQwLjc0ODJaIi8+CiAgICAgICAgPHBhdGggY2xhc3M9ImNscy03IiBkPSJNNzkuNDMzLDExNi45ODJBNTEuMjE2Miw1MS4yMTYyLDAsMCwwLDExOC40MjQxLDY3LjAzN2E0OS4xMzkxLDQ5LjEzOTEsMCwwLDEtNS4wODY3LDIuMjc4OWMtMTUuNzAyOSw1Ljk2MDktMjkuNjg5NSwyLjExLTM2LjQ5ODcuMTMwOC0yMC40MzA3LTUuOTM5LTI0Ljc5LTE3LjM3ODUtMzkuMDQxNC0yNC41ODIzYTQ4LjMwOTIsNDguMzA5MiwwLDAsMC0xNC4wOTM5LTQuNTNjLS4wODYyLjEzOTUtLjE3OTMuMjczLS4yNjQ0LjQxMzVBNTEuMTkwNyw1MS4xOTA3LDAsMCwwLDc5LjQzMywxMTYuOTgyWiIvPgogICAgICA8L2c+CiAgICA8L2c+CiAgPC9nPgo8L3N2Zz4K" + }, + { + "body": "\nEndpoints that return collections of objects should always apply pagination.\nPaginated collections are always wrapped in the following wrapper object:\n\n```json\n{\n \"size\": 5421,\n \"page\": 2,\n \"pagelen\": 10,\n \"next\": \"https://api.bitbucket.org/2.0/repositories/pypy/pypy/commits?page=3\",\n \"previous\": \"https://api.bitbucket.org/2.0/repositories/pypy/pypy/commits?page=1\",\n \"values\": [\n ...\n ]\n}\n```\n\nPagination is often page-bound, with a query parameter page indicating which\npage is to be returned.\n\nHowever, clients are not expected to construct URLs themselves by manipulating\nthe page number query parameter. Instead, the response contains a link to the\nnext page. This link should be treated as an opaque location that is not to be\nconstructed by clients or even assumed to be predictable. The only contract\naround the next link is that it will return the next chunk of results.\n\nLack of a next link in the response indicates the end of the collection.\n\nThe paginated response contains the following fields:\n\n| Field | Value |\n|------------|----------|\n| `size` | Total number of objects in the response. This is an optional element that is not provided in all responses, as it can be expensive to compute. |\n| `page` | Page number of the current results. This is an optional element that is not provided in all responses. |\n| `pagelen` | Current number of objects on the existing page. Globally, the minimum length is 10 and the maximum is 100. Some APIs may specify a different default. |\n| `next` | Link to the next page if it exists. The last page of a collection does not have this value. Use this link to navigate the result set and refrain from constructing your own URLs. |\n| `previous` | Link to previous page if it exists. A collections first page does not have this value. This is an optional element that is not provided in all responses. Some result sets strictly support forward navigation and never provide previous links. Clients must anticipate that backwards navigation is not always available. Use this link to navigate the result set and refrain from constructing your own URLs. |\n| `values` | The list of objects. This contains at most `pagelen` objects. |\n\nThe link to the next page is included such that you don't have to hardcode or construct any links. Only values and next are guaranteed (except the last page, which lacks next). This is because the previous and size values can be expensive for some data sets.\n\nIt is important to realize that Bitbucket support both list-based pagination and iterator-based pagination. List-based pagination assumes that the collection is a discrete, immutable, consistently ordered, finite array of objects with a fixed size. Clients navigate a list-based collection by requesting offset-based chunks. In Bitbucket Cloud, list-based responses include the optional size, page, and previous element. The the next and previous links typically resemble something like /foo/bar?page=4.\n\nHowever, not all result sets can be treated as immutable and finite โ€“ much like how programming languages tend to distinguish between lists and arrays on one hand and iterators or stream on the other. Where an list-based pagination offers random access into any point in a collection, iterator-based pagination can only navigate forward one element at a time. In Bitbucket such iterator-based pagination contains the next link and pagelen elements, but not necessarily anything else. In these cases, the next link's value often contains an unpredictable hash instead of an explicit page number. The commits resource uses iterator-based pagination.\n", + "title": "Pagination", + "anchor": "pagination", + "description": "Learn more about pagination", + "icon": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgMjM4LjgyIDE1MS42Ij48ZGVmcz48c3R5bGU+LmNscy0xe2ZpbGw6I2IyZDRmZjt9LmNscy0ye2ZpbGw6IzRjOWFmZjt9LmNscy0ze2ZpbGw6IzAwNTJjYzt9LmNscy00e29wYWNpdHk6MC42O30uY2xzLTV7ZmlsbDp1cmwoI2xpbmVhci1ncmFkaWVudCk7fS5jbHMtNntmaWxsOnVybCgjbGluZWFyLWdyYWRpZW50LTIpO30uY2xzLTd7ZmlsbDp1cmwoI2xpbmVhci1ncmFkaWVudC0zKTt9LmNscy04e2ZpbGw6dXJsKCNsaW5lYXItZ3JhZGllbnQtNCk7fS5jbHMtOXtmaWxsOnVybCgjbGluZWFyLWdyYWRpZW50LTUpO30uY2xzLTEwLC5jbHMtMTEsLmNscy0xMntmaWxsOm5vbmU7fS5jbHMtMTB7c3Ryb2tlOiMzMzg0ZmY7fS5jbHMtMTAsLmNscy0xMSwuY2xzLTEyLC5jbHMtMTN7c3Ryb2tlLW1pdGVybGltaXQ6MTA7c3Ryb2tlLXdpZHRoOjJweDt9LmNscy0xMXtzdHJva2U6I2ZmYWIwMDt9LmNscy0xMntzdHJva2U6I2ZhZmJmYzt9LmNscy0xM3tmaWxsOiNmZmFiMDA7c3Ryb2tlOiMyNjg0ZmY7fTwvc3R5bGU+PGxpbmVhckdyYWRpZW50IGlkPSJsaW5lYXItZ3JhZGllbnQiIHkxPSI2NS4xNyIgeDI9Ijg2LjM4IiB5Mj0iNjUuMTciIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj48c3RvcCBvZmZzZXQ9IjAiIHN0b3AtY29sb3I9IiM0YzlhZmYiLz48c3RvcCBvZmZzZXQ9IjAuMDgiIHN0b3AtY29sb3I9IiM0YzlhZmYiIHN0b3Atb3BhY2l0eT0iMC45NCIvPjxzdG9wIG9mZnNldD0iMC4yNCIgc3RvcC1jb2xvcj0iIzRjOWFmZiIgc3RvcC1vcGFjaXR5PSIwLjc4Ii8+PHN0b3Agb2Zmc2V0PSIwLjQ1IiBzdG9wLWNvbG9yPSIjNGM5YWZmIiBzdG9wLW9wYWNpdHk9IjAuNTMiLz48c3RvcCBvZmZzZXQ9IjAuNTUiIHN0b3AtY29sb3I9IiM0YzlhZmYiIHN0b3Atb3BhY2l0eT0iMC40Ii8+PC9saW5lYXJHcmFkaWVudD48bGluZWFyR3JhZGllbnQgaWQ9ImxpbmVhci1ncmFkaWVudC0yIiB4MT0iMTUyLjQ0IiB5MT0iNjUuMTciIHgyPSIyMzguODIiIHkyPSI2NS4xNyIgeGxpbms6aHJlZj0iI2xpbmVhci1ncmFkaWVudCIvPjxsaW5lYXJHcmFkaWVudCBpZD0ibGluZWFyLWdyYWRpZW50LTMiIHgxPSIxOC44NSIgeTE9IjExOC43OCIgeDI9IjEyNi4wOCIgeTI9IjExLjU2IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwLjA2IiBzdG9wLWNvbG9yPSIjMDA2NWZmIi8+PHN0b3Agb2Zmc2V0PSIwLjE5IiBzdG9wLWNvbG9yPSIjMDA2NWZmIiBzdG9wLW9wYWNpdHk9IjAuOTQiLz48c3RvcCBvZmZzZXQ9IjAuNDYiIHN0b3AtY29sb3I9IiMwMDY1ZmYiIHN0b3Atb3BhY2l0eT0iMC43OCIvPjxzdG9wIG9mZnNldD0iMC44MiIgc3RvcC1jb2xvcj0iIzAwNjVmZiIgc3RvcC1vcGFjaXR5PSIwLjUzIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjMDA2NWZmIiBzdG9wLW9wYWNpdHk9IjAuNCIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IGlkPSJsaW5lYXItZ3JhZGllbnQtNCIgeDE9IjExMi43NSIgeTE9IjExOC43OCIgeDI9IjIxOS45NyIgeTI9IjExLjU2IiB4bGluazpocmVmPSIjbGluZWFyLWdyYWRpZW50LTMiLz48bGluZWFyR3JhZGllbnQgaWQ9ImxpbmVhci1ncmFkaWVudC01IiB4MT0iNTAuOTciIHkxPSIxMzMuNjEiIHgyPSIxODcuODYiIHkyPSItMy4yOCIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPjxzdG9wIG9mZnNldD0iMC42NiIgc3RvcC1jb2xvcj0iIzI1Mzg1OCIvPjxzdG9wIG9mZnNldD0iMC44OCIgc3RvcC1jb2xvcj0iIzI1Mzg1OCIgc3RvcC1vcGFjaXR5PSIwLjgzIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjMjUzODU4IiBzdG9wLW9wYWNpdHk9IjAuNyIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjx0aXRsZT5WaWV3IFZlcnNpb25zPC90aXRsZT48ZyBpZD0iTGF5ZXJfMiIgZGF0YS1uYW1lPSJMYXllciAyIj48ZyBpZD0iU29mdHdhcmUiPjxjaXJjbGUgY2xhc3M9ImNscy0xIiBjeD0iOTQuNTMiIGN5PSIxNDcuOTMiIHI9IjMuNjciLz48Y2lyY2xlIGNsYXNzPSJjbHMtMiIgY3g9IjEwNi45NCIgY3k9IjE0Ny45MyIgcj0iMy42NyIvPjxjaXJjbGUgY2xhc3M9ImNscy0zIiBjeD0iMTE5LjM0IiBjeT0iMTQ3LjkzIiByPSIzLjY3Ii8+PGNpcmNsZSBjbGFzcz0iY2xzLTIiIGN4PSIxMzEuNzUiIGN5PSIxNDcuOTMiIHI9IjMuNjciLz48Y2lyY2xlIGNsYXNzPSJjbHMtMSIgY3g9IjE0NC4xNiIgY3k9IjE0Ny45MyIgcj0iMy42NyIvPjxnIGNsYXNzPSJjbHMtNCI+PHJlY3QgaWQ9Il9SZWN0YW5nbGVfIiBkYXRhLW5hbWU9IiZsdDtSZWN0YW5nbGUmZ3Q7IiBjbGFzcz0iY2xzLTUiIHk9IjI1LjkyIiB3aWR0aD0iODYuMzgiIGhlaWdodD0iNzguNDkiLz48L2c+PGcgY2xhc3M9ImNscy00Ij48cmVjdCBpZD0iX1JlY3RhbmdsZV8yIiBkYXRhLW5hbWU9IiZsdDtSZWN0YW5nbGUmZ3Q7IiBjbGFzcz0iY2xzLTYiIHg9IjE1Mi40NCIgeT0iMjUuOTIiIHdpZHRoPSI4Ni4zOCIgaGVpZ2h0PSI3OC40OSIvPjwvZz48cmVjdCBpZD0iX1JlY3RhbmdsZV8zIiBkYXRhLW5hbWU9IiZsdDtSZWN0YW5nbGUmZ3Q7IiBjbGFzcz0iY2xzLTciIHg9IjE2LjI4IiB5PSIxNC4xMiIgd2lkdGg9IjExMi4zNiIgaGVpZ2h0PSIxMDIuMDkiLz48cmVjdCBpZD0iX1JlY3RhbmdsZV80IiBkYXRhLW5hbWU9IiZsdDtSZWN0YW5nbGUmZ3Q7IiBjbGFzcz0iY2xzLTgiIHg9IjExMC4xOCIgeT0iMTQuMTIiIHdpZHRoPSIxMTIuMzYiIGhlaWdodD0iMTAyLjA5Ii8+PHJlY3QgaWQ9Il9SZWN0YW5nbGVfNSIgZGF0YS1uYW1lPSImbHQ7UmVjdGFuZ2xlJmd0OyIgY2xhc3M9ImNscy05IiB4PSI0Ny42OSIgd2lkdGg9IjE0My40NSIgaGVpZ2h0PSIxMzAuMzQiLz48bGluZSBjbGFzcz0iY2xzLTEwIiB4MT0iNzkuMTYiIHkxPSIxNi4xOCIgeDI9IjExNy4yNCIgeTI9IjE2LjE4Ii8+PGxpbmUgY2xhc3M9ImNscy0xMCIgeDE9IjYyLjkzIiB5MT0iMTYuMTgiIHgyPSI3Mi42IiB5Mj0iMTYuMTgiLz48bGluZSBjbGFzcz0iY2xzLTExIiB4MT0iNzkuMTYiIHkxPSIyNi45NSIgeDI9IjExNy4yNCIgeTI9IjI2Ljk1Ii8+PGxpbmUgY2xhc3M9ImNscy0xMCIgeDE9IjYyLjkzIiB5MT0iMjYuOTUiIHgyPSI3Mi42IiB5Mj0iMjYuOTUiLz48bGluZSBjbGFzcz0iY2xzLTEwIiB4MT0iNzkuMTYiIHkxPSIzNy43MiIgeDI9IjE1MC43IiB5Mj0iMzcuNzIiLz48bGluZSBjbGFzcz0iY2xzLTEwIiB4MT0iNjIuOTMiIHkxPSIzNy43MiIgeDI9IjcyLjYiIHkyPSIzNy43MiIvPjxsaW5lIGNsYXNzPSJjbHMtMTEiIHgxPSIxNTAuNyIgeTE9IjQ4LjQ5IiB4Mj0iMTc1LjU5IiB5Mj0iNDguNDkiLz48bGluZSBjbGFzcz0iY2xzLTEyIiB4MT0iMTEwLjMyIiB5MT0iNDguNDkiIHgyPSIxNDMuMDUiIHkyPSI0OC40OSIvPjxsaW5lIGNsYXNzPSJjbHMtMTEiIHgxPSI3OS4xNiIgeTE9IjQ4LjQ5IiB4Mj0iMTAxLjM3IiB5Mj0iNDguNDkiLz48bGluZSBjbGFzcz0iY2xzLTEwIiB4MT0iNjIuOTMiIHkxPSI0OC40OSIgeDI9IjcyLjYiIHkyPSI0OC40OSIvPjxsaW5lIGNsYXNzPSJjbHMtMTAiIHgxPSI3OS4xNiIgeTE9IjU5LjI2IiB4Mj0iMTUwLjciIHkyPSI1OS4yNiIvPjxsaW5lIGNsYXNzPSJjbHMtMTAiIHgxPSI2Mi45MyIgeTE9IjU5LjI2IiB4Mj0iNzIuNiIgeTI9IjU5LjI2Ii8+PGxpbmUgY2xhc3M9ImNscy0xMCIgeDE9Ijc5LjE2IiB5MT0iNzAuMDMiIHgyPSIxNzUuNTkiIHkyPSI3MC4wMyIvPjxsaW5lIGNsYXNzPSJjbHMtMTAiIHgxPSI2Mi45MyIgeTE9IjcwLjAzIiB4Mj0iNzIuNiIgeTI9IjcwLjAzIi8+PGxpbmUgY2xhc3M9ImNscy0xMSIgeDE9Ijc5LjE2IiB5MT0iODAuNzkiIHgyPSIxMTcuMjQiIHkyPSI4MC43OSIvPjxsaW5lIGNsYXNzPSJjbHMtMTAiIHgxPSI2Mi45MyIgeTE9IjgwLjc5IiB4Mj0iNzIuNiIgeTI9IjgwLjc5Ii8+PGxpbmUgY2xhc3M9ImNscy0xMyIgeDE9Ijc5LjE2IiB5MT0iOTEuNTYiIHgyPSIxNDkuMDYiIHkyPSI5MS41NiIvPjxsaW5lIGNsYXNzPSJjbHMtMTAiIHgxPSI2Mi45MyIgeTE9IjkxLjU2IiB4Mj0iNzIuNiIgeTI9IjkxLjU2Ii8+PGxpbmUgY2xhc3M9ImNscy0xMCIgeDE9IjYyLjkzIiB5MT0iODAuNzkiIHgyPSI3Mi42IiB5Mj0iODAuNzkiLz48bGluZSBjbGFzcz0iY2xzLTEwIiB4MT0iNjIuOTMiIHkxPSI5MS41NiIgeDI9IjcyLjYiIHkyPSI5MS41NiIvPjxsaW5lIGNsYXNzPSJjbHMtMTEiIHgxPSI3OS4xNiIgeTE9IjEwMi4zMyIgeDI9IjExNy4yNCIgeTI9IjEwMi4zMyIvPjxsaW5lIGNsYXNzPSJjbHMtMTAiIHgxPSI2Mi45MyIgeTE9IjEwMi4zMyIgeDI9IjcyLjYiIHkyPSIxMDIuMzMiLz48bGluZSBjbGFzcz0iY2xzLTEwIiB4MT0iMTI1Ljk4IiB5MT0iMTEzLjEiIHgyPSIxNDkuMDYiIHkyPSIxMTMuMSIvPjxsaW5lIGNsYXNzPSJjbHMtMTIiIHgxPSI3OS4xNiIgeTE9IjExMy4xIiB4Mj0iMTE3LjI0IiB5Mj0iMTEzLjEiLz48bGluZSBjbGFzcz0iY2xzLTEwIiB4MT0iNjIuOTMiIHkxPSIxMTMuMSIgeDI9IjcyLjYiIHkyPSIxMTMuMSIvPjwvZz48L2c+PC9zdmc+" + }, + { + "body": "\nBy default, each endpoint returns the full representation of a resource and in\nsome cases that can be a lot of data. For example, retrieving a list of pull\nrequests can amount to quite a large document.\n\nFor better performance, you can ask the server to only return the fields you\nreally need and to omit unwanted data. To request a partial response and to\nadd or remove specific fields from a response, use the `fields` query\nparameter.\n\n\n### Example\n\nMost API resources embed a substantial list of links pointing to related\nresources. This saves the client from constructing its own URLs, but is\nsomewhat wasteful when the client doesn't need them.\n\nTo significantly reduce the size of the response, use `?fields=-links`:\n\n```json\n$ curl https://api.bitbucket.org/2.0/users/evzijst?fields=-links\n{\n \"nickname\": \"evzijst\",\n \"account_status\": \"active\",\n \"website\": \"\",\n \"display_name\": \"Erik van Zijst\",\n \"uuid\": \"{a288a0ab-e13b-43f0-a689-c4ef0a249875}\",\n \"created_on\": \"2010-07-07T05:16:36+00:00\",\n \"location\": null,\n \"type\": \"user\"\n}\n```\n\n### Fields parameter syntax\n\nThe `fields` parameter supports 3 modes of operation:\n\n1. Removal of select fields (e.g. `-links`)\n2. Pulling in additional fields not normally returned by an endpoint, while\n still getting all the default fields (e.g. `+reviewers`)\n3. Omitting all fields, except those specified (e.g. `owner.display_name`)\n\nThe fields parameter can contain a list of multiple comma-separated field names\n(e.g. `fields=owner.display_name,uuid,links.self.href`). The parameter itself is\nnot repeated.\n\nAs discussed at [Condensed Versus Full Objects](serialization#representations),\nmost objects that are embedded inside other objects (like how `owner` is an\nembedded `user` object in `repository`) appear in \"condensed\" form that omits\nmany fields. The `fields` parameter allows us to pull in additional fields in\nsuch cases.\n\nFor example, the embedded repository object in a pull request does not normally\ncontain its `owner`. To add that in we can use:\n`+values.destination.repository.owner`.\n\n\n### Wildcards\n\nThe asterisk can be used to match all fields on a particular level. For\nexample, removing all entries from the `links` element can be done like this:\n\n```json\n$ curl https://api.bitbucket.org/2.0/users/evzijst?fields=-links.*\n{\n \"nickname\": \"evzijst\",\n \"account_status\": \"active\",\n \"website\": \"\",\n \"display_name\": \"Erik van Zijst\",\n \"uuid\": \"{a288a0ab-e13b-43f0-a689-c4ef0a249875}\",\n \"links\": {},\n \"created_on\": \"2010-07-07T05:16:36+00:00\",\n \"location\": null,\n \"type\": \"user\"\n}\n```\n\nWildcards can be used in combination with exclusion and inclusion. For\ninstance, `-*,+foo,+bar` will remove all elements from the root level and then\nadd in `foo` and `bar`.\n\n\n### URL encoding\n\nBe aware that when using the `+foo.bar` syntax in the query string, that the\n\"+\" must be URL encoded as \"%2B\" and so the URL will be:\n\n```\nhttps://api.bitbucket.org/2.0/repositories/evzijst/interruptingcow?fields=%2Bowner.created_on\n```\n\nWithout URL escaping, \"+\" is interpreted as an encoded space which will not\nmatch any fields.\n\n\n### Field discovery\n\nWhile a resource's `self` URL, as well its \"collection\" URL typically return\nthe full object with all its fields, there are some exceptions for fields that\nare overly verbose or costly to generate.\n\nFor instance, a pull request contains the embedded lists of reviewers and\nparticipants. These fields are included from the `self` URL, but not from the\n`/pullrequests` collections resource, as it would impact performance too much.\n\nTo discover any additional fields that might not be included by default,\n`fields=*` can be used.\n\n\n### More examples\n\nIf we want to get a list of all reviewer nicknames on pull requests I created,\nwe could combine a [filter](filtering) with a partial response. This will omit\nall other data from the response:\n\n```\n/2.0/repositories/bitbucket/bitbucket/pullrequests?fields=values.id,values.reviewers.nickname,values.state&q=author.uuid%3D%22%7Bd301aafa-d676-4ee0-88be-962be7417567%7D%22\n{\n \"values\": [\n {\n \"reviewers\": [\n {\n \"nickname\": \"abhin\"\n },\n {\n \"nickname\": \"dtao\"\n },\n {\n \"nickname\": \"csomme\"\n }\n ],\n \"state\": \"OPEN\",\n \"id\": 11355\n },\n {\n \"reviewers\": [\n {\n \"nickname\": \"csomme\"\n },\n {\n \"nickname\": \"abhin\"\n },\n {\n \"nickname\": \"dstevens\"\n }\n ],\n \"state\": \"MERGED\",\n \"id\": 11347\n },\n {\n \"reviewers\": [\n {\n \"nickname\": \"csomme\"\n },\n {\n \"nickname\": \"jmooring\"\n },\n {\n \"nickname\": \"zdavis\"\n },\n {\n \"nickname\": \"flexbox\"\n }\n ],\n \"state\": \"OPEN\",\n \"id\": 11344\n }\n ]\n}\n```\n", + "title": "Partial responses", + "anchor": "partial-response", + "description": "Tweak which fields are returned", + "icon": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgMTYyLjQ0ODcgMjEwLjExMTUiPgogIDxkZWZzPgogICAgPHN0eWxlPgogICAgICAuY2xzLTEgewogICAgICAgIGlzb2xhdGlvbjogaXNvbGF0ZTsKICAgICAgfQoKICAgICAgLmNscy0yLCAuY2xzLTYsIC5jbHMtOCB7CiAgICAgICAgZmlsbDogbm9uZTsKICAgICAgfQoKICAgICAgLmNscy0yLCAuY2xzLTggewogICAgICAgIHN0cm9rZTogIzAwNjVmZjsKICAgICAgICBzdHJva2Utd2lkdGg6IDJweDsKICAgICAgfQoKICAgICAgLmNscy0yIHsKICAgICAgICBzdHJva2UtbGluZWpvaW46IHJvdW5kOwogICAgICB9CgogICAgICAuY2xzLTMgewogICAgICAgIGZpbGw6ICNlN2U4ZWM7CiAgICAgIH0KCiAgICAgIC5jbHMtNCB7CiAgICAgICAgZmlsbDogI2ZmZTM4MDsKICAgICAgfQoKICAgICAgLmNscy01IHsKICAgICAgICBmaWxsOiAjZmZmMGIyOwogICAgICB9CgogICAgICAuY2xzLTYgewogICAgICAgIHN0cm9rZTogI2ZmOTkxZjsKICAgICAgICBzdHJva2Utd2lkdGg6IDEuODE1NnB4OwogICAgICB9CgogICAgICAuY2xzLTYsIC5jbHMtOCB7CiAgICAgICAgc3Ryb2tlLW1pdGVybGltaXQ6IDEwOwogICAgICB9CgogICAgICAuY2xzLTcgewogICAgICAgIG1peC1ibGVuZC1tb2RlOiBtdWx0aXBseTsKICAgICAgICBmaWxsOiB1cmwoI2xpbmVhci1ncmFkaWVudCk7CiAgICAgIH0KCiAgICAgIC5jbHMtOSB7CiAgICAgICAgZmlsbDogI2Y0ZjVmNzsKICAgICAgfQogICAgPC9zdHlsZT4KICAgIDxsaW5lYXJHcmFkaWVudCBpZD0ibGluZWFyLWdyYWRpZW50IiB4MT0iMTEzLjM4MTgiIHkxPSI0OS40MyIgeDI9IjE1My43ODkzIiB5Mj0iOS4wMjI1IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+CiAgICAgIDxzdG9wIG9mZnNldD0iMCIgc3RvcC1jb2xvcj0iI2ZhZmJmYyIvPgogICAgICA8c3RvcCBvZmZzZXQ9IjAuMjc4NiIgc3RvcC1jb2xvcj0iI2VmZjFmMyIvPgogICAgICA8c3RvcCBvZmZzZXQ9IjAuNzY4OCIgc3RvcC1jb2xvcj0iI2QxZDZkZCIvPgogICAgICA8c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNjMWM3ZDAiLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgPC9kZWZzPgogIDx0aXRsZT5Eb2N1bWVudCBUYWJsZTwvdGl0bGU+CiAgPGcgY2xhc3M9ImNscy0xIj4KICAgIDxnIGlkPSJMYXllcl8yIiBkYXRhLW5hbWU9IkxheWVyIDIiPgogICAgICA8ZyBpZD0iT2JqZWN0cyI+CiAgICAgICAgPGxpbmUgY2xhc3M9ImNscy0yIiB4MT0iMTcuNDcxIiB5MT0iMTcxLjc1NzMiIHgyPSI3OS4yOTgiIHkyPSIxNzEuNzU3MyIvPgogICAgICAgIDxwb2x5Z29uIGlkPSJfUGF0aF8iIGRhdGEtbmFtZT0iJmx0O1BhdGgmZ3Q7IiBjbGFzcz0iY2xzLTMiIHBvaW50cz0iMTYyLjQ0NSAzOC43MTEgMTYyLjQ0NSAyMTAuMTExIDAgMjEwLjExMSAwIDAgMTIzLjcwNCAwIDE2Mi40MTUgMzguNzExIDE2Mi40NDUgMzguNzExIi8+CiAgICAgICAgPHJlY3QgY2xhc3M9ImNscy00IiB4PSIxOC45MTE3IiB5PSI3OC4xNTQyIiB3aWR0aD0iNDcuODQ4NSIgaGVpZ2h0PSI3OS42NTM3Ii8+CiAgICAgICAgPHJlY3QgY2xhc3M9ImNscy01IiB4PSIxOC45MTE3IiB5PSI1MS42MDMiIHdpZHRoPSIxMjMuNDUyOSIgaGVpZ2h0PSIyNi41NTEyIi8+CiAgICAgICAgPHJlY3QgY2xhc3M9ImNscy02IiB4PSIxOC45MTE3IiB5PSI1MS42MDMiIHdpZHRoPSIxMjMuNDUyOSIgaGVpZ2h0PSIxMDYuMjA0OSIvPgogICAgICAgIDxsaW5lIGNsYXNzPSJjbHMtNiIgeDE9IjY2Ljc2MDEiIHkxPSI1MS42MDMiIHgyPSI2Ni43NjAxIiB5Mj0iMTU3LjgwNzkiLz4KICAgICAgICA8bGluZSBjbGFzcz0iY2xzLTYiIHgxPSI5MS4zMjg0IiB5MT0iNTEuNjAzIiB4Mj0iOTEuMzI4NCIgeTI9IjE1Ny44MDc5Ii8+CiAgICAgICAgPGxpbmUgY2xhc3M9ImNscy02IiB4MT0iMTE1Ljg5NjciIHkxPSI1MS42MDMiIHgyPSIxMTUuODk2NyIgeTI9IjE1Ny44MDc5Ii8+CiAgICAgICAgPGxpbmUgY2xhc3M9ImNscy02IiB4MT0iMTguOTExNyIgeTE9Ijc4LjE1NDIiIHgyPSIxNDIuMzY0NiIgeTI9Ijc4LjE1NDIiLz4KICAgICAgICA8bGluZSBjbGFzcz0iY2xzLTYiIHgxPSIxOC45MTE3IiB5MT0iMTA0LjcwNTUiIHgyPSIxNDIuMzY0NiIgeTI9IjEwNC43MDU1Ii8+CiAgICAgICAgPGxpbmUgY2xhc3M9ImNscy02IiB4MT0iMTguOTExNyIgeTE9IjEzMS4yNTY3IiB4Mj0iMTQyLjM2NDYiIHkyPSIxMzEuMjU2NyIvPgogICAgICAgIDxwb2x5Z29uIGNsYXNzPSJjbHMtNyIgcG9pbnRzPSIxNjIuNDQ1IDM4LjcxMSAxNjIuNDE1IDM4LjcxMSAxMjMuODcyIDAuMTY5IDEyMy44NzIgNTkuOTIxIDE2Mi40NDUgMzkuMTM3IDE2Mi40NDUgMzguNzExIi8+CiAgICAgICAgPGxpbmUgY2xhc3M9ImNscy04IiB4MT0iMTguMzk3MyIgeTE9IjE4MS4zNDQiIHgyPSI3OS4xMTM1IiB5Mj0iMTgxLjM0NCIvPgogICAgICAgIDxsaW5lIGNsYXNzPSJjbHMtOCIgeDE9IjE4LjM5NzMiIHkxPSIxOTAuOTMwNiIgeDI9IjUxLjMwNDkiIHkyPSIxOTAuOTMwNiIvPgogICAgICAgIDxsaW5lIGNsYXNzPSJjbHMtOCIgeDE9IjE4LjM5NzMiIHkxPSIxNzEuNzU3MyIgeDI9Ijc5LjExMzUiIHkyPSIxNzEuNzU3MyIvPgogICAgICAgIDxsaW5lIGNsYXNzPSJjbHMtOCIgeDE9IjE4LjM5NzMiIHkxPSIzNi4xNzA1IiB4Mj0iNzkuMTEzNSIgeTI9IjM2LjE3MDUiLz4KICAgICAgICA8bGluZSBjbGFzcz0iY2xzLTgiIHgxPSIxOC4zOTczIiB5MT0iMjYuNTgzOCIgeDI9Ijc5LjExMzUiIHkyPSIyNi41ODM4Ii8+CiAgICAgICAgPHBvbHlnb24gY2xhc3M9ImNscy05IiBwb2ludHM9IjE2Mi40NDkgMzguNzQyIDEyMy43MDcgMzguNzQyIDEyMy43MDcgMCAxNjIuNDQ5IDM4Ljc0MiIvPgogICAgICA8L2c+CiAgICA8L2c+CiAgPC9nPgo8L3N2Zz4K" + }, + { + "body": "\n----\n\n* [Open API Specification](#open-api-specification)\n* [JSON Schema](#json-schema)\n* [Condensed Versus Full Objects](#condensed-versus-full-objects)\n\n____\n\n\n### Open API Specification\n\nBitbucket uses the [Open API Specification](https://openapis.org) (OAI,\nformerly known as Swagger) to describe its APIs. Our OAI specification schema\nis hosted at [https://api.bitbucket.org/swagger.json](https://api.bitbucket.org/swagger.json)\nand serves as the canonical definition and comprehensive declaration of all\navailable endpoints.\n\nThe OAI specification makes writing client applications easier by:\nauto-generating boilerplate code (like data object classes) and dealing with\nauthentication and error handling.\n\nYou can find a comprehensive set of open tools for the OAI specification at:\n[https://github.com/swagger-api](https://github.com/swagger-api).\n\n\n### JSON Schema\n\nBitbucket uses JSON Schema to describe the layout of every type of object\nconsumed or produced by the API. These schemas are collected under the\n`#definitions` element of our swagger.json file.\n\nWhen an endpoint expects an object as part of a POST or PUT, it also expects\nthe object to validate against the JSON schemas. The same applies to objects\nreturned by an endpoint.\n\n\n### Condensed Versus Full Objects\n\nMost objects in Bitbucket come both in \"full\" and \"partial\" representation.\nThe full representation is when all elements are included. This is the layout\nreturned by a resource's `self` location (e.g. `/2.0/repositories/foo/bar`),\nas well as resource collection endpoints (e.g. `/2.0/repositories`).\n\nHowever, Bitbucket objects often embed other objects. For example, a `repository`\nobject embeds a `user` object for its owner. Likewise, a `pullrequest` object\nembeds its `repository` object.\n\nThese related objects are embedded, or inlined, to reduce the \"chatter\" when\nclients make frequent followup API calls to collect information on common,\nrelated information.\n\nEmbedded related objects are typically limited in their fields to avoid such\nobject graphs from becoming too deep and noisy. They often exclude their own\nnested objects in an attempt to strike a balance between performance and\nutility.\n\nAn object's embedded or condensed representation tends to be standardized,\nmeaning the fields included is the same set, regardless of where the object\nwas embedded.\n", + "title": "Schemas and Serialization", + "anchor": "serialization", + "description": "Learn more about object representations", + "icon": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyMjAuNzYyNCAyMDUuNTg2Ij4KICA8ZGVmcz4KICAgIDxzdHlsZT4KICAgICAgLmNscy0xIHsKICAgICAgICBpc29sYXRpb246IGlzb2xhdGU7CiAgICAgIH0KCiAgICAgIC5jbHMtMiwgLmNscy02IHsKICAgICAgICBtaXgtYmxlbmQtbW9kZTogbXVsdGlwbHk7CiAgICAgIH0KCiAgICAgIC5jbHMtMTMsIC5jbHMtMywgLmNscy00LCAuY2xzLTYgewogICAgICAgIGZpbGw6IG5vbmU7CiAgICAgICAgc3Ryb2tlOiAjYzFjN2QwOwogICAgICAgIHN0cm9rZS1saW5lY2FwOiByb3VuZDsKICAgICAgICBzdHJva2UtbWl0ZXJsaW1pdDogMTA7CiAgICAgICAgc3Ryb2tlLXdpZHRoOiAycHg7CiAgICAgIH0KCiAgICAgIC5jbHMtNCB7CiAgICAgICAgc3Ryb2tlLWRhc2hhcnJheTogMy43ODE2IDUuMjk0MzsKICAgICAgfQoKICAgICAgLmNscy01IHsKICAgICAgICBmaWxsOiAjMDA2NWZmOwogICAgICB9CgogICAgICAuY2xzLTYgewogICAgICAgIHN0cm9rZS1kYXNoYXJyYXk6IDMuOTIxOSA1LjQ5MDc7CiAgICAgIH0KCiAgICAgIC5jbHMtNyB7CiAgICAgICAgZmlsbDogIzAwNTJjYzsKICAgICAgfQoKICAgICAgLmNscy04IHsKICAgICAgICBmaWxsOiAjNGM5YWZmOwogICAgICB9CgogICAgICAuY2xzLTkgewogICAgICAgIGZpbGw6ICMwMDQ5YjA7CiAgICAgIH0KCiAgICAgIC5jbHMtMTAgewogICAgICAgIGZpbGw6ICM1N2Q5YTM7CiAgICAgIH0KCiAgICAgIC5jbHMtMTEgewogICAgICAgIGZpbGw6ICM3OWYyYzA7CiAgICAgIH0KCiAgICAgIC5jbHMtMTIgewogICAgICAgIGZpbGw6ICMzNmIzN2U7CiAgICAgIH0KCiAgICAgIC5jbHMtMTMgewogICAgICAgIHN0cm9rZS1kYXNoYXJyYXk6IDMuODY4MSA1LjQxNTQ7CiAgICAgIH0KCiAgICAgIC5jbHMtMTQgewogICAgICAgIGZpbGw6ICM0MjUyNmU7CiAgICAgIH0KCiAgICAgIC5jbHMtMTUgewogICAgICAgIGZpbGw6ICMzNDQ1NjM7CiAgICAgIH0KCiAgICAgIC5jbHMtMTYgewogICAgICAgIGZpbGw6ICM1MDVmNzk7CiAgICAgIH0KICAgIDwvc3R5bGU+CiAgPC9kZWZzPgogIDx0aXRsZT5JbnRlZ3JhdGlvbnM8L3RpdGxlPgogIDxnIGNsYXNzPSJjbHMtMSI+CiAgICA8ZyBpZD0iTGF5ZXJfMiIgZGF0YS1uYW1lPSJMYXllciAyIj4KICAgICAgPGcgaWQ9Ik9iamVjdHMiPgogICAgICAgIDxnIGNsYXNzPSJjbHMtMiI+CiAgICAgICAgICA8Zz4KICAgICAgICAgICAgPGxpbmUgY2xhc3M9ImNscy0zIiB4MT0iNzUuMjExNCIgeTE9IjE4Ny44NTczIiB4Mj0iNzcuMDI0OCIgeTI9IjE4Ny4xMTA5Ii8+CiAgICAgICAgICAgIDxsaW5lIGNsYXNzPSJjbHMtNCIgeDE9IjgxLjkyMDUiIHkxPSIxODUuMDk1NiIgeDI9IjEzOC4yMjEyIiB5Mj0iMTYxLjkyMDMiLz4KICAgICAgICAgICAgPGxpbmUgY2xhc3M9ImNscy0zIiB4MT0iMTQwLjY2OSIgeTE9IjE2MC45MTI2IiB4Mj0iMTQyLjQ4MjQiIHkyPSIxNjAuMTY2MiIvPgogICAgICAgICAgPC9nPgogICAgICAgIDwvZz4KICAgICAgICA8cG9seWdvbiBjbGFzcz0iY2xzLTUiIHBvaW50cz0iMTk0LjUyNiAyNi41NTIgMTc2LjkwMSAzOC4yNDEgMTU5LjI3IDI2LjU1MiAxNzYuOTAxIDE0Ljg3IDE5NC41MjYgMjYuNTUyIi8+CiAgICAgICAgPGxpbmUgY2xhc3M9ImNscy02IiB4MT0iMTgzLjcxMzUiIHkxPSI0My4yMTg4IiB4Mj0iMTgzLjcxMzUiIHkyPSI5Ny44ODMyIi8+CiAgICAgICAgPHBvbHlnb24gY2xhc3M9ImNscy03IiBwb2ludHM9IjE3Ni45MDEgMzguMjQxIDE3Ni45MDEgNTguMTY2IDE1OS4yNyA0Ni40NzcgMTU5LjI3IDI2LjU1MiAxNzYuOTAxIDM4LjI0MSIvPgogICAgICAgIDxwb2x5Z29uIGNsYXNzPSJjbHMtOCIgcG9pbnRzPSIxOTQuNTI2IDI2LjU1MiAxOTQuNTI2IDQ2LjQ3NyAxNzYuOTAxIDU4LjE2NiAxNzYuOTAxIDM4LjI0MSAxOTQuNTI2IDI2LjU1MiIvPgogICAgICAgIDxsaW5lIGNsYXNzPSJjbHMtNiIgeDE9IjQ3Ljk0ODgiIHkxPSI0Mi4yMTg4IiB4Mj0iMTU5LjExNzIiIHkyPSI0Mi4yMTg4Ii8+CiAgICAgICAgPHBvbHlnb24gY2xhc3M9ImNscy01IiBwb2ludHM9IjIyMC43NjIgOTkuNzUyIDE2Ny44MTcgMTM0Ljg2NCAxMTQuODU0IDk5Ljc1MiAxNjcuODE3IDY0LjY1NyAyMjAuNzYyIDk5Ljc1MiIvPgogICAgICAgIDxwb2x5Z29uIGNsYXNzPSJjbHMtOSIgcG9pbnRzPSIxNjcuODE3IDEzNC44NjQgMTY3LjgxNyAxOTQuNzE4IDExNC44NTQgMTU5LjYwNiAxMTQuODU0IDk5Ljc1MiAxNjcuODE3IDEzNC44NjQiLz4KICAgICAgICA8cG9seWdvbiBjbGFzcz0iY2xzLTgiIHBvaW50cz0iMjIwLjc2MiA5OS43NTIgMjIwLjc2MiAxNTkuNjA2IDE2Ny44MTcgMTk0LjcxOCAxNjcuODE3IDEzNC44NjQgMjIwLjc2MiA5OS43NTIiLz4KICAgICAgICA8cG9seWdvbiBjbGFzcz0iY2xzLTEwIiBwb2ludHM9IjExMC41NDEgMjEuNjA0IDc3Ljk0OSA0My4yMTkgNDUuMzQ1IDIxLjYwNCA3Ny45NDkgMCAxMTAuNTQxIDIxLjYwNCIvPgogICAgICAgIDxwb2x5Z29uIGNsYXNzPSJjbHMtMTEiIHBvaW50cz0iMTEwLjU0MSAyMS42MDQgMTEwLjU0MSA1OC40NDkgNzcuOTQ5IDgwLjA2NCA3Ny45NDkgNDMuMjE5IDExMC41NDEgMjEuNjA0Ii8+CiAgICAgICAgPHBvbHlnb24gY2xhc3M9ImNscy01IiBwb2ludHM9IjE0MS4xOSAxNDguMDczIDE2Ny44MTMgMTMwLjQxNyAxOTQuNDQ0IDE0OC4wNzMgMTY3LjgxMyAxNjUuNzE5IDE0MS4xOSAxNDguMDczIi8+CiAgICAgICAgPHBvbHlnb24gY2xhc3M9ImNscy05IiBwb2ludHM9IjE2Ny44MTMgMTMwLjQxNyAxNjcuODEzIDEwMC4zMjEgMTk0LjQ0NCAxMTcuOTc2IDE5NC40NDQgMTQ4LjA3MyAxNjcuODEzIDEzMC40MTciLz4KICAgICAgICA8cG9seWdvbiBjbGFzcz0iY2xzLTgiIHBvaW50cz0iMTQxLjE5IDE0OC4wNzMgMTQxLjE5IDExNy45NzYgMTY3LjgxMyAxMDAuMzIxIDE2Ny44MTMgMTMwLjQxNyAxNDEuMTkgMTQ4LjA3MyIvPgogICAgICAgIDxwb2x5Z29uIGNsYXNzPSJjbHMtMTIiIHBvaW50cz0iNDUuMzQ1IDIxLjYwNCA0NS4zNDUgNDQuOTg0IDU3LjIzMSA1Mi44NjQgNTcuMjMxIDY2LjI5NiA3Ny45NDkgODAuMDY0IDc3Ljk0OSA0My4yMTkgNDUuMzQ1IDIxLjYwNCIvPgogICAgICAgIDxnIGNsYXNzPSJjbHMtMiI+CiAgICAgICAgICA8Zz4KICAgICAgICAgICAgPGxpbmUgY2xhc3M9ImNscy0zIiB4MT0iMjQuNjQzOCIgeTE9Ijg2Ljk1NDQiIHgyPSIyNi4wMTU3IiB5Mj0iODUuNTUzMSIvPgogICAgICAgICAgICA8bGluZSBjbGFzcz0iY2xzLTEzIiB4MT0iMjkuODA0IiB5MT0iODEuNjgzNCIgeDI9IjYwLjM4MTEiIHkyPSI1MC40NDkyIi8+CiAgICAgICAgICAgIDxsaW5lIGNsYXNzPSJjbHMtMyIgeDE9IjYyLjI3NTIiIHkxPSI0OC41MTQzIiB4Mj0iNjMuNjQ3IiB5Mj0iNDcuMTEzIi8+CiAgICAgICAgICA8L2c+CiAgICAgICAgPC9nPgogICAgICAgIDxwb2x5Z29uIGNsYXNzPSJjbHMtNSIgcG9pbnRzPSIzNS4yNTUgODkuNjQ1IDE3LjczNiAxMDEuNDkyIDAgODkuOTYyIDE3LjUyNSA3OC4xMjEgMzUuMjU1IDg5LjY0NSIvPgogICAgICAgIDxwb2x5Z29uIGNsYXNzPSJjbHMtNyIgcG9pbnRzPSIxNy43MzYgMTAxLjQ5MiAxNy45MTUgMTIxLjQxNiAwLjE3OSAxMDkuODg3IDAgODkuOTYyIDE3LjczNiAxMDEuNDkyIi8+CiAgICAgICAgPGxpbmUgY2xhc3M9ImNscy02IiB4MT0iMjAuNTg0OSIgeTE9IjEwNS41MzA1IiB4Mj0iNjUuODc0OSIgeTI9IjE3MS4zNjgxIi8+CiAgICAgICAgPHBvbHlnb24gY2xhc3M9ImNscy04IiBwb2ludHM9IjM1LjI1NSA4OS42NDUgMzUuNDM0IDEwOS41NjkgMTcuOTE1IDEyMS40MTYgMTcuNzM2IDEwMS40OTIgMzUuMjU1IDg5LjY0NSIvPgogICAgICAgIDxwb2x5Z29uIGNsYXNzPSJjbHMtMTQiIHBvaW50cz0iOTIuMzk0IDE3My44MTUgNzQuODc1IDE4NS42NjIgNTcuMTM5IDE3NC4xMzIgNzQuNjY0IDE2Mi4yOTEgOTIuMzk0IDE3My44MTUiLz4KICAgICAgICA8cG9seWdvbiBjbGFzcz0iY2xzLTE1IiBwb2ludHM9Ijc0Ljg3NSAxODUuNjYyIDc1LjA1NCAyMDUuNTg2IDU3LjMxOSAxOTQuMDU3IDU3LjEzOSAxNzQuMTMyIDc0Ljg3NSAxODUuNjYyIi8+CiAgICAgICAgPHBvbHlnb24gY2xhc3M9ImNscy0xNiIgcG9pbnRzPSI5Mi4zOTQgMTczLjgxNSA5Mi41NzQgMTkzLjczOSA3NS4wNTQgMjA1LjU4NiA3NC44NzUgMTg1LjY2MiA5Mi4zOTQgMTczLjgxNSIvPgogICAgICA8L2c+CiAgICA8L2c+CiAgPC9nPgo8L3N2Zz4K" + }, + { + "body": "\nYou should be familiar with REST architecture before writing an integration. Read this overview page to gain a good understanding of Bitbucket's REST implementation.\n\n----\n\n* [URI structure](#uri-structure)\n* [HTTP methods](#http-methods)\n* [UUID](#universally-unique-identifier)\n * [User object and UUID](#user-object-and-uuid)\n * [Repository object and UUID](#repository-object-and-uuid)\n * [Team object and UUID](#team-object-and-uuid)\n* [Standard error responses](#standardized-error-responses)\n* [Standard ISO-8601 timestamps](#standard-iso-8601-timestamps)\n\n----\n\n\n### URI structure\n\nAll Bitbucket Cloud requests start with the `https://api.bitbucket.org/2.0` prefix (for the 2.0 API) and `https://api.bitbucket.org/1.0` prefix (1.0 API).\n\nThe next segment of the URI path depends on the endpoint of the request. For example, using the curl command and the repositories endpoint you can list all the issues on Bitbucket's tutorial repository:\n\n```\ncurl https://api.bitbucket.org/2.0/repositories/tutorials/tutorials.bitbucket.org\n```\nGiven a specific endpoint, you can then drill down to a particular aspect or resource of that endpoint. The issues resource on a repository is an example:\n\n```\ncurl https://api.bitbucket.org/1.0/repositories/tutorials/tutorials.bitbucket.org/issues\n```\n\n#### HTTP methods\n\nA given endpoint or resource has a series of actions (or methods) associated with it. The Bitbucket service supports these standard HTTP methods:\n\n| Call | Description |\n|------|-------------|\n| GET | Retrieves information. |\n| PUT | Updates existing information. |\n| POST | Creates new information. |\n| DELETE | Removes existing information. |\n\nFor example, you can call use the POST action on the issues resource and create an issue on the issue tracker.\n\n**Specifying content length**\n\nYou can get a `411 Length Required` response. If this happens, the API requires a Content-Length header but the client is not sending it. You should add the header yourself, for example using the curl client:\n\n```\ncurl -r PUT --header \"Content-Length: 0\" -u user:app_password https://api.bitbucket.org/1.0/emails/rap@atlassian.com\n```\n\n### Universally Unique Identifier\n\nUUID's provide a single point of recognition for users, teams, and repositories. The UUID is distinct from the username, team name, and repository name fields and remains the same even when those fields change. For example when a user changes their username or moves a repository you will need to modify calls which use those identifiers but not if you are pointing to the UUID.\n\n#### UUID examples and structure\n\nUUID's work with both the 1.0 and 2.0 APIs for the user, team, and repository objects. The following examples the following characters are replacements for curly brackets: `%7B` replaces `{` and `%7D` replaces `}`. You will see this structure in the following example sections.\n\n#### User object and UUID\n\nWhen you make a call using either the username or the UUID for that user the response is the same.\n\n**Call with username**:\n\n```\ncurl https://api.bitbucket.org/2.0/users/tutorials\n```\n\n***Call with UUID for the user**:\n\n```\ncurl https://api.bitbucket.org/2.0/users/%7Bc788b2da-b7a2-404c-9e26-d3f077557007%7D\n```\n\n**Response**\n```JSON\n{\n \"username\": \"tutorials\",\n \"nickname\": \"tutorials\",\n \"account_status\": \"active\",\n \"website\": \"https://tutorials.bitbucket.org/\",\n \"display_name\": \"tutorials account\",\n \"uuid\": \"{c788b2da-b7a2-404c-9e26-d3f077557007}\",\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/users/tutorials\"\n },\n \"repositories\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/tutorials\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/tutorials\"\n },\n \"followers\": {\n \"href\": \"https://api.bitbucket.org/2.0/users/tutorials/followers\"\n },\n \"avatar\": {\n \"href\": \"https://bitbucket-assetroot.s3.amazonaws.com/c/photos/2013/Nov/25/tutorials-avatar-1563784409-6_avatar.png\"\n },\n \"following\": {\n \"href\": \"https://api.bitbucket.org/2.0/users/tutorials/following\"\n }\n },\n \"created_on\": \"2011-12-20T16:34:07.132459+00:00\",\n \"location\": \"Santa Monica, CA\",\n \"type\": \"user\"\n}\n```\n\n#### Repository object and UUID\n\nOnce you have the UUID for a repository you no longer need a username or team name to make the API call so long as you use an empty field. This helps you resolve repositories no matter if the username or team name changes.\n\n**Call with team name (1team) and repository name (moxie)**:\n\n```\ncurl https://api.bitbucket.org/2.0/repositories/1team/moxie\n```\n**Call with UUID and empty field**:\n\n```\ncurl https://api.bitbucket.org/2.0/repositories/%7B%7D/%7B21fa9bf8-b5b2-4891-97ed-d590bad0f871%7D\n```\n\n**Call with UUID and teamname**:\n\n```\ncurl https://api.bitbucket.org/2.0/repositories/1team/%7B21fa9bf8-b5b2-4891-97ed-d590bad0f871%7D\n```\n\n**Response**\n\n```JSON\n{\n \"created_on\": \"2013-11-08T01:11:03.222520+00:00\",\n \"description\": \"\",\n \"fork_policy\": \"allow_forks\",\n \"full_name\": \"1team/moxie\",\n \"has_issues\": false,\n \"has_wiki\": false,\n \"is_private\": false,\n \"language\": \"\",\n \"links\": {\n \"avatar\": {\n \"href\": \"https://bitbucket.org/1team/moxie/avatar/32/\"\n },\n \"branches\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/1team/moxie/refs/branches\"\n },\n \"clone\": [\n {\n \"href\": \"https://bitbucket.org/1team/moxie.git\",\n \"name\": \"https\"\n },\n {\n \"href\": \"ssh://git@bitbucket.org/1team/moxie.git\",\n \"name\": \"ssh\"\n }\n ],\n \"commits\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/1team/moxie/commits\"\n },\n \"downloads\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/1team/moxie/downloads\"\n },\n \"forks\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/1team/moxie/forks\"\n },\n \"hooks\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/1team/moxie/hooks\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/1team/moxie\"\n },\n \"pullrequests\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/1team/moxie/pullrequests\"\n },\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/1team/moxie\"\n },\n \"tags\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/1team/moxie/refs/tags\"\n },\n \"watchers\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/1team/moxie/watchers\"\n }\n },\n \"name\": \"moxie\",\n \"owner\": {\n \"display_name\": \"the team\",\n \"links\": {\n \"avatar\": {\n \"href\": \"https://bitbucket.org/account/1team/avatar/32/\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/1team/\"\n },\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/teams/1team\"\n }\n },\n \"type\": \"team\",\n \"username\": \"1team\",\n \"uuid\": \"{aa559944-83c9-4963-a9a8-69ac8d9cf5d2}\"\n },\n \"project\": {\n \"key\": \"PROJ\",\n \"links\": {\n \"avatar\": {\n \"href\": \"https://bitbucket.org/account/user/1team/projects/PROJ/avatar/32\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/account/user/1team/projects/PROJ\"\n }\n },\n \"name\": \"Untitled project\",\n \"type\": \"project\",\n \"uuid\": \"{ab52aaeb-16ad-4fb0-bb1d-47e4f00367ff}\"\n },\n \"scm\": \"git\",\n \"size\": 33348,\n \"type\": \"repository\",\n \"updated_on\": \"2013-11-08T01:11:03.263237+00:00\",\n \"uuid\": \"{21fa9bf8-b5b2-4891-97ed-d590bad0f871}\",\n \"website\": \"\"\n}\n```\n\n#### Team object and UUID\n\nThis example shows a call for a list of team members using both the team name and with the UUID for the team object. As the call is unauthenticated in the following example the response object will only show members with public profiles. The response is the same in either case.\n\n**Call with teamname**\n\n```\ncurl https://api.bitbucket.org/2.0/teams/1team/members\n```\n**Call with UUID for team object**\n\n```\ncurl https://api.bitbucket.org/2.0/teams/%7Baa559944-83c9-4963-a9a8-69ac8d9cf5d2%7D/members\n```\n\n**Response**\n\n```JSON\n{\n \"page\": 1,\n \"pagelen\": 50,\n \"size\": 2,\n \"values\": [\n {\n \"created_on\": \"2011-12-20T16:34:07.132459+00:00\",\n \"display_name\": \"tutorials account\",\n \"links\": {\n \"avatar\": {\n \"href\": \"https://bitbucket.org/account/tutorials/avatar/32/\"\n },\n \"followers\": {\n \"href\": \"https://api.bitbucket.org/2.0/users/tutorials/followers\"\n },\n \"following\": {\n \"href\": \"https://api.bitbucket.org/2.0/users/tutorials/following\"\n },\n \"hooks\": {\n \"href\": \"https://api.bitbucket.org/2.0/users/tutorials/hooks\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/tutorials/\"\n },\n \"repositories\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/tutorials\"\n },\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/users/tutorials\"\n },\n \"snippets\": {\n \"href\": \"https://api.bitbucket.org/2.0/snippets/tutorials\"\n }\n },\n \"location\": null,\n \"type\": \"user\",\n \"username\": \"tutorials\",\n \"nickname\": \"tutorials\",\n \"account_status\": \"active\",\n \"uuid\": \"{c788b2da-b7a2-404c-9e26-d3f077557007}\",\n \"website\": \"https://tutorials.bitbucket.org/\"\n },\n {\n \"created_on\": \"2013-12-10T14:44:13+00:00\",\n \"display_name\": \"Dan Stevens [Atlassian]\",\n \"links\": {\n \"avatar\": {\n \"href\": \"https://bitbucket.org/account/dans9190/avatar/32/\"\n },\n \"followers\": {\n \"href\": \"https://api.bitbucket.org/2.0/users/dans9190/followers\"\n },\n \"following\": {\n \"href\": \"https://api.bitbucket.org/2.0/users/dans9190/following\"\n },\n \"hooks\": {\n \"href\": \"https://api.bitbucket.org/2.0/users/dans9190/hooks\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/dans9190/\"\n },\n \"repositories\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/dans9190\"\n },\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/users/dans9190\"\n },\n \"snippets\": {\n \"href\": \"https://api.bitbucket.org/2.0/snippets/dans9190\"\n }\n },\n \"location\": null,\n \"type\": \"user\",\n \"username\": \"dans9190\",\n \"nickname\": \"dans9190\",\n \"account_status\": \"active\",\n \"uuid\": \"{1cd06601-cd0e-4fce-be03-e9ac226978b7}\",\n \"website\": \"\"\n }\n ]\n}\n```\n\n### Standardized error responses\n\nThe 2.0 API standardizes the error response layout. The 2.0 API serves a JSON\nobject along with the appropriate HTTP status code. The JSON object provides a\ndetailed problem description.\n\n```json\n{\n \"type\": \"error\",\n \"error\": {\n \"message\": \"Bad request\",\n \"fields\": {\n \"src\": [\n \"This field is required.\"\n ]\n },\n \"detail\": \"You must specify a valid source branch when creating a pull request.\",\n \"id\": \"d23a1cc5178f7637f3d9bf2d13824258\",\n \"data\": {\n \"extra\": \"Optional, endpoint-specific data to further augment the error.\"\n }\n }\n}\n```\n\nThis object contains an error element which contains the following nested\nelements:\n\n| Element | Description |\n|---------|-------------|\n| message | A short description of the problem. This element is always present. Its value may be localized. |\n| fields | This optional element is used in response to POST or PUT operations in which clients have provided invalid input. It contains a list of one or more client-provided fields that failed validation. The values may be localized. |\n| detail | An optional detailed explanation of the failure. Its value may be localized.\n| id | An optional unique error identifier that identifies the error in Bitbucket's logging system. If you feel you hit a bug in an API and this field is provided, please mention it if you decide to contact support as it will greatly help us narrow down the problem. |\n\n### Standard ISO-8601 timestamps\n\nAll 2.0 APIs use standardized ISO-8601 timestamps. In most cases, our APIs return UTC timestamps and for these, the timezone offset part will be 00:00. In rare cases where the original localized timestamp has significance, the timezone offset may identify the event's original timezone.\n", + "title": "URI, UUID, and structures", + "anchor": "uri-uuid", + "description": "URL's, UUID's, errors, and timestamps", + "icon": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgMTc5LjI2IDE3Ny42NSI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOnVybCgjbGluZWFyLWdyYWRpZW50KTt9LmNscy0ye2ZpbGw6IzA5MWU0Mjt9LmNscy0xMCwuY2xzLTExLC5jbHMtMywuY2xzLTQsLmNscy05e2ZpbGw6bm9uZTt9LmNscy0ze3N0cm9rZTojOTljMWZmO30uY2xzLTMsLmNscy00e3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2Utd2lkdGg6MDt9LmNscy0xMiwuY2xzLTQsLmNscy05e3N0cm9rZTojZTVlOGVjO30uY2xzLTV7ZmlsbDojMzQ0NTYzO30uY2xzLTZ7ZmlsbDojZmY4YjAwO30uY2xzLTd7ZmlsbDojZmZjNDAwO30uY2xzLTh7ZmlsbDojMDA2NWZmO30uY2xzLTEwLC5jbHMtMTEsLmNscy0xMiwuY2xzLTEzLC5jbHMtOXtzdHJva2UtbWl0ZXJsaW1pdDoxMDtzdHJva2Utd2lkdGg6MnB4O30uY2xzLTEwe3N0cm9rZTojZmZhYjAwO30uY2xzLTExLC5jbHMtMTN7c3Ryb2tlOiMwMDY1ZmY7fS5jbHMtMTJ7ZmlsbDojOTljMWZmO30uY2xzLTEze2ZpbGw6I2U1ZThlYzt9PC9zdHlsZT48bGluZWFyR3JhZGllbnQgaWQ9ImxpbmVhci1ncmFkaWVudCIgeDE9IjAuNCIgeTE9IjE3OC4wNSIgeDI9IjE3OC44NSIgeTI9Ii0wLjQiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj48c3RvcCBvZmZzZXQ9IjAiIHN0b3AtY29sb3I9IiMwOTFlNDIiLz48c3RvcCBvZmZzZXQ9IjAuMDciIHN0b3AtY29sb3I9IiMwZDIyNDUiLz48c3RvcCBvZmZzZXQ9IjAuNDkiIHN0b3AtY29sb3I9IiMxZjMyNTMiLz48c3RvcCBvZmZzZXQ9IjAuNzkiIHN0b3AtY29sb3I9IiMyNTM4NTgiLz48L2xpbmVhckdyYWRpZW50PjwvZGVmcz48dGl0bGU+Q29kZTwvdGl0bGU+PGcgaWQ9IkxheWVyXzIiIGRhdGEtbmFtZT0iTGF5ZXIgMiI+PGcgaWQ9IlNvZnR3YXJlIj48cmVjdCBpZD0iX1JlY3RhbmdsZV8iIGRhdGEtbmFtZT0iJmx0O1JlY3RhbmdsZSZndDsiIGNsYXNzPSJjbHMtMSIgd2lkdGg9IjE3OS4yNiIgaGVpZ2h0PSIxNzcuNjUiLz48cGF0aCBjbGFzcz0iY2xzLTIiIGQ9Ik0xNzkuMjYsMjYuNjRIMFY3MS43NUExNjYuNDEsMTY2LjQxLDAsMCwwLDYzLjI0LDU5LjUxYTE4OC40MSwxODguNDEsMCwwLDAsMTcuMzktOC4zNmMxOC40NC05LjQzLDQ4LjM3LTE3LjksOTguNjItMTNabS0xNTkuNDQsMzRoMFptMC0xNC4wOGgwWiIvPjxsaW5lIGNsYXNzPSJjbHMtMyIgeDE9IjE5LjgxIiB5MT0iNDYuNTgiIHgyPSIyNS4wNyIgeTI9IjQ2LjU4Ii8+PGxpbmUgY2xhc3M9ImNscy0zIiB4MT0iMjUuMDciIHkxPSI2MC42NiIgeDI9IjE5LjgxIiB5Mj0iNjAuNjYiLz48bGluZSBjbGFzcz0iY2xzLTMiIHgxPSIxOS44MSIgeTE9Ijc0Ljc0IiB4Mj0iMjUuMDciIHkyPSI3NC43NCIvPjxsaW5lIGNsYXNzPSJjbHMtMyIgeDE9IjI1LjA3IiB5MT0iODguODIiIHgyPSIxOS44MSIgeTI9Ijg4LjgyIi8+PGxpbmUgY2xhc3M9ImNscy0zIiB4MT0iMjUuMDciIHkxPSIxMDIuODkiIHgyPSIxOS44MSIgeTI9IjEwMi44OSIvPjxsaW5lIGNsYXNzPSJjbHMtMyIgeDE9IjI1LjA3IiB5MT0iMTE2Ljk3IiB4Mj0iMTkuODEiIHkyPSIxMTYuOTciLz48bGluZSBjbGFzcz0iY2xzLTMiIHgxPSIyNS4wNyIgeTE9IjEzMS4wNSIgeDI9IjE5LjgxIiB5Mj0iMTMxLjA1Ii8+PGxpbmUgY2xhc3M9ImNscy0zIiB4MT0iMjUuMDciIHkxPSIxNDUuMTMiIHgyPSIxOS44MSIgeTI9IjE0NS4xMyIvPjxsaW5lIGNsYXNzPSJjbHMtMyIgeDE9IjI1LjA3IiB5MT0iMTU5LjIxIiB4Mj0iMTkuODEiIHkyPSIxNTkuMjEiLz48bGluZSBjbGFzcz0iY2xzLTQiIHgxPSI1NS44OSIgeTE9IjEzMS4wNSIgeDI9IjkzLjk5IiB5Mj0iMTMxLjA1Ii8+PGxpbmUgY2xhc3M9ImNscy00IiB4MT0iNTUuODkiIHkxPSIxNDUuMTMiIHgyPSIxMzkuNjciIHkyPSIxNDUuMTMiLz48cmVjdCBjbGFzcz0iY2xzLTUiIHdpZHRoPSIxNzkuMjYiIGhlaWdodD0iMjYuNjQiLz48Y2lyY2xlIGNsYXNzPSJjbHMtNiIgY3g9IjEzLjUiIGN5PSIxMi4wOCIgcj0iNS4xMSIvPjxjaXJjbGUgY2xhc3M9ImNscy03IiBjeD0iMzAuMTgiIGN5PSIxMi4wOCIgcj0iNS4xMSIvPjxjaXJjbGUgY2xhc3M9ImNscy04IiBjeD0iNDYuODYiIGN5PSIxMi4wOCIgcj0iNS4xMSIvPjxwYXRoIGNsYXNzPSJjbHMtOSIgZD0iTTc1LjQxLDg4LjgyIi8+PHBhdGggY2xhc3M9ImNscy05IiBkPSJNMzIuODksODguODIiLz48bGluZSBjbGFzcz0iY2xzLTEwIiB4MT0iMzIuODkiIHkxPSI3NC43NCIgeDI9Ijc1LjQxIiB5Mj0iNzQuNzQiLz48bGluZSBjbGFzcz0iY2xzLTExIiB4MT0iMzIuODkiIHkxPSI2MC42NiIgeDI9Ijc1LjQxIiB5Mj0iNjAuNjYiLz48bGluZSBjbGFzcz0iY2xzLTkiIHgxPSIzMi44OSIgeTE9IjQ2LjU4IiB4Mj0iNTUuODkiIHkyPSI0Ni41OCIvPjxsaW5lIGNsYXNzPSJjbHMtMTIiIHgxPSIxOS44MSIgeTE9IjQ2LjU4IiB4Mj0iMjUuMDciIHkyPSI0Ni41OCIvPjxsaW5lIGNsYXNzPSJjbHMtMTIiIHgxPSIxOS44MSIgeTE9IjYwLjY2IiB4Mj0iMjUuMDciIHkyPSI2MC42NiIvPjxsaW5lIGNsYXNzPSJjbHMtMTIiIHgxPSIxOS44MSIgeTE9Ijc0Ljc0IiB4Mj0iMjUuMDciIHkyPSI3NC43NCIvPjxsaW5lIGNsYXNzPSJjbHMtMTIiIHgxPSIxOS44MSIgeTE9Ijg4LjgyIiB4Mj0iMjUuMDciIHkyPSI4OC44MiIvPjxsaW5lIGNsYXNzPSJjbHMtMTIiIHgxPSIxOS44MSIgeTE9IjEwMi44OSIgeDI9IjI1LjA3IiB5Mj0iMTAyLjg5Ii8+PGxpbmUgY2xhc3M9ImNscy0xMiIgeDE9IjE5LjgxIiB5MT0iMTE2Ljk3IiB4Mj0iMjUuMDciIHkyPSIxMTYuOTciLz48bGluZSBjbGFzcz0iY2xzLTEyIiB4MT0iMTkuODEiIHkxPSIxMzEuMDUiIHgyPSIyNS4wNyIgeTI9IjEzMS4wNSIvPjxsaW5lIGNsYXNzPSJjbHMtMTIiIHgxPSIxOS44MSIgeTE9IjE0NS4xMyIgeDI9IjI1LjA3IiB5Mj0iMTQ1LjEzIi8+PGxpbmUgY2xhc3M9ImNscy0xMiIgeDE9IjE5LjgxIiB5MT0iMTU5LjIxIiB4Mj0iMjUuMDciIHkyPSIxNTkuMjEiLz48bGluZSBpZD0iX0xpbmVfIiBkYXRhLW5hbWU9IiZsdDtMaW5lJmd0OyIgY2xhc3M9ImNscy0xMCIgeDE9Ijg0LjI0IiB5MT0iMTE2Ljk3IiB4Mj0iMTU2LjY1IiB5Mj0iMTE2Ljk3Ii8+PHBhdGggY2xhc3M9ImNscy05IiBkPSJNMzIuODksMTE3aDBaIi8+PGxpbmUgY2xhc3M9ImNscy0xMCIgeDE9IjEwMiIgeTE9IjEzMS4wNSIgeDI9IjE2My42MiIgeTI9IjEzMS4wNSIvPjxsaW5lIGNsYXNzPSJjbHMtMTMiIHgxPSI1NS44OSIgeTE9IjEzMS4wNSIgeDI9IjkzLjk5IiB5Mj0iMTMxLjA1Ii8+PGxpbmUgY2xhc3M9ImNscy0xMyIgeDE9IjU1Ljg5IiB5MT0iMTQ1LjEzIiB4Mj0iMTM5LjY3IiB5Mj0iMTQ1LjEzIi8+PGxpbmUgY2xhc3M9ImNscy05IiB4MT0iNzguOSIgeTE9IjE1OS4yMSIgeDI9IjExMy41MSIgeTI9IjE1OS4yMSIvPjxsaW5lIGNsYXNzPSJjbHMtOSIgeDE9IjU5LjYxIiB5MT0iODguODIiIHgyPSI5OS4zMyIgeTI9Ijg4LjgyIi8+PGxpbmUgY2xhc3M9ImNscy05IiB4MT0iNTkuNjEiIHkxPSIxMDIuODkiIHgyPSI5OS4zMyIgeTI9IjEwMi44OSIvPjxjaXJjbGUgY2xhc3M9ImNscy02IiBjeD0iMTMuNSIgY3k9IjEyLjA4IiByPSI1LjExIi8+PGNpcmNsZSBjbGFzcz0iY2xzLTciIGN4PSIzMC4xOCIgY3k9IjEyLjA4IiByPSI1LjExIi8+PGNpcmNsZSBjbGFzcz0iY2xzLTgiIGN4PSI0Ni44NiIgY3k9IjEyLjA4IiByPSI1LjExIi8+PC9nPjwvZz48L3N2Zz4=" + }, + { + "body": "\nThis section describes [Cross-origin resource sharing](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing) (CORS), what content types we support in requests and responses, and hyperlinking resources in each json responses.\n\n\n----\n\n* [CORS](#cors)\n* [Supported content types](#supported-content-types)\n* [Resource links](#resource-links)\n\n----\n\n### Cors\n\nThe Bitbucket API supports Cross-origin resource sharing to allow requests for restricted resources across domains. For more information you can refer to:\n\n* [Wikipedia article on CORS](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing)\n* [W3C CORS recommendation](https://www.w3.org/TR/cors/)\n\nSending a general request from the api to bitbucket.com:\n\n`curl -i https://api.bitbucket.org -H \"origin: http://bitbucket.com\"`\n\nGives this result:\n\n HTTP/1.1 302 FOUND\n Server: nginx/1.6.2\n Vary: Cookie\n Cache-Control: max-age=900\n Content-Type: text/html; charset=utf-8\n Strict-Transport-Security: max-age=31536000\n Date: Tue, 21 Jun 2016 17:54:37 GMT\n Location: http://confluence.atlassian.com/x/IYBGDQ\n X-Served-By: app-110\n X-Static-Version: 2c820eb0d2b3\n ETag: \"d41d8cd98f00b204e9800998ecf8427e\"\n X-Content-Type-Options: nosniff\n X-Render-Time: 0.00379920005798\n Connection: Keep-Alive\n X-Version: 2c820eb0d2b3\n X-Frame-Options: SAMEORIGIN\n X-Request-Count: 383\n X-Cache-Info: cached\n Content-Length: 0\n\nSending the same request with the CORS check -X OPTIONS in the call:\n\n`curl -i https://api.bitbucket.org -H \"origin: http://bitbucket.com\" -X OPTIONS`\n\nGives this result:\n\n HTTP/1.1 302 FOUND\n Server: nginx/1.6.2\n Vary: Cookie\n Cache-Control: max-age=900\n Content-Type: text/html; charset=utf-8\n Access-Control-Expose-Headers: Accept-Ranges, Content-Encoding, Content-Length, Content-Type, ETag, Last-Modified\n Strict-Transport-Security: max-age=31536000\n Date: Tue, 21 Jun 2016 18:04:30 GMT\n Access-Control-Max-Age: 86400\n Location: http://confluence.atlassian.com/x/IYBGDQ\n X-Served-By: app-111\n Access-Control-Allow-Origin: *\n X-Static-Version: 2c820eb0d2b3\n ETag: \"d41d8cd98f00b204e9800998ecf8427e\"\n X-Content-Type-Options: nosniff\n X-Render-Time: 0.00371098518372\n Connection: keep-alive\n X-Version: 2c820eb0d2b3\n X-Frame-Options: SAMEORIGIN\n X-Request-Count: 357\n Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE, OPTIONS\n Access-Control-Allow-Headers: Accept, Authorization, Content-Type, If-Match, If-Modified-Since, If-None-Match, If-Unmodified-Since, Origin, Range, X-CsrftokenX-Requested-With\n X-Cache-Info: not cacheable; request wasn't a GET or HEAD\n Content-Length: 0\n\n\n\n### Supported content types\n\nThe default and primary content type for 2.0 APIs is JSON. This applies both to responses from the server and to the request bodies provided by the client.\n\nUnless documented otherwise, whenever creating a new (POST) or modifying an existing (PUT) object, your client must provide the object's normal representation. Not every object element can be mutated. For example, a repository's created_on date is an auto-generated, immutable field. Your client can omit immutable fields from a request body.\n\nIn some cases, a resource might also accept regular application/x-www-url-form-encoded POST and PUT bodies. Such bodies can be more convenient in scripts and command line usage. Requests bodies can contain contain nested elements or they can be flat (without nested elements). Clients can send flat request bodies as either as application/json or as application/x-www-url-form-encoded. Nested objects always require JSON.\n\n### Resource links\n\nEvery 2.0 object contains a links element that points to related resources or alternate representations. Use links to quickly discover and traverse to related objects. Links serve a \"self-documenting\" function for each endpoint. For example, the following request for a specific user:\n\n\n`$ curl https://api.bitbucket.org/2.0/users/tutorials`\n\n```json\n{\n \"username\": \"tutorials\",\n \"nickname\": \"tutorials\",\n \"account_status\": \"active\",\n \"website\": \"https://tutorials.bitbucket.org/\",\n \"display_name\": \"tutorials account\",\n \"uuid\": \"{c788b2da-b7a2-404c-9e26-d3f077557007}\",\n \"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/users/tutorials\"\n },\n \"repositories\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/tutorials\"\n },\n \"html\": {\n \"href\": \"https://bitbucket.org/tutorials\"\n },\n \"followers\": {\n \"href\": \"https://api.bitbucket.org/2.0/users/tutorials/followers\"\n },\n \"avatar\": {\n \"href\": \"https://bitbucket-assetroot.s3.amazonaws.com/c/photos/2013/Nov/25/tutorials-avatar-1563784409-6_avatar.png\"\n },\n \"following\": {\n \"href\": \"https://api.bitbucket.org/2.0/users/tutorials/following\"\n }\n },\n \"created_on\": \"2011-12-20T16:34:07.132459+00:00\",\n \"location\": \"Santa Monica, CA\",\n \"type\": \"user\"\n}\n```\nLinks can be actual REST API resources or they can be informational. In this example, informative resources include the user's avatar and the HTML URL for the user's Bitbucket account. Your client should avoid hardcoding an API's URL and instead use the URLs returned in API responses.\n\nA link's key is its `rel` (relationship) attribute and it contains a mandatory href element. For example, the following link:\n\n```json\n\"self\": {\n \"href\": \"https://api.bitbucket.org/api/2.0/users/tutorials\"\n}\n```\n\nThe rel for this link is self and the href is https://api.bitbucket.org/api/2.0/users/tutorials. A single rel key can contain an list (array) of href objects. Your client should anticipate that any rel key can contain one or more href objects.\n\nFinally, links can also contain optional elements. Two common optional elements are the name element and the title element. They are often used to disambiguate links that share the same rel key. In the example below, the repository object that contains a clone link with two href objects. Each object contains the optional name element to clarify its use.\n\n```json\n\"links\": {\n \"self\": {\n \"href\": \"https://api.bitbucket.org/2.0/repositories/evzijst/bitbucket\"\n },\n \"clone\": [\n {\n \"href\": \"https://api.bitbucket.org/evzijst/bitbucket.git\",\n \"name\": \"https\"\n },\n {\n \"href\": \"ssh://git@bitbucket.org/erik/bitbucket.git\",\n \"name\": \"ssh\"\n }\n ],\n ...\n}\n```\nLinks can support [URI Templates](https://tools.ietf.org/html/rfc6570); Those that do contain a `\"templated\": \"true\"` element.\n", + "title": "Cors and hypermedia", + "anchor": "cors-hypermedia", + "description": "Learn about resources and linking", + "icon": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgMjM2LjYgMjE4LjQzIj48ZGVmcz48c3R5bGU+LmNscy0xe2lzb2xhdGlvbjppc29sYXRlO30uY2xzLTIsLmNscy0zLC5jbHMtNHtmaWxsOm5vbmU7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7c3Ryb2tlLXdpZHRoOjExcHg7fS5jbHMtMntzdHJva2U6dXJsKCNsaW5lYXItZ3JhZGllbnQpO30uY2xzLTN7c3Ryb2tlOnVybCgjTmV3X0dyYWRpZW50X1N3YXRjaF8xNCk7fS5jbHMtNHtzdHJva2U6dXJsKCNOZXdfR3JhZGllbnRfU3dhdGNoXzEpO30uY2xzLTV7ZmlsbDojNDI1MjZlO30uY2xzLTZ7ZmlsbDojZmY1NjMwO30uY2xzLTEwLC5jbHMtNywuY2xzLTh7bWl4LWJsZW5kLW1vZGU6bXVsdGlwbHk7fS5jbHMtN3tmaWxsOnVybCgjbGluZWFyLWdyYWRpZW50LTIpO30uY2xzLTh7ZmlsbDp1cmwoI2xpbmVhci1ncmFkaWVudC0zKTt9LmNscy05e2ZpbGw6IzAwNjVmZjt9LmNscy0xMHtmaWxsOnVybCgjbGluZWFyLWdyYWRpZW50LTQpO308L3N0eWxlPjxsaW5lYXJHcmFkaWVudCBpZD0ibGluZWFyLWdyYWRpZW50IiB5MT0iMTY3Ljg3IiB4Mj0iMTkxLjU2IiB5Mj0iMTY3Ljg3IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjNTA1Zjc5Ii8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjMzQ0NTYzIi8+PC9saW5lYXJHcmFkaWVudD48bGluZWFyR3JhZGllbnQgaWQ9Ik5ld19HcmFkaWVudF9Td2F0Y2hfMTQiIHgxPSIxMTIuODMiIHkxPSIxMzEuNzIiIHgyPSIyMzYuNiIgeTI9IjEzMS43MiIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPjxzdG9wIG9mZnNldD0iMCIgc3RvcC1jb2xvcj0iIzAwNTJjYyIvPjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iIzI2ODRmZiIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IGlkPSJOZXdfR3JhZGllbnRfU3dhdGNoXzEiIHgxPSI0NS4wNiIgeTE9Ijg2LjY5IiB4Mj0iMTY4Ljg4IiB5Mj0iODYuNjkiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj48c3RvcCBvZmZzZXQ9IjAiIHN0b3AtY29sb3I9IiNkZTM1MGIiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNmZjc0NTIiLz48L2xpbmVhckdyYWRpZW50PjxsaW5lYXJHcmFkaWVudCBpZD0ibGluZWFyLWdyYWRpZW50LTIiIHgxPSIzNDQ3LjkzIiB5MT0iLTkxOC43OSIgeDI9IjM0NTEuNCIgeTI9Ii0xMDMyLjc2IiBncmFkaWVudFRyYW5zZm9ybT0idHJhbnNsYXRlKC01NzIuMzggMzcwNC4yOSkgcm90YXRlKC02NC4zNCkiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj48c3RvcCBvZmZzZXQ9IjAuMzEiIHN0b3AtY29sb3I9IiNjMWM3ZDAiIHN0b3Atb3BhY2l0eT0iMCIvPjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iI2MxYzdkMCIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IGlkPSJsaW5lYXItZ3JhZGllbnQtMyIgeDE9IjM1MDYuNiIgeTE9Ii03OTYuNjYiIHgyPSIzNTEwLjA3IiB5Mj0iLTkxMC42MyIgeGxpbms6aHJlZj0iI2xpbmVhci1ncmFkaWVudC0yIi8+PGxpbmVhckdyYWRpZW50IGlkPSJsaW5lYXItZ3JhZGllbnQtNCIgeDE9IjM1ODEuNjgiIHkxPSItOTA2LjgyIiB4Mj0iMzU4NS4xNiIgeTI9Ii0xMDIwLjc5IiB4bGluazpocmVmPSIjbGluZWFyLWdyYWRpZW50LTIiLz48L2RlZnM+PHRpdGxlPldlYmhvb2tzPC90aXRsZT48ZyBjbGFzcz0iY2xzLTEiPjxnIGlkPSJMYXllcl8yIiBkYXRhLW5hbWU9IkxheWVyIDIiPjxnIGlkPSJTb2Z0d2FyZSI+PHBhdGggY2xhc3M9ImNscy0yIiBkPSJNMTg2LjA2LDE2Ny44N0gxMTcuNzJBMjcuNTgsMjcuNTgsMCwwLDAsOTIuMjQsMTg1YTQ1LjA2LDQ1LjA2LDAsMSwxLTQxLjY4LTYyLjE5Ii8+PHBhdGggY2xhc3M9ImNscy0zIiBkPSJNMTE4LjMzLDUwLjUybDM0LjE1LDU5LjE4YTI3LjU5LDI3LjU5LDAsMCwwLDI3LjU4LDEzLjUxLDQ1LjA2LDQ1LjA2LDAsMSwxLTMzLDY3LjE5Ii8+PHBhdGggY2xhc3M9ImNscy00IiBkPSJNNTAuNTYsMTY3Ljg3bDM0LjE4LTU5LjE2YTI3LjU5LDI3LjU5LDAsMCwwLTIuMDktMzAuNjQsNDUuMDYsNDUuMDYsMCwxLDEsNzQuNy01Ii8+PHBhdGggY2xhc3M9ImNscy01IiBkPSJNMTg2LjA2LDE5OS42NmEzMS43OSwzMS43OSwwLDEsMSwzMS43OS0zMS43OUEzMS44MiwzMS44MiwwLDAsMSwxODYuMDYsMTk5LjY2WiIvPjxnIGlkPSJfR3JvdXBfIiBkYXRhLW5hbWU9IiZsdDtHcm91cCZndDsiPjxwYXRoIGNsYXNzPSJjbHMtNiIgZD0iTTQ5LjU2LDE5OS42NGEzMS43OSwzMS43OSwwLDEsMSwzMi43Ny0zMC43N0EzMS44MiwzMS44MiwwLDAsMSw0OS41NiwxOTkuNjRaIi8+PC9nPjxwYXRoIGNsYXNzPSJjbHMtNyIgZD0iTTU0LjEyLDE4MC4zNmE1OC45LDU4LjksMCwwLDAtMS41OS0xMS4yN3MtMi4zNS05LjQ0LTcuMzMtMTYuODNhNDMuODksNDMuODksMCwwLDAtMTEuNzMtMTEuMTcsMzEuNzcsMzEuNzcsMCwwLDAsMjkuMjgsNTYuMTNDNTguMjksMTkyLjQ0LDU0LjY4LDE4Ni45LDU0LjEyLDE4MC4zNloiLz48cGF0aCBjbGFzcz0iY2xzLTgiIGQ9Ik0xODkuNjIsMTgwLjM2QTU4LjksNTguOSwwLDAsMCwxODgsMTY5LjA4cy0yLjM1LTkuNDQtNy4zMy0xNi44M0E0My44OSw0My44OSwwLDAsMCwxNjksMTQxLjA4YTMxLjc3LDMxLjc3LDAsMCwwLDI5LjI4LDU2LjEzQzE5My43OSwxOTIuNDQsMTkwLjE4LDE4Ni45LDE4OS42MiwxODAuMzZaIi8+PGcgaWQ9Il9Hcm91cF8yIiBkYXRhLW5hbWU9IiZsdDtHcm91cCZndDsiPjxwYXRoIGNsYXNzPSJjbHMtOSIgZD0iTTg5LjksMzMuNzhhMzIuNDYsMzIuNDYsMCwxLDEsMTEuODgsNDQuMzRBMzIuNDksMzIuNDksMCwwLDEsODkuOSwzMy43OFoiLz48L2c+PHBhdGggY2xhc3M9ImNscy0xMCIgZD0iTTEyMi4yMyw2Ni4xM2E1OC45LDU4LjksMCwwLDAtMS41OS0xMS4yN3MtMi4zNS05LjQ0LTcuMzMtMTYuODNjLTMuMzctNS05LjA2LTkuNzktMTUuNDMtMTMuNDhBMzIuNDQsMzIuNDQsMCwwLDAsMTI4Ljc3LDgwLjZDMTI1LjMxLDc2LjM5LDEyMi43LDcxLjYxLDEyMi4yMyw2Ni4xM1oiLz48L2c+PC9nPjwvZz48L3N2Zz4=" + }, + { + "body": "\nYou can use the Atlassian Connect for Bitbucket Cloud to build add-ons which\ncan connect with the Bitbucket UI and your own application set. An add-on could\nbe an integration with another existing service, new features for the Atlassian\napplication, or even a new product that runs within the Atlassian application.\n\nFor complete information see:\n[Atlassian Connect for Bitbucket Cloud](https://developer.atlassian.com/bitbucket/index.html)\n", + "title": "Atlassian Connect", + "anchor": "bb-connect", + "description": "Build Bitbucket add-ons with Connect", + "icon": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgMjU3LjMxNjMgMTYyLjU5OTQiPgogIDxkZWZzPgogICAgPHN0eWxlPgogICAgICAuY2xzLTEgewogICAgICAgIGlzb2xhdGlvbjogaXNvbGF0ZTsKICAgICAgfQoKICAgICAgLmNscy0yIHsKICAgICAgICBmaWxsOiAjMzQ0NTYzOwogICAgICB9CgogICAgICAuY2xzLTMgewogICAgICAgIGZpbGw6ICMxZGI5ZDQ7CiAgICAgIH0KCiAgICAgIC5jbHMtNCB7CiAgICAgICAgZmlsbDogIzAwNTdkODsKICAgICAgfQoKICAgICAgLmNscy01LCAuY2xzLTcsIC5jbHMtOSB7CiAgICAgICAgbWl4LWJsZW5kLW1vZGU6IG11bHRpcGx5OwogICAgICB9CgogICAgICAuY2xzLTUgewogICAgICAgIGZpbGw6IHVybCgjTjc1KTsKICAgICAgfQoKICAgICAgLmNscy02IHsKICAgICAgICBmaWxsOiAjMDA2NWZmOwogICAgICB9CgogICAgICAuY2xzLTcgewogICAgICAgIGZpbGw6IHVybCgjTjc1LTIpOwogICAgICB9CgogICAgICAuY2xzLTggewogICAgICAgIGZpbGw6IHVybCgjbGluZWFyLWdyYWRpZW50KTsKICAgICAgfQoKICAgICAgLmNscy05IHsKICAgICAgICBmaWxsOiB1cmwoI043NS0zKTsKICAgICAgfQoKICAgICAgLmNscy0xMCB7CiAgICAgICAgZmlsbDogdXJsKCNUMjAwLVQ3NSk7CiAgICAgIH0KICAgIDwvc3R5bGU+CiAgICA8bGluZWFyR3JhZGllbnQgaWQ9Ik43NSIgeDE9Ii0yMTg5LjU1NiIgeTE9IjI4MDguMjI4NCIgeDI9Ii0yMDkxLjE1NTEiIHkyPSIyODA4LjIyODQiIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMC4xNjgxLCAwLjk4NTgsIDAuOTg1OCwgLTAuMTY4MSwgLTIzNzMuNzM2LCAyNzIyLjEyNzgpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+CiAgICAgIDxzdG9wIG9mZnNldD0iMCIgc3RvcC1jb2xvcj0iI2U1ZThlYyIvPgogICAgICA8c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNlNWU4ZWMiIHN0b3Atb3BhY2l0eT0iMC4xIi8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50IGlkPSJONzUtMiIgZGF0YS1uYW1lPSJONzUiIHgxPSItMjE2OC41OTQ3IiB5MT0iMjkzNS4wNTU2IiB4Mj0iLTIwNzAuMTkzNyIgeTI9IjI5MzUuMDU1NiIgeGxpbms6aHJlZj0iI043NSIvPgogICAgPGxpbmVhckdyYWRpZW50IGlkPSJsaW5lYXItZ3JhZGllbnQiIHgxPSIxOTAuMzU0NyIgeTE9IjE1OS45NjciIHgyPSIyNTkuOTQ4NyIgeTI9IjkwLjM3MyIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPgogICAgICA8c3RvcCBvZmZzZXQ9IjAiIHN0b3AtY29sb3I9IiMzNDQ1NjMiLz4KICAgICAgPHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjNWU2Yzg0Ii8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50IGlkPSJONzUtMyIgZGF0YS1uYW1lPSJONzUiIHgxPSItMjI1Ny4zOTc3IiB5MT0iMjg4NC4yMjYzIiB4Mj0iLTIxNTguOTk2NyIgeTI9IjI4ODQuMjI2MyIgeGxpbms6aHJlZj0iI043NSIvPgogICAgPGxpbmVhckdyYWRpZW50IGlkPSJUMjAwLVQ3NSIgeDE9IjEyNi4wMjU3IiB5MT0iODUuMTA4IiB4Mj0iMTk1LjYxOTciIHkyPSIxNS41MTQiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj4KICAgICAgPHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjM2RjN2RjIi8+CiAgICAgIDxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iIzljZTNlZSIvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICA8L2RlZnM+CiAgPHRpdGxlPkFkZCBPbiBCbG9ja3MgMjwvdGl0bGU+CiAgPGcgY2xhc3M9ImNscy0xIj4KICAgIDxnIGlkPSJMYXllcl8yIiBkYXRhLW5hbWU9IkxheWVyIDIiPgogICAgICA8ZyBpZD0iT2JqZWN0cyI+CiAgICAgICAgPHJlY3QgaWQ9Il9SZWN0YW5nbGVfIiBkYXRhLW5hbWU9IiZsdDtSZWN0YW5nbGUmZ3Q7IiBjbGFzcz0iY2xzLTIiIHg9IjEyOC42NTgxIiB5PSI4Ny43NDA1IiB3aWR0aD0iNjQuMzI5MSIgaGVpZ2h0PSI3NC44NTkiLz4KICAgICAgICA8cmVjdCBpZD0iX1JlY3RhbmdsZV8yIiBkYXRhLW5hbWU9IiZsdDtSZWN0YW5nbGUmZ3Q7IiBjbGFzcz0iY2xzLTMiIHg9IjY0LjMyOTEiIHk9IjEyLjg4MTUiIHdpZHRoPSI2NC4zMjkxIiBoZWlnaHQ9Ijc0Ljg1OSIvPgogICAgICAgIDxyZWN0IGlkPSJfUmVjdGFuZ2xlXzMiIGRhdGEtbmFtZT0iJmx0O1JlY3RhbmdsZSZndDsiIGNsYXNzPSJjbHMtNCIgeT0iODcuNzQwNSIgd2lkdGg9IjY0LjMyOTEiIGhlaWdodD0iNzQuODU5Ii8+CiAgICAgICAgPHBhdGggY2xhc3M9ImNscy01IiBkPSJNNjQuMzI5MSw4Ny43NEg1OC42NTIzYTYyLjc4NzcsNjIuNzg3NywwLDAsMC0yNC41Njg0LDIwLjc2MjFjLTguMjYwNywxMi4xOTYtNC4zNDM3LDE4LjI0MTUtMTEuNjYzNywzMS42Mzk1QzE2LjUxLDE1MC45Niw4LjA1MSwxNTcuODIsMCwxNjIuNTU2di4wNDM1aDY0LjMyOVoiLz4KICAgICAgICA8cmVjdCBpZD0iX1JlY3RhbmdsZV80IiBkYXRhLW5hbWU9IiZsdDtSZWN0YW5nbGUmZ3Q7IiBjbGFzcz0iY2xzLTYiIHg9IjY0LjMyOTEiIHk9Ijg3Ljc0MDUiIHdpZHRoPSI2NC4zMjkxIiBoZWlnaHQ9Ijc0Ljg1OSIvPgogICAgICAgIDxwYXRoIGNsYXNzPSJjbHMtNyIgZD0iTTE5Mi45ODcyLDg3Ljc0SDE4My4zNDNhNjIuNzg3Nyw2Mi43ODc3LDAsMCwwLTI0LjU2ODQsMjAuNzYyMWMtOC4yNjA3LDEyLjE5Ni00LjM0MzgsMTguMjQxNS0xMS42NjM3LDMxLjYzOTVhNTYuNzI0Niw1Ni43MjQ2LDAsMCwxLTE4LjQ1MjcsMTkuOTF2Mi41NDc0aDY0LjMyOVoiLz4KICAgICAgICA8cmVjdCBjbGFzcz0iY2xzLTgiIHg9IjE5Mi45ODcyIiB5PSI4Ny43NDA1IiB3aWR0aD0iNjQuMzI5MSIgaGVpZ2h0PSI3NC44NTkiLz4KICAgICAgICA8cGF0aCBjbGFzcz0iY2xzLTkiIGQ9Ik0xMjguNjU4MiwxMi44ODE1SDExNi41OUE2MS45NjQ2LDYxLjk2NDYsMCwwLDAsMTAxLjMyMzMsMjguMjE1QzkzLjA2MjUsNDAuNDEwOSw5Ni45Nzk0LDQ2LjQ1NjUsODkuNjYsNTkuODU0NCw4My4wMzE2LDcxLjk4NTcsNzMuMTk3LDc5LjE1MTQsNjQuMzI5MSw4My45MTA4djMuODNoNjQuMzI5MVoiLz4KICAgICAgICA8cmVjdCBjbGFzcz0iY2xzLTEwIiB4PSIxMjguNjU4MSIgeT0iMTIuODgxNSIgd2lkdGg9IjY0LjMyOTEiIGhlaWdodD0iNzQuODU5Ii8+CiAgICAgICAgPHJlY3QgY2xhc3M9ImNscy00IiB4PSIxMC4xNjA1IiB5PSI3NC44NTkiIHdpZHRoPSIyNC43NTM2IiBoZWlnaHQ9IjEyLjg4MTUiLz4KICAgICAgICA8cmVjdCBjbGFzcz0iY2xzLTQiIHg9IjUxLjk1MjMiIHk9Ijc0Ljg1OSIgd2lkdGg9IjI0Ljc1MzYiIGhlaWdodD0iMTIuODgxNSIvPgogICAgICAgIDxyZWN0IGNsYXNzPSJjbHMtNCIgeD0iOTMuNzQ0MSIgeT0iNzQuODU5IiB3aWR0aD0iMjQuNzUzNiIgaGVpZ2h0PSIxMi44ODE1Ii8+CiAgICAgICAgPHJlY3QgY2xhc3M9ImNscy0yIiB4PSIxMzguODE4NiIgeT0iNzQuODU5IiB3aWR0aD0iMjQuNzUzNiIgaGVpZ2h0PSIxMi44ODE1Ii8+CiAgICAgICAgPHJlY3QgY2xhc3M9ImNscy0yIiB4PSIxODAuNjEwNCIgeT0iNzQuODU5IiB3aWR0aD0iMjQuNzUzNiIgaGVpZ2h0PSIxMi44ODE1Ii8+CiAgICAgICAgPHJlY3QgY2xhc3M9ImNscy0yIiB4PSIyMjIuNDAyMiIgeT0iNzQuODU5IiB3aWR0aD0iMjQuNzUzNiIgaGVpZ2h0PSIxMi44ODE1Ii8+CiAgICAgICAgPHJlY3QgY2xhc3M9ImNscy0zIiB4PSI3NC40ODk1IiB3aWR0aD0iMjQuNzUzNiIgaGVpZ2h0PSIxMi44ODE1Ii8+CiAgICAgICAgPHJlY3QgY2xhc3M9ImNscy0zIiB4PSIxMTYuMjgxNCIgd2lkdGg9IjI0Ljc1MzYiIGhlaWdodD0iMTIuODgxNSIvPgogICAgICAgIDxyZWN0IGNsYXNzPSJjbHMtMyIgeD0iMTU4LjA3MzIiIHdpZHRoPSIyNC43NTM2IiBoZWlnaHQ9IjEyLjg4MTUiLz4KICAgICAgPC9nPgogICAgPC9nPgogIDwvZz4KPC9zdmc+Cg==" + } + ] + }, + "servers": [ + { + "url": "https://api.bitbucket.org/2.0" + } + ], + "components": { + "requestBodies": { + "application_property": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/application_property" + } + } + }, + "description": "The application property to create or update.", + "required": true + }, + "pipeline_variable": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/pipeline_variable" + } + } + }, + "description": "The updated variable.", + "required": true + }, + "snippet": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/snippet" + } + } + }, + "description": "The new snippet object.", + "required": true + }, + "issue_comment": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/issue_comment" + } + } + }, + "description": "The updated comment.", + "required": true + }, + "pipeline_variable2": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/pipeline_variable" + } + } + }, + "description": "The variable to create." + }, + "project": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/project" + } + } + }, + "required": true + } + }, + "securitySchemes": { + "basic": { + "type": "http", + "description": "Basic HTTP Authentication as per [RFC-2617](https://tools.ietf.org/html/rfc2617) (Digest not supported). Note that Basic Auth is available only with username and app password as credentials.", + "scheme": "basic" + }, + "api_key": { + "description": "API Keys can be used as Basic HTTP Authentication credentials and provide a substitute for the account's actual username and password. API Keys are only available to team accounts and there is only 1 key per account. API Keys do not support scopes and have therefore access to all contents of the account.", + "type": "apiKey", + "name": "Authorization", + "in": "header" + }, + "oauth2": { + "description": "OAuth 2 as per [RFC-6749](https://tools.ietf.org/html/rfc6749).", + "type": "oauth2", + "flows": { + "authorizationCode": { + "authorizationUrl": "https://bitbucket.org/site/oauth2/authorize", + "tokenUrl": "https://bitbucket.org/site/oauth2/access_token", + "scopes": { + "wiki": "Read and modify your repositories' wikis", + "pullrequest:write": "Read and modify your repositories and their pull requests", + "runner": "Access your workspaces/repositories' runners", + "runner:write": "Access and edit your workspaces/repositories' runners", + "pipeline:variable": "Access your repositories' build pipelines and configure their variables", + "project:write": "Read and modify your workspace's project settings, and read and transfer repositories within your workspace's projects", + "pipeline:write": "Access and rerun your repositories' build pipelines", + "snippet": "Read your snippets", + "repository:delete": "Delete your repositories", + "repository:write": "Read and modify your repositories", + "issue": "Read your repositories' issues", + "email": "Read your account's primary email address", + "repository": "Read your repositories", + "issue:write": "Read and modify your repositories' issues", + "webhook": "Read and modify your repositories' webhooks", + "pipeline": "Access your repositories' build pipelines", + "snippet:write": "Read and modify your snippets", + "account": "Read your account information", + "repository:admin": "Administer your repositories", + "pullrequest": "Read your repositories and their pull requests", + "project": "Read your workspace's project settings and read repositories contained within your workspace's projects", + "team": "Read your team membership information", + "team:write": "Read and modify your team membership information", + "account:write": "Read and modify your account information" + } + } + } + } + }, + "schemas": { + "account": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "type": "object", + "properties": { + "account_status": { + "type": "string", + "description": "The status of the account. Currently the only possible value is \"active\", but more values may be added in the future." + }, + "created_on": { + "type": "string", + "format": "date-time" + }, + "display_name": { + "type": "string" + }, + "has_2fa_enabled": { + "type": "boolean" + }, + "links": { + "type": "object", + "properties": { + "avatar": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + }, + "followers": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + }, + "following": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + }, + "html": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + }, + "repositories": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + }, + "self": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "nickname": { + "type": "string", + "description": "Account name defined by the owner. Should be used instead of the \"username\" field. Note that \"nickname\" cannot be used in place of \"username\" in URLs and queries, as \"nickname\" is not guaranteed to be unique." + }, + "username": { + "type": "string", + "pattern": "^[a-zA-Z0-9_\\-]+$" + }, + "uuid": { + "type": "string" + }, + "website": { + "type": "string" + } + }, + "additionalProperties": true + } + ], + "title": "Account", + "description": "An account object." + }, + "application_property": { + "additionalProperties": true, + "type": "object", + "title": "Application Property", + "description": "An application property. It is a caller defined JSON object that Bitbucket will store and return. \nThe `_attributes` field at its top level can be used to control who is allowed to read and update the property. \nThe keys of the JSON object must match an allowed pattern. For details, \nsee [Application properties](https://developer.atlassian.com/cloud/bitbucket/application-properties/).\n", + "properties": { + "_attributes": { + "type": "array", + "items": { + "type": "string", + "enum": ["public", "read_only"] + } + } + } + }, + "author": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "type": "object", + "properties": { + "raw": { + "type": "string", + "description": "The raw author value from the repository. This may be the only value available if the author does not match a user in Bitbucket." + }, + "user": { + "$ref": "#/components/schemas/account" + } + }, + "additionalProperties": true + } + ], + "title": "Author", + "description": "The author of a change in a repository" + }, + "base_commit": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "type": "object", + "properties": { + "author": { + "$ref": "#/components/schemas/author" + }, + "date": { + "type": "string", + "format": "date-time" + }, + "hash": { + "type": "string", + "pattern": "[0-9a-f]{7,}?" + }, + "message": { + "type": "string" + }, + "parents": { + "type": "array", + "items": { + "$ref": "#/components/schemas/base_commit" + }, + "minItems": 0 + }, + "summary": { + "type": "object", + "properties": { + "html": { + "type": "string", + "description": "The user's content rendered as HTML." + }, + "markup": { + "type": "string", + "description": "The type of markup language the raw content is to be interpreted in.", + "enum": ["markdown", "creole", "plaintext"] + }, + "raw": { + "type": "string", + "description": "The text as it was typed by a user." + } + }, + "additionalProperties": false + } + }, + "additionalProperties": true + } + ], + "title": "Base Commit", + "description": "The common base type for both repository and snippet commits." + }, + "branch": { + "allOf": [ + { + "$ref": "#/components/schemas/ref" + }, + { + "type": "object", + "properties": { + "default_merge_strategy": { + "type": "string", + "description": "The default merge strategy for pull requests targeting this branch." + }, + "merge_strategies": { + "type": "array", + "description": "Available merge strategies for pull requests targeting this branch.", + "items": { + "type": "string", + "enum": ["merge_commit", "squash", "fast_forward"] + } + } + }, + "additionalProperties": true + } + ], + "title": "Branch", + "description": "A branch object, representing a branch in a repository." + }, + "branching_model": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "type": "object", + "properties": { + "branch_types": { + "type": "array", + "description": "The active branch types.", + "items": { + "type": "object", + "properties": { + "kind": { + "type": "string", + "description": "The kind of branch.", + "enum": ["feature", "bugfix", "release", "hotfix"] + }, + "prefix": { + "type": "string", + "description": "The prefix for this branch type. A branch with this prefix will be classified as per `kind`. The prefix must be a valid prefix for a branch and must always exist. It cannot be blank, empty or `null`." + } + }, + "required": ["kind", "prefix"], + "additionalProperties": false + }, + "minItems": 0, + "maxItems": 4, + "uniqueItems": true + }, + "development": { + "type": "object", + "properties": { + "branch": { + "$ref": "#/components/schemas/branch" + }, + "branch_does_not_exist": { + "type": "boolean", + "description": "Indicates if the indicated branch exists on the repository (`false`)or not (`true`). This is useful for determining a fallback to the mainbranch when a repository is inheriting its project's branching model." + }, + "name": { + "type": "string", + "description": "Name of the target branch. Will be listed here even when the target branch does not exist. Will be `null` if targeting the main branch and the repository is empty." + }, + "use_mainbranch": { + "type": "boolean", + "description": "Indicates if the setting points at an explicit branch (`false`) or tracks the main branch (`true`)." + } + }, + "required": ["name", "use_mainbranch"], + "additionalProperties": false + }, + "production": { + "type": "object", + "properties": { + "branch": { + "$ref": "#/components/schemas/branch" + }, + "branch_does_not_exist": { + "type": "boolean", + "description": "Indicates if the indicated branch exists on the repository (`false`)or not (`true`). This is useful for determining a fallback to the mainbranch when a repository is inheriting its project's branching model." + }, + "name": { + "type": "string", + "description": "Name of the target branch. Will be listed here even when the target branch does not exist. Will be `null` if targeting the main branch and the repository is empty." + }, + "use_mainbranch": { + "type": "boolean", + "description": "Indicates if the setting points at an explicit branch (`false`) or tracks the main branch (`true`)." + } + }, + "required": ["name", "use_mainbranch"], + "additionalProperties": false + } + }, + "additionalProperties": true + } + ], + "title": "Branching Model", + "description": "A repository's branching model" + }, + "branching_model_settings": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "type": "object", + "properties": { + "branch_types": { + "type": "array", + "items": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "Whether the branch type is enabled or not. A disabled branch type may contain an invalid `prefix`." + }, + "kind": { + "type": "string", + "description": "The kind of the branch type.", + "enum": ["feature", "bugfix", "release", "hotfix"] + }, + "prefix": { + "type": "string", + "description": "The prefix for this branch type. A branch with this prefix will be classified as per `kind`. The `prefix` of an enabled branch type must be a valid branch prefix.Additionally, it cannot be blank, empty or `null`. The `prefix` for a disabled branch type can be empty or invalid." + } + }, + "required": ["kind"], + "additionalProperties": false + }, + "minItems": 0, + "maxItems": 4, + "uniqueItems": true + }, + "development": { + "type": "object", + "properties": { + "branch_does_not_exist": { + "type": "boolean", + "description": "Optional and only returned for a repository's branching model. Indicates ifthe indicated branch exists on the repository (`false`)or not (`true`). This is useful for determining a fallback to the mainbranch when a repository is inheriting its project's branching model." + }, + "is_valid": { + "type": "boolean", + "description": "Indicates if the configured branch is valid, that is, if the configured branch actually exists currently. Is always `true` when `use_mainbranch` is `true` (even if the main branch does not exist). This field is read-only. This field is ignored when updating/creating settings." + }, + "name": { + "type": "string", + "description": "The configured branch. It must be `null` when `use_mainbranch` is `true`. Otherwise it must be a non-empty value. It is possible for the configured branch to not exist (e.g. it was deleted after the settings are set). In this case `is_valid` will be `false`. The branch must exist when updating/setting the `name` or an error will occur." + }, + "use_mainbranch": { + "type": "boolean", + "description": "Indicates if the setting points at an explicit branch (`false`) or tracks the main branch (`true`). When `true` the `name` must be `null` or not provided. When `false` the `name` must contain a non-empty branch name." + } + }, + "additionalProperties": false + }, + "links": { + "type": "object", + "properties": { + "self": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "production": { + "type": "object", + "properties": { + "branch_does_not_exist": { + "type": "boolean", + "description": "Optional and only returned for a repository's branching model. Indicates ifthe indicated branch exists on the repository (`false`)or not (`true`). This is useful for determining a fallback to the mainbranch when a repository is inheriting its project's branching model." + }, + "enabled": { + "type": "boolean", + "description": "Indicates if branch is enabled or not." + }, + "is_valid": { + "type": "boolean", + "description": "Indicates if the configured branch is valid, that is, if the configured branch actually exists currently. Is always `true` when `use_mainbranch` is `true` (even if the main branch does not exist). This field is read-only. This field is ignored when updating/creating settings." + }, + "name": { + "type": "string", + "description": "The configured branch. It must be `null` when `use_mainbranch` is `true`. Otherwise it must be a non-empty value. It is possible for the configured branch to not exist (e.g. it was deleted after the settings are set). In this case `is_valid` will be `false`. The branch must exist when updating/setting the `name` or an error will occur." + }, + "use_mainbranch": { + "type": "boolean", + "description": "Indicates if the setting points at an explicit branch (`false`) or tracks the main branch (`true`). When `true` the `name` must be `null` or not provided. When `false` the `name` must contain a non-empty branch name." + } + }, + "additionalProperties": false + } + }, + "additionalProperties": true + } + ], + "title": "Branching Model Settings", + "description": "A repository's branching model settings" + }, + "branchrestriction": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "type": "object", + "properties": { + "branch_match_kind": { + "type": "string", + "description": "Indicates how the restriction is matched against a branch. The default is `glob`.", + "enum": ["branching_model", "glob"] + }, + "branch_type": { + "type": "string", + "description": "Apply the restriction to branches of this type. Active when `branch_match_kind` is `branching_model`. The branch type will be calculated using the branching model configured for the repository.", + "enum": [ + "feature", + "bugfix", + "release", + "hotfix", + "development", + "production" + ] + }, + "groups": { + "type": "array", + "items": { + "$ref": "#/components/schemas/group" + }, + "minItems": 0 + }, + "id": { + "type": "integer", + "description": "The branch restriction status' id." + }, + "kind": { + "type": "string", + "description": "The type of restriction that is being applied.", + "enum": [ + "require_tasks_to_be_completed", + "allow_auto_merge_when_builds_pass", + "require_passing_builds_to_merge", + "force", + "require_all_dependencies_merged", + "require_commits_behind", + "restrict_merges", + "enforce_merge_checks", + "reset_pullrequest_changes_requested_on_change", + "require_no_changes_requested", + "smart_reset_pullrequest_approvals", + "push", + "require_approvals_to_merge", + "require_default_reviewer_approvals_to_merge", + "reset_pullrequest_approvals_on_change", + "delete" + ] + }, + "links": { + "type": "object", + "properties": { + "self": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "pattern": { + "type": "string", + "description": "Apply the restriction to branches that match this pattern. Active when `branch_match_kind` is `glob`. Will be empty when `branch_match_kind` is `branching_model`." + }, + "users": { + "type": "array", + "items": { + "$ref": "#/components/schemas/account" + }, + "minItems": 0 + }, + "value": { + "type": "integer" + } + }, + "required": ["kind", "branch_match_kind", "pattern"], + "additionalProperties": true + } + ], + "title": "Branch Restriction", + "description": "A branch restriction rule." + }, + "comment": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "type": "object", + "properties": { + "content": { + "type": "object", + "properties": { + "html": { + "type": "string", + "description": "The user's content rendered as HTML." + }, + "markup": { + "type": "string", + "description": "The type of markup language the raw content is to be interpreted in.", + "enum": ["markdown", "creole", "plaintext"] + }, + "raw": { + "type": "string", + "description": "The text as it was typed by a user." + } + }, + "additionalProperties": false + }, + "created_on": { + "type": "string", + "format": "date-time" + }, + "deleted": { + "type": "boolean" + }, + "id": { + "type": "integer" + }, + "inline": { + "type": "object", + "properties": { + "from": { + "type": "integer", + "description": "The comment's anchor line in the old version of the file.", + "minimum": 1 + }, + "path": { + "type": "string", + "description": "The path of the file this comment is anchored to." + }, + "to": { + "type": "integer", + "description": "The comment's anchor line in the new version of the file. If the 'from' line is also provided, this value will be removed.", + "minimum": 1 + } + }, + "required": ["path"], + "additionalProperties": false + }, + "links": { + "type": "object", + "properties": { + "code": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + }, + "html": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + }, + "self": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "parent": { + "$ref": "#/components/schemas/comment" + }, + "updated_on": { + "type": "string", + "format": "date-time" + }, + "user": { + "$ref": "#/components/schemas/user" + } + }, + "additionalProperties": true + } + ], + "title": "Comment", + "description": "The base type for all comments. This type should be considered abstract. Each of the \"commentable\" resources defines its own subtypes (e.g. `issue_comment`)." + }, + "commit": { + "allOf": [ + { + "$ref": "#/components/schemas/base_commit" + }, + { + "type": "object", + "properties": { + "participants": { + "type": "array", + "items": { + "$ref": "#/components/schemas/participant" + }, + "minItems": 0 + }, + "repository": { + "$ref": "#/components/schemas/repository" + } + }, + "additionalProperties": true + } + ], + "title": "Commit", + "description": "A repository commit object." + }, + "commit_comment": { + "allOf": [ + { + "$ref": "#/components/schemas/comment" + }, + { + "type": "object", + "properties": { + "commit": { + "$ref": "#/components/schemas/commit" + } + }, + "additionalProperties": true + } + ], + "title": "Commit Comment", + "description": "A commit comment." + }, + "commit_file": { + "type": "object", + "title": "Commit File", + "description": "A file object, representing a file at a commit in a repository", + "properties": { + "attributes": { + "type": "string", + "enum": ["link", "executable", "subrepository", "binary", "lfs"] + }, + "commit": { + "$ref": "#/components/schemas/commit" + }, + "escaped_path": { + "type": "string", + "description": "The escaped version of the path as it appears in a diff. If the path does not require escaping this will be the same as path." + }, + "path": { + "type": "string", + "description": "The path in the repository" + }, + "type": { + "type": "string" + } + }, + "required": ["type"], + "additionalProperties": true + }, + "commitstatus": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "type": "object", + "properties": { + "created_on": { + "type": "string", + "format": "date-time" + }, + "description": { + "type": "string", + "description": "A description of the build (e.g. \"Unit tests in Bamboo\")" + }, + "key": { + "type": "string", + "description": "An identifier for the status that's unique to\n its type (current \"build\" is the only supported type) and the vendor,\n e.g. BB-DEPLOY" + }, + "links": { + "type": "object", + "properties": { + "commit": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + }, + "self": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "name": { + "type": "string", + "description": "An identifier for the build itself, e.g. BB-DEPLOY-1" + }, + "refname": { + "type": "string", + "description": "\nThe name of the ref that pointed to this commit at the time the status\nobject was created. Note that this the ref may since have moved off of\nthe commit. This optional field can be useful for build systems whose\nbuild triggers and configuration are branch-dependent (e.g. a Pipeline\nbuild).\nIt is legitimate for this field to not be set, or even apply (e.g. a\nstatic linting job)." + }, + "state": { + "type": "string", + "description": "Provides some indication of the status of this commit", + "enum": ["SUCCESSFUL", "FAILED", "INPROGRESS", "STOPPED"] + }, + "updated_on": { + "type": "string", + "format": "date-time" + }, + "url": { + "type": "string", + "description": "A URL linking back to the vendor or build system, for providing more information about whatever process produced this status. Accepts context variables `repository` and `commit` that Bitbucket will evaluate at runtime whenever at runtime. For example, one could use `https://foo.com/builds/{repository.full_name}` which Bitbucket will turn into https://foo.com/builds/foo/bar at render time." + }, + "uuid": { + "type": "string", + "description": "The commit status' id." + } + }, + "additionalProperties": true + } + ], + "title": "Commit Status", + "description": "A commit status object." + }, + "component": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "links": { + "type": "object", + "properties": { + "self": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "name": { + "type": "string" + } + }, + "additionalProperties": true + } + ], + "title": "Component", + "description": "A component as defined in a repository's issue tracker." + }, + "ddev_report": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "additionalProperties": true, + "type": "object", + "description": "A report for a commit." + } + ], + "x-bb-default-fields": ["uuid", "commitHash"], + "x-bb-url": "/rest/2.0/accounts/{target_user.uuid}/repositories/{repository.uuid}/commits/{commitHash}/reports/{uuid}" + }, + "deploy_key": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "type": "object", + "properties": { + "added_on": { + "type": "string", + "format": "date-time" + }, + "comment": { + "type": "string", + "description": "The comment parsed from the deploy key (if present)" + }, + "key": { + "type": "string", + "description": "The deploy key value." + }, + "label": { + "type": "string", + "description": "The user-defined label for the deploy key" + }, + "last_used": { + "type": "string", + "format": "date-time" + }, + "links": { + "type": "object", + "properties": { + "self": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "owner": { + "$ref": "#/components/schemas/account" + }, + "repository": { + "$ref": "#/components/schemas/repository" + } + }, + "additionalProperties": true + } + ], + "title": "Deploy Key", + "description": "Represents deploy key for a repository." + }, + "deployment": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "additionalProperties": true, + "type": "object", + "properties": { + "environment": { + "$ref": "#/components/schemas/deployment_environment" + }, + "release": { + "$ref": "#/components/schemas/deployment_release" + }, + "state": { + "$ref": "#/components/schemas/deployment_state" + }, + "uuid": { + "type": "string", + "description": "The UUID identifying the deployment." + } + } + } + ], + "title": "Deployment", + "description": "A Bitbucket Deployment." + }, + "deployment_environment": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "additionalProperties": true, + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The name of the environment." + }, + "uuid": { + "type": "string", + "description": "The UUID identifying the environment." + } + } + } + ], + "x-bb-default-fields": ["uuid"], + "x-bb-url": "/rest/2.0/accounts/{target_user.uuid}/repositories/{repository.uuid}/environments/{uuid}", + "x-bb-batch-url": "/rest/2.0/accounts/{target_user.uuid}/repositories/{repository.uuid}/environments_batch", + "x-bb-batch-max-size": 100, + "title": "Deployment Environment", + "description": "A Bitbucket Deployment Environment." + }, + "deployment_environment_lock": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "additionalProperties": true, + "type": "object", + "properties": { + "environmentUuid": { + "type": "string", + "description": "The UUID identifying the environment." + } + } + } + ], + "x-bb-default-fields": ["*", "lock_opener.*", "owner.*"], + "x-bb-batch-url": "/rest/2.0/accounts/{target_user.uuid}/repositories/{repository.uuid}/environments/locks_batch", + "x-bb-batch-max-size": 100, + "title": "Deployment Environment Lock", + "description": "A Bitbucket Deployment Environment Lock." + }, + "deployment_release": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "additionalProperties": true, + "type": "object", + "properties": { + "commit": { + "$ref": "#/components/schemas/commit" + }, + "created_on": { + "type": "string", + "format": "date-time", + "description": "The timestamp when the release was created." + }, + "name": { + "type": "string", + "description": "The name of the release." + }, + "url": { + "type": "string", + "format": "uri", + "description": "Link to the pipeline that produced the release." + }, + "uuid": { + "type": "string", + "description": "The UUID identifying the release." + } + } + } + ], + "title": "Deployment Release", + "description": "A Bitbucket Deployment Release." + }, + "deployment_state": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "additionalProperties": true, + "type": "object", + "properties": {} + } + ], + "title": "Deployment State", + "description": "The representation of the progress state of a deployment." + }, + "deployment_state_completed": { + "allOf": [ + { + "$ref": "#/components/schemas/deployment_state" + }, + { + "additionalProperties": true, + "type": "object", + "description": "A Bitbucket Deployment COMPLETED deployment state.", + "properties": { + "completion_date": { + "type": "string", + "format": "date-time", + "description": "The timestamp when the deployment completed." + }, + "deployer": { + "$ref": "#/components/schemas/account" + }, + "name": { + "enum": ["COMPLETED"], + "type": "string", + "description": "The name of deployment state (COMPLETED)." + }, + "start_date": { + "type": "string", + "format": "date-time", + "description": "The timestamp when the deployment was started." + }, + "status": { + "$ref": "#/components/schemas/deployment_state_completed_status" + }, + "url": { + "type": "string", + "format": "uri", + "description": "Link to the deployment result." + } + } + } + ] + }, + "deployment_state_completed_status": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "additionalProperties": true, + "type": "object", + "properties": {} + } + ], + "title": "Completed Deployment", + "description": "The status of a completed deployment." + }, + "deployment_state_completed_status_failed": { + "allOf": [ + { + "$ref": "#/components/schemas/deployment_state_completed_status" + }, + { + "additionalProperties": true, + "type": "object", + "description": "A FAILED completed deployment status.", + "properties": { + "name": { + "enum": ["FAILED"], + "type": "string", + "description": "The name of the completed deployment status (FAILED)." + } + } + } + ] + }, + "deployment_state_completed_status_stopped": { + "allOf": [ + { + "$ref": "#/components/schemas/deployment_state_completed_status" + }, + { + "additionalProperties": true, + "type": "object", + "description": "A STOPPED completed deployment status.", + "properties": { + "name": { + "enum": ["STOPPED"], + "type": "string", + "description": "The name of the completed deployment status (STOPPED)." + } + } + } + ] + }, + "deployment_state_completed_status_successful": { + "allOf": [ + { + "$ref": "#/components/schemas/deployment_state_completed_status" + }, + { + "additionalProperties": true, + "type": "object", + "description": "A SUCCESSFUL completed deployment status.", + "properties": { + "name": { + "enum": ["SUCCESSFUL"], + "type": "string", + "description": "The name of the completed deployment status (SUCCESSFUL)." + } + } + } + ] + }, + "deployment_state_in_progress": { + "allOf": [ + { + "$ref": "#/components/schemas/deployment_state" + }, + { + "additionalProperties": true, + "type": "object", + "description": "A Bitbucket Deployment IN_PROGRESS deployment state.", + "properties": { + "deployer": { + "$ref": "#/components/schemas/account" + }, + "name": { + "enum": ["IN_PROGRESS"], + "type": "string", + "description": "The name of deployment state (IN_PROGRESS)." + }, + "start_date": { + "type": "string", + "format": "date-time", + "description": "The timestamp when the deployment was started." + }, + "url": { + "type": "string", + "format": "uri", + "description": "Link to the deployment result." + } + } + } + ] + }, + "deployment_state_undeployed": { + "allOf": [ + { + "$ref": "#/components/schemas/deployment_state" + }, + { + "additionalProperties": true, + "type": "object", + "description": "A Bitbucket Deployment UNDEPLOYED deployment state.", + "properties": { + "name": { + "enum": ["UNDEPLOYED"], + "type": "string", + "description": "The name of deployment state (UNDEPLOYED)." + }, + "trigger_url": { + "type": "string", + "format": "uri", + "description": "Link to trigger the deployment." + } + } + } + ] + }, + "deployment_variable": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "additionalProperties": true, + "type": "object", + "properties": { + "key": { + "type": "string", + "description": "The unique name of the variable." + }, + "secured": { + "type": "boolean", + "description": "If true, this variable will be treated as secured. The value will never be exposed in the logs or the REST API." + }, + "uuid": { + "type": "string", + "description": "The UUID identifying the variable." + }, + "value": { + "type": "string", + "description": "The value of the variable. If the variable is secured, this will be empty." + } + } + } + ], + "title": "Deployment Variable", + "description": "A Pipelines deployment variable." + }, + "deployments_ddev_deployment_environment": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "additionalProperties": true, + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The name of the environment." + }, + "uuid": { + "type": "string", + "description": "The UUID identifying the environment." + } + } + } + ], + "x-bb-default-fields": ["uuid"], + "x-bb-url": "/rest/2.0/accounts/{target_user.uuid}/repositories/{repository.uuid}/environments/{uuid}", + "x-bb-batch-url": "/rest/2.0/accounts/{target_user.uuid}/repositories/{repository.uuid}/environments_batch", + "x-bb-batch-max-size": 100, + "title": "Deployment Environment", + "description": "A Bitbucket Deployment Environment." + }, + "deployments_ddev_deployment_environment_lock": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "additionalProperties": true, + "type": "object", + "properties": { + "environmentUuid": { + "type": "string", + "description": "The UUID identifying the environment." + } + } + } + ], + "x-bb-default-fields": ["*", "lock_opener.*", "owner.*"], + "x-bb-batch-url": "/rest/2.0/accounts/{target_user.uuid}/repositories/{repository.uuid}/environments/locks_batch", + "x-bb-batch-max-size": 100, + "title": "Deployment Environment Lock", + "description": "A Bitbucket Deployment Environment Lock." + }, + "deployments_ddev_paginated_environments": { + "title": "Paginated Deployment Environments", + "description": "A paged list of environments", + "allOf": [ + { + "$ref": "#/components/schemas/paginated" + }, + { + "type": "object", + "properties": { + "values": { + "type": "array", + "minItems": 0, + "items": { + "$ref": "#/components/schemas/deployments_ddev_deployment_environment" + }, + "description": "The values of the current page." + } + } + } + ] + }, + "deployments_stg_west_deployment_environment": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "additionalProperties": true, + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The name of the environment." + }, + "uuid": { + "type": "string", + "description": "The UUID identifying the environment." + } + } + } + ], + "x-bb-default-fields": ["uuid"], + "x-bb-url": "/rest/2.0/accounts/{target_user.uuid}/repositories/{repository.uuid}/environments/{uuid}", + "x-bb-batch-url": "/rest/2.0/accounts/{target_user.uuid}/repositories/{repository.uuid}/environments_batch", + "x-bb-batch-max-size": 100, + "title": "Deployment Environment", + "description": "A Bitbucket Deployment Environment." + }, + "deployments_stg_west_deployment_environment_lock": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "additionalProperties": true, + "type": "object", + "properties": { + "environmentUuid": { + "type": "string", + "description": "The UUID identifying the environment." + } + } + } + ], + "x-bb-default-fields": ["*", "lock_opener.*", "owner.*"], + "x-bb-batch-url": "/rest/2.0/accounts/{target_user.uuid}/repositories/{repository.uuid}/environments/locks_batch", + "x-bb-batch-max-size": 100, + "title": "Deployment Environment Lock", + "description": "A Bitbucket Deployment Environment Lock." + }, + "deployments_stg_west_paginated_environments": { + "title": "Paginated Deployment Environments", + "description": "A paged list of environments", + "allOf": [ + { + "$ref": "#/components/schemas/paginated" + }, + { + "type": "object", + "properties": { + "values": { + "type": "array", + "minItems": 0, + "items": { + "$ref": "#/components/schemas/deployments_stg_west_deployment_environment" + }, + "description": "The values of the current page." + } + } + } + ] + }, + "diffstat": { + "type": "object", + "title": "Diff Stat", + "description": "A diffstat object that includes a summary of changes made to a file between two commits.", + "properties": { + "lines_added": { + "type": "integer" + }, + "lines_removed": { + "type": "integer" + }, + "new": { + "$ref": "#/components/schemas/commit_file" + }, + "old": { + "$ref": "#/components/schemas/commit_file" + }, + "status": { + "type": "string", + "enum": ["added", "removed", "modified", "renamed"] + }, + "type": { + "type": "string" + } + }, + "required": ["type"], + "additionalProperties": true + }, + "error": { + "type": "object", + "title": "Error", + "description": "Base type for most resource objects. It defines the common `type` element that identifies an object's type. It also identifies the element as Swagger's `discriminator`.", + "properties": { + "error": { + "type": "object", + "properties": { + "data": { + "type": "object", + "description": "Optional structured data that is endpoint-specific.", + "properties": {}, + "additionalProperties": true + }, + "detail": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "required": ["message"], + "additionalProperties": false + }, + "type": { + "type": "string" + } + }, + "required": ["type"], + "additionalProperties": true + }, + "export_options": { + "type": "object", + "title": "Export Options", + "description": "Options for issue export.", + "properties": { + "include_attachments": { + "type": "boolean" + }, + "project_key": { + "type": "string" + }, + "project_name": { + "type": "string" + }, + "send_email": { + "type": "boolean" + }, + "type": { + "type": "string" + } + }, + "required": ["type"], + "additionalProperties": true + }, + "group": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "type": "object", + "properties": { + "full_slug": { + "type": "string", + "description": "The concatenation of the workspace's slug and the group's slug,\nseparated with a colon (e.g. `acme:developers`)\n" + }, + "links": { + "type": "object", + "properties": { + "html": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + }, + "self": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "name": { + "type": "string" + }, + "owner": { + "$ref": "#/components/schemas/account" + }, + "slug": { + "type": "string", + "description": "The \"sluggified\" version of the group's name. This contains only ASCII\ncharacters and can therefore be slightly different than the name" + }, + "workspace": { + "$ref": "#/components/schemas/workspace" + } + }, + "additionalProperties": true + } + ], + "title": "Group", + "description": "A group object" + }, + "hook_event": { + "type": "object", + "title": "Hook Event", + "description": "An event, associated with a resource or subject type.", + "properties": { + "category": { + "type": "string", + "description": "The category this event belongs to." + }, + "description": { + "type": "string", + "description": "More detailed description of the webhook event type." + }, + "event": { + "type": "string", + "description": "The event identifier.", + "enum": [ + "pullrequest:unapproved", + "issue:comment_created", + "repo:imported", + "repo:created", + "repo:commit_comment_created", + "pullrequest:approved", + "pullrequest:comment_updated", + "issue:updated", + "project:updated", + "repo:deleted", + "pullrequest:changes_request_created", + "pullrequest:comment_created", + "repo:commit_status_updated", + "pullrequest:updated", + "issue:created", + "repo:fork", + "pullrequest:comment_deleted", + "repo:commit_status_created", + "repo:updated", + "pullrequest:rejected", + "pullrequest:fulfilled", + "pullrequest:created", + "pullrequest:changes_request_removed", + "repo:transfer", + "repo:push" + ] + }, + "label": { + "type": "string", + "description": "Summary of the webhook event type." + } + }, + "additionalProperties": false + }, + "issue": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "type": "object", + "properties": { + "assignee": { + "$ref": "#/components/schemas/user" + }, + "component": { + "$ref": "#/components/schemas/component" + }, + "content": { + "type": "object", + "properties": { + "html": { + "type": "string", + "description": "The user's content rendered as HTML." + }, + "markup": { + "type": "string", + "description": "The type of markup language the raw content is to be interpreted in.", + "enum": ["markdown", "creole", "plaintext"] + }, + "raw": { + "type": "string", + "description": "The text as it was typed by a user." + } + }, + "additionalProperties": false + }, + "created_on": { + "type": "string", + "format": "date-time" + }, + "edited_on": { + "type": "string", + "format": "date-time" + }, + "id": { + "type": "integer" + }, + "kind": { + "type": "string", + "enum": ["bug", "enhancement", "proposal", "task"] + }, + "links": { + "type": "object", + "properties": { + "attachments": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + }, + "comments": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + }, + "html": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + }, + "self": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + }, + "vote": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + }, + "watch": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "milestone": { + "$ref": "#/components/schemas/milestone" + }, + "priority": { + "type": "string", + "enum": ["trivial", "minor", "major", "critical", "blocker"] + }, + "reporter": { + "$ref": "#/components/schemas/user" + }, + "repository": { + "$ref": "#/components/schemas/repository" + }, + "state": { + "type": "string", + "enum": [ + "new", + "open", + "resolved", + "on hold", + "invalid", + "duplicate", + "wontfix", + "closed" + ] + }, + "title": { + "type": "string" + }, + "updated_on": { + "type": "string", + "format": "date-time" + }, + "version": { + "$ref": "#/components/schemas/version" + }, + "votes": { + "type": "integer" + } + }, + "additionalProperties": true + } + ], + "title": "Issue", + "description": "An issue." + }, + "issue_attachment": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "type": "object", + "properties": { + "links": { + "type": "object", + "properties": { + "self": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "name": { + "type": "string" + } + }, + "additionalProperties": true + } + ], + "title": "Issue Attachment", + "description": "An issue file attachment's meta data. Note this does not contain the file's actual contents." + }, + "issue_change": { + "type": "object", + "title": "Issue Change", + "description": "An issue change.", + "properties": { + "changes": { + "type": "object", + "properties": { + "assignee": { + "type": "object", + "properties": { + "new": { + "type": "string" + }, + "old": { + "type": "string" + } + }, + "additionalProperties": false + }, + "component": { + "type": "object", + "properties": { + "new": { + "type": "string" + }, + "old": { + "type": "string" + } + }, + "additionalProperties": false + }, + "content": { + "type": "object", + "properties": { + "new": { + "type": "string" + }, + "old": { + "type": "string" + } + }, + "additionalProperties": false + }, + "kind": { + "type": "object", + "properties": { + "new": { + "type": "string" + }, + "old": { + "type": "string" + } + }, + "additionalProperties": false + }, + "milestone": { + "type": "object", + "properties": { + "new": { + "type": "string" + }, + "old": { + "type": "string" + } + }, + "additionalProperties": false + }, + "priority": { + "type": "object", + "properties": { + "new": { + "type": "string" + }, + "old": { + "type": "string" + } + }, + "additionalProperties": false + }, + "state": { + "type": "object", + "properties": { + "new": { + "type": "string" + }, + "old": { + "type": "string" + } + }, + "additionalProperties": false + }, + "title": { + "type": "object", + "properties": { + "new": { + "type": "string" + }, + "old": { + "type": "string" + } + }, + "additionalProperties": false + }, + "version": { + "type": "object", + "properties": { + "new": { + "type": "string" + }, + "old": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "created_on": { + "type": "string", + "format": "date-time" + }, + "issue": { + "$ref": "#/components/schemas/issue" + }, + "links": { + "type": "object", + "properties": { + "issue": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + }, + "self": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "message": { + "type": "object", + "properties": { + "html": { + "type": "string", + "description": "The user's content rendered as HTML." + }, + "markup": { + "type": "string", + "description": "The type of markup language the raw content is to be interpreted in.", + "enum": ["markdown", "creole", "plaintext"] + }, + "raw": { + "type": "string", + "description": "The text as it was typed by a user." + } + }, + "additionalProperties": false + }, + "name": { + "type": "string" + }, + "type": { + "type": "string" + }, + "user": { + "$ref": "#/components/schemas/user" + } + }, + "required": ["type"], + "additionalProperties": true + }, + "issue_comment": { + "allOf": [ + { + "$ref": "#/components/schemas/comment" + }, + { + "type": "object", + "properties": { + "issue": { + "$ref": "#/components/schemas/issue" + } + }, + "additionalProperties": true + } + ], + "title": "Issue Comment", + "description": "A issue comment." + }, + "issue_job_status": { + "type": "object", + "title": "Issue Job Status", + "description": "The status of an import or export job", + "properties": { + "count": { + "type": "integer", + "description": "The total number of issues already imported/exported" + }, + "pct": { + "type": "number", + "description": "The percentage of issues already imported/exported", + "minimum": 0, + "maximum": 100 + }, + "phase": { + "type": "string", + "description": "The phase of the import/export job" + }, + "status": { + "type": "string", + "description": "The status of the import/export job", + "enum": ["ACCEPTED", "STARTED", "RUNNING", "FAILURE"] + }, + "total": { + "type": "integer", + "description": "The total number of issues being imported/exported" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + }, + "jira_project": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "additionalProperties": true, + "type": "object", + "description": "A Jira Project." + } + ], + "x-bb-default-fields": ["type", "cloudId", "id"], + "x-bb-detail-fields": ["key", "name", "url", "avatarUrls.*", "site"], + "x-bb-url": "/api/{target_user.uuid}/jira/sites/{cloudId}/projects/{id}?atlassian_account_id={user.account_id}" + }, + "jira_site": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "additionalProperties": true, + "type": "object", + "description": "A Jira Site." + } + ], + "x-bb-default-fields": ["type", "cloudId", "cloudUrl", "cloudName"], + "x-bb-detail-fields": ["connected"], + "x-bb-url": "/api/{target_user.uuid}/jira/sites/{cloudId}?atlassian_account_id={user.account_id}" + }, + "milestone": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "links": { + "type": "object", + "properties": { + "self": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "name": { + "type": "string" + } + }, + "additionalProperties": true + } + ], + "title": "Milestone", + "description": "A milestone as defined in a repository's issue tracker." + }, + "object": { + "type": "object", + "description": "Base type for most resource objects. It defines the common `type` element that identifies an object's type. It also identifies the element as Swagger's `discriminator`.", + "properties": { + "type": { + "type": "string" + } + }, + "required": ["type"], + "additionalProperties": true, + "discriminator": { + "propertyName": "type" + } + }, + "paginated": { + "type": "object", + "title": "Paginated", + "description": "A generic paginated list.", + "discriminator": { + "propertyName": "type" + }, + "properties": { + "next": { + "type": "string", + "format": "uri", + "description": "Link to the next page if it exists. The last page of a collection does not have this value. Use this link to navigate the result set and refrain from constructing your own URLs." + }, + "page": { + "type": "integer", + "description": "Page number of the current results. This is an optional element that is not provided in all responses." + }, + "pagelen": { + "type": "integer", + "description": "Current number of objects on the existing page. The default value is 10 with 100 being the maximum allowed value. Individual APIs may enforce different values." + }, + "previous": { + "type": "string", + "format": "uri", + "description": "Link to previous page if it exists. A collections first page does not have this value. This is an optional element that is not provided in all responses. Some result sets strictly support forward navigation and never provide previous links. Clients must anticipate that backwards navigation is not always available. Use this link to navigate the result set and refrain from constructing your own URLs." + }, + "size": { + "type": "integer", + "description": "Total number of objects in the response. This is an optional element that is not provided in all responses, as it can be expensive to compute." + }, + "values": { + "description": "The values of the current page.", + "oneOf": [ + { + "type": "array", + "minItems": 0, + "items": {}, + "uniqueItems": false + }, + { + "type": "array", + "minItems": 0, + "items": {}, + "uniqueItems": true + } + ] + } + } + }, + "paginated_annotations": { + "title": "Paginated Annotations", + "description": "A paginated list of annotations.", + "allOf": [ + { + "$ref": "#/components/schemas/paginated" + }, + { + "type": "object", + "properties": { + "values": { + "type": "array", + "minItems": 0, + "items": { + "$ref": "#/components/schemas/report_annotation" + }, + "description": "The values of the current page." + } + } + } + ] + }, + "paginated_branches": { + "title": "Paginated Branches", + "description": "A paginated list of branches.", + "allOf": [ + { + "$ref": "#/components/schemas/paginated" + }, + { + "type": "object", + "properties": { + "values": { + "type": "array", + "items": { + "$ref": "#/components/schemas/branch" + }, + "minItems": 0, + "uniqueItems": true, + "description": "The values of the current page." + } + } + } + ] + }, + "paginated_branchrestrictions": { + "title": "Paginated Branch Restrictions", + "description": "A paginated list of branch restriction rules.", + "allOf": [ + { + "$ref": "#/components/schemas/paginated" + }, + { + "type": "object", + "properties": { + "values": { + "type": "array", + "items": { + "$ref": "#/components/schemas/branchrestriction" + }, + "minItems": 0, + "uniqueItems": true, + "description": "The values of the current page." + } + } + } + ] + }, + "paginated_changeset": { + "title": "Page", + "description": "A paginated list of commits.", + "allOf": [ + { + "$ref": "#/components/schemas/paginated" + }, + { + "type": "object", + "properties": { + "values": { + "type": "array", + "items": { + "$ref": "#/components/schemas/base_commit" + }, + "minItems": 0, + "uniqueItems": true, + "description": "The values of the current page." + } + } + } + ] + }, + "paginated_commit_comments": { + "title": "Paginated Commit Comments", + "description": "A paginated list of commit comments.", + "allOf": [ + { + "$ref": "#/components/schemas/paginated" + }, + { + "type": "object", + "properties": { + "values": { + "type": "array", + "items": { + "$ref": "#/components/schemas/commit_comment" + }, + "minItems": 0, + "uniqueItems": true, + "description": "The values of the current page." + } + } + } + ] + }, + "paginated_commitstatuses": { + "title": "Paginated Commit Statuses", + "description": "A paginated list of commit status objects.", + "allOf": [ + { + "$ref": "#/components/schemas/paginated" + }, + { + "type": "object", + "properties": { + "values": { + "type": "array", + "items": { + "$ref": "#/components/schemas/commitstatus" + }, + "minItems": 0, + "uniqueItems": true, + "description": "The values of the current page." + } + } + } + ] + }, + "paginated_components": { + "title": "Paginated Components", + "description": "A paginated list of issue tracker components.", + "allOf": [ + { + "$ref": "#/components/schemas/paginated" + }, + { + "type": "object", + "properties": { + "values": { + "type": "array", + "items": { + "$ref": "#/components/schemas/component" + }, + "minItems": 0, + "uniqueItems": true, + "description": "The values of the current page." + } + } + } + ] + }, + "paginated_deploy_keys": { + "title": "Paginated Deploy Keys", + "description": "A paginated list of deploy keys.", + "allOf": [ + { + "$ref": "#/components/schemas/paginated" + }, + { + "type": "object", + "properties": { + "values": { + "type": "array", + "items": { + "$ref": "#/components/schemas/deploy_key" + }, + "minItems": 0, + "uniqueItems": true, + "description": "The values of the current page." + } + } + } + ] + }, + "paginated_deployment_variable": { + "title": "Paginated Deployment Variables", + "description": "A paged list of deployment variables.", + "allOf": [ + { + "$ref": "#/components/schemas/paginated" + }, + { + "type": "object", + "properties": { + "values": { + "type": "array", + "minItems": 0, + "items": { + "$ref": "#/components/schemas/deployment_variable" + }, + "description": "The values of the current page." + } + } + } + ] + }, + "paginated_deployments": { + "title": "Paginated Deployments", + "description": "A paged list of deployments", + "allOf": [ + { + "$ref": "#/components/schemas/paginated" + }, + { + "type": "object", + "properties": { + "values": { + "type": "array", + "minItems": 0, + "items": { + "$ref": "#/components/schemas/deployment" + }, + "description": "The values of the current page." + } + } + } + ] + }, + "paginated_diffstats": { + "title": "Paginated Diff Stat", + "description": "A paginated list of diffstats.", + "allOf": [ + { + "$ref": "#/components/schemas/paginated" + }, + { + "type": "object", + "properties": { + "values": { + "type": "array", + "items": { + "$ref": "#/components/schemas/diffstat" + }, + "minItems": 0, + "uniqueItems": true, + "description": "The values of the current page." + } + } + } + ] + }, + "paginated_environments": { + "title": "Paginated Deployment Environments", + "description": "A paged list of environments", + "allOf": [ + { + "$ref": "#/components/schemas/paginated" + }, + { + "type": "object", + "properties": { + "values": { + "type": "array", + "minItems": 0, + "items": { + "$ref": "#/components/schemas/deployment_environment" + }, + "description": "The values of the current page." + } + } + } + ] + }, + "paginated_files": { + "title": "Paginated Files", + "description": "A paginated list of commit_file objects.", + "allOf": [ + { + "$ref": "#/components/schemas/paginated" + }, + { + "type": "object", + "properties": { + "values": { + "type": "array", + "items": { + "$ref": "#/components/schemas/commit_file" + }, + "minItems": 0, + "uniqueItems": true, + "description": "The values of the current page." + } + } + } + ] + }, + "paginated_hook_events": { + "title": "Paginated Hook Events", + "description": "A paginated list of webhook types available to subscribe on.", + "allOf": [ + { + "$ref": "#/components/schemas/paginated" + }, + { + "type": "object", + "properties": { + "values": { + "type": "array", + "items": { + "$ref": "#/components/schemas/hook_event" + }, + "minItems": 0, + "uniqueItems": true, + "description": "The values of the current page." + } + } + } + ] + }, + "paginated_issue_attachments": { + "title": "Paginated Issue Attachment", + "description": "A paginated list of issue attachments.", + "allOf": [ + { + "$ref": "#/components/schemas/paginated" + }, + { + "type": "object", + "properties": { + "values": { + "type": "array", + "items": { + "$ref": "#/components/schemas/issue_attachment" + }, + "minItems": 0, + "description": "The values of the current page." + } + } + } + ] + }, + "paginated_issue_comments": { + "title": "Paginated Issue Comments", + "description": "A paginated list of issue comments.", + "allOf": [ + { + "$ref": "#/components/schemas/paginated" + }, + { + "type": "object", + "properties": { + "values": { + "type": "array", + "items": { + "$ref": "#/components/schemas/issue_comment" + }, + "minItems": 0, + "uniqueItems": true, + "description": "The values of the current page." + } + } + } + ] + }, + "paginated_issues": { + "title": "Paginated Issues", + "description": "A paginated list of issues.", + "allOf": [ + { + "$ref": "#/components/schemas/paginated" + }, + { + "type": "object", + "properties": { + "values": { + "type": "array", + "items": { + "$ref": "#/components/schemas/issue" + }, + "minItems": 0, + "uniqueItems": true, + "description": "The values of the current page." + } + } + } + ] + }, + "paginated_log_entries": { + "title": "Paginated Log Entries", + "description": "A paginated list of issue changes.", + "allOf": [ + { + "$ref": "#/components/schemas/paginated" + }, + { + "type": "object", + "properties": { + "values": { + "type": "array", + "items": { + "$ref": "#/components/schemas/issue_change" + }, + "minItems": 0, + "description": "The values of the current page." + } + } + } + ] + }, + "paginated_milestones": { + "title": "Paginated Milestones", + "description": "A paginated list of issue tracker milestones.", + "allOf": [ + { + "$ref": "#/components/schemas/paginated" + }, + { + "type": "object", + "properties": { + "values": { + "type": "array", + "items": { + "$ref": "#/components/schemas/milestone" + }, + "minItems": 0, + "uniqueItems": true, + "description": "The values of the current page." + } + } + } + ] + }, + "paginated_pipeline_caches": { + "title": "Paginated Pipeline Cache", + "description": "A paged list of pipeline caches", + "allOf": [ + { + "$ref": "#/components/schemas/paginated" + }, + { + "type": "object", + "properties": { + "values": { + "type": "array", + "minItems": 0, + "items": { + "$ref": "#/components/schemas/pipeline_cache" + }, + "description": "The values of the current page." + } + } + } + ] + }, + "paginated_pipeline_known_hosts": { + "title": "Paginated Pipeline Known Hosts", + "description": "A paged list of known hosts.", + "allOf": [ + { + "$ref": "#/components/schemas/paginated" + }, + { + "type": "object", + "properties": { + "values": { + "type": "array", + "minItems": 0, + "items": { + "$ref": "#/components/schemas/pipeline_known_host" + }, + "description": "The values of the current page." + } + } + } + ] + }, + "paginated_pipeline_schedule_executions": { + "title": "Paginated Pipeline Schedule Executions", + "description": "A paged list of the executions of a schedule.", + "allOf": [ + { + "$ref": "#/components/schemas/paginated" + }, + { + "type": "object", + "properties": { + "values": { + "type": "array", + "minItems": 0, + "items": { + "$ref": "#/components/schemas/pipeline_schedule_execution" + }, + "description": "The values of the current page." + } + } + } + ] + }, + "paginated_pipeline_schedules": { + "title": "Paginated Pipeline Schedule", + "description": "A paged list of schedules", + "allOf": [ + { + "$ref": "#/components/schemas/paginated" + }, + { + "type": "object", + "properties": { + "values": { + "type": "array", + "minItems": 0, + "items": { + "$ref": "#/components/schemas/pipeline_schedule" + }, + "description": "The values of the current page." + } + } + } + ] + }, + "paginated_pipeline_steps": { + "title": "Paginated Pipeline Steps", + "description": "A paged list of pipeline steps.", + "allOf": [ + { + "$ref": "#/components/schemas/paginated" + }, + { + "type": "object", + "properties": { + "values": { + "type": "array", + "minItems": 0, + "items": { + "$ref": "#/components/schemas/pipeline_step" + }, + "description": "The values of the current page." + } + } + } + ] + }, + "paginated_pipeline_variables": { + "title": "Paginated Pipeline Variables", + "description": "A paged list of variables.", + "allOf": [ + { + "$ref": "#/components/schemas/paginated" + }, + { + "type": "object", + "properties": { + "values": { + "type": "array", + "minItems": 0, + "items": { + "$ref": "#/components/schemas/pipeline_variable" + }, + "description": "The values of the current page." + } + } + } + ] + }, + "paginated_pipelines": { + "title": "Paginated Pipelines", + "description": "A paged list of pipelines", + "allOf": [ + { + "$ref": "#/components/schemas/paginated" + }, + { + "type": "object", + "properties": { + "values": { + "type": "array", + "minItems": 0, + "items": { + "$ref": "#/components/schemas/pipeline" + }, + "description": "The values of the current page." + } + } + } + ] + }, + "paginated_projects": { + "title": "Paginated Projects", + "description": "A paginated list of projects", + "allOf": [ + { + "$ref": "#/components/schemas/paginated" + }, + { + "type": "object", + "properties": { + "values": { + "type": "array", + "items": { + "$ref": "#/components/schemas/project" + }, + "minItems": 0, + "uniqueItems": true, + "description": "The values of the current page." + } + } + } + ] + }, + "paginated_pullrequest_comments": { + "title": "Paginated Pull Request Comments", + "description": "A paginated list of pullrequest comments.", + "allOf": [ + { + "$ref": "#/components/schemas/paginated" + }, + { + "type": "object", + "properties": { + "values": { + "type": "array", + "items": { + "$ref": "#/components/schemas/pullrequest_comment" + }, + "minItems": 0, + "uniqueItems": true, + "description": "The values of the current page." + } + } + } + ] + }, + "paginated_pullrequests": { + "title": "Paginated Pull Requests", + "description": "A paginated list of pullrequests.", + "allOf": [ + { + "$ref": "#/components/schemas/paginated" + }, + { + "type": "object", + "properties": { + "values": { + "type": "array", + "items": { + "$ref": "#/components/schemas/pullrequest" + }, + "minItems": 0, + "uniqueItems": true, + "description": "The values of the current page." + } + } + } + ] + }, + "paginated_refs": { + "title": "Paginated Refs", + "description": "A paginated list of refs.", + "allOf": [ + { + "$ref": "#/components/schemas/paginated" + }, + { + "type": "object", + "properties": { + "values": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ref" + }, + "minItems": 0, + "uniqueItems": true, + "description": "The values of the current page." + } + } + } + ] + }, + "paginated_reports": { + "title": "Paginated Reports", + "description": "A paginated list of reports.", + "allOf": [ + { + "$ref": "#/components/schemas/paginated" + }, + { + "type": "object", + "properties": { + "values": { + "type": "array", + "minItems": 0, + "items": { + "$ref": "#/components/schemas/report" + }, + "description": "The values of the current page." + } + } + } + ] + }, + "paginated_repositories": { + "title": "Paginated Repositories", + "description": "A paginated list of repositories.", + "allOf": [ + { + "$ref": "#/components/schemas/paginated" + }, + { + "type": "object", + "properties": { + "values": { + "type": "array", + "items": { + "$ref": "#/components/schemas/repository" + }, + "minItems": 0, + "uniqueItems": true, + "description": "The values of the current page." + } + } + } + ] + }, + "paginated_repository_group_permissions": { + "title": "Paginated Repository Group Permissions", + "description": "A paginated list of repository group permissions.", + "allOf": [ + { + "$ref": "#/components/schemas/paginated" + }, + { + "type": "object", + "properties": { + "values": { + "type": "array", + "items": { + "$ref": "#/components/schemas/repository_group_permission" + }, + "minItems": 0, + "uniqueItems": true, + "description": "The values of the current page." + } + } + } + ] + }, + "paginated_repository_permissions": { + "title": "Paginated Repository Permissions", + "description": "A paginated list of repository permissions.", + "allOf": [ + { + "$ref": "#/components/schemas/paginated" + }, + { + "type": "object", + "properties": { + "values": { + "type": "array", + "items": { + "$ref": "#/components/schemas/repository_permission" + }, + "minItems": 0, + "uniqueItems": true, + "description": "The values of the current page." + } + } + } + ] + }, + "paginated_repository_user_permissions": { + "title": "Paginated Repository User Permissions", + "description": "A paginated list of repository user permissions.", + "allOf": [ + { + "$ref": "#/components/schemas/paginated" + }, + { + "type": "object", + "properties": { + "values": { + "type": "array", + "items": { + "$ref": "#/components/schemas/repository_user_permission" + }, + "minItems": 0, + "uniqueItems": true, + "description": "The values of the current page." + } + } + } + ] + }, + "paginated_snippet_comments": { + "title": "Paginated Snippet Comments", + "description": "A paginated list of snippet comments.", + "allOf": [ + { + "$ref": "#/components/schemas/paginated" + }, + { + "type": "object", + "properties": { + "values": { + "type": "array", + "items": { + "$ref": "#/components/schemas/snippet_comment" + }, + "minItems": 0, + "uniqueItems": true, + "description": "The values of the current page." + } + } + } + ] + }, + "paginated_snippet_commits": { + "title": "Paginated Snippet Commits", + "description": "A paginated list of snippet commits.", + "allOf": [ + { + "$ref": "#/components/schemas/paginated" + }, + { + "type": "object", + "properties": { + "values": { + "type": "array", + "items": { + "$ref": "#/components/schemas/snippet_commit" + }, + "minItems": 0, + "description": "The values of the current page." + } + } + } + ] + }, + "paginated_snippets": { + "title": "Paginated Snippets", + "description": "A paginated list of snippets.", + "allOf": [ + { + "$ref": "#/components/schemas/paginated" + }, + { + "type": "object", + "properties": { + "values": { + "type": "array", + "items": { + "$ref": "#/components/schemas/snippet" + }, + "minItems": 0, + "uniqueItems": true, + "description": "The values of the current page." + } + } + } + ] + }, + "paginated_ssh_user_keys": { + "title": "Paginated SSH User Keys", + "description": "A paginated list of SSH keys.", + "allOf": [ + { + "$ref": "#/components/schemas/paginated" + }, + { + "type": "object", + "properties": { + "values": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ssh_account_key" + }, + "minItems": 0, + "uniqueItems": true, + "description": "The values of the current page." + } + } + } + ] + }, + "paginated_tags": { + "title": "Paginated Tags", + "description": "A paginated list of tags.", + "allOf": [ + { + "$ref": "#/components/schemas/paginated" + }, + { + "type": "object", + "properties": { + "values": { + "type": "array", + "items": { + "$ref": "#/components/schemas/tag" + }, + "minItems": 0, + "uniqueItems": true, + "description": "The values of the current page." + } + } + } + ] + }, + "paginated_team_permissions": { + "title": "Paginated Team Permissions", + "description": "A paginated list of team permissions.", + "allOf": [ + { + "$ref": "#/components/schemas/paginated" + }, + { + "type": "object", + "properties": { + "values": { + "type": "array", + "items": { + "$ref": "#/components/schemas/team_permission" + }, + "minItems": 0, + "uniqueItems": true, + "description": "The values of the current page." + } + } + } + ] + }, + "paginated_teams": { + "title": "Paginated Teams", + "description": "A paginated list of teams.", + "allOf": [ + { + "$ref": "#/components/schemas/paginated" + }, + { + "type": "object", + "properties": { + "values": { + "type": "array", + "items": { + "$ref": "#/components/schemas/team" + }, + "minItems": 0, + "uniqueItems": true, + "description": "The values of the current page." + } + } + } + ] + }, + "paginated_treeentries": { + "title": "Paginated Tree Entry", + "description": "A paginated list of commit_file and/or commit_directory objects.", + "allOf": [ + { + "$ref": "#/components/schemas/paginated" + }, + { + "type": "object", + "properties": { + "values": { + "type": "array", + "items": { + "$ref": "#/components/schemas/treeentry" + }, + "minItems": 0, + "uniqueItems": true, + "description": "The values of the current page." + } + } + } + ] + }, + "paginated_users": { + "title": "Paginated Users", + "description": "A paginated list of users.", + "allOf": [ + { + "$ref": "#/components/schemas/paginated" + }, + { + "type": "object", + "properties": { + "values": { + "type": "array", + "items": { + "$ref": "#/components/schemas/user" + }, + "minItems": 0, + "uniqueItems": true, + "description": "The values of the current page." + } + } + } + ] + }, + "paginated_versions": { + "title": "Paginated Versions", + "description": "A paginated list of issue tracker versions.", + "allOf": [ + { + "$ref": "#/components/schemas/paginated" + }, + { + "type": "object", + "properties": { + "values": { + "type": "array", + "items": { + "$ref": "#/components/schemas/version" + }, + "minItems": 0, + "uniqueItems": true, + "description": "The values of the current page." + } + } + } + ] + }, + "paginated_webhook_subscriptions": { + "title": "Paginated Webhook Subscriptions", + "description": "A paginated list of webhook subscriptions", + "allOf": [ + { + "$ref": "#/components/schemas/paginated" + }, + { + "type": "object", + "properties": { + "values": { + "type": "array", + "items": { + "$ref": "#/components/schemas/webhook_subscription" + }, + "minItems": 0, + "uniqueItems": true, + "description": "The values of the current page." + } + } + } + ] + }, + "paginated_workspace_memberships": { + "title": "Paginated Workspace Memberships", + "description": "A paginated list of workspace memberships.", + "allOf": [ + { + "$ref": "#/components/schemas/paginated" + }, + { + "type": "object", + "properties": { + "values": { + "type": "array", + "items": { + "$ref": "#/components/schemas/workspace_membership" + }, + "minItems": 0, + "uniqueItems": true, + "description": "The values of the current page." + } + } + } + ] + }, + "paginated_workspaces": { + "title": "Paginated Workspaces", + "description": "A paginated list of workspaces.", + "allOf": [ + { + "$ref": "#/components/schemas/paginated" + }, + { + "type": "object", + "properties": { + "values": { + "type": "array", + "items": { + "$ref": "#/components/schemas/workspace" + }, + "minItems": 0, + "uniqueItems": true, + "description": "The values of the current page." + } + } + } + ] + }, + "participant": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "type": "object", + "properties": { + "approved": { + "type": "boolean" + }, + "participated_on": { + "type": "string", + "description": "The ISO8601 timestamp of the participant's action. For approvers, this is the time of their approval. For commenters and pull request reviewers who are not approvers, this is the time they last commented, or null if they have not commented.", + "format": "date-time" + }, + "role": { + "type": "string", + "enum": ["PARTICIPANT", "REVIEWER"] + }, + "state": { + "type": "string", + "enum": ["approved", "changes_requested", null] + }, + "user": { + "$ref": "#/components/schemas/user" + } + }, + "additionalProperties": true + } + ], + "title": "Participant", + "description": "Object describing a user's role on resources like commits or pull requests." + }, + "pipeline": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "additionalProperties": true, + "type": "object", + "properties": { + "build_number": { + "type": "integer", + "description": "The build number of the pipeline." + }, + "build_seconds_used": { + "type": "integer", + "description": "The number of build seconds used by this pipeline." + }, + "completed_on": { + "type": "string", + "format": "date-time", + "description": "The timestamp when the Pipeline was completed. This is not set if the pipeline is still in progress." + }, + "created_on": { + "type": "string", + "format": "date-time", + "description": "The timestamp when the pipeline was created." + }, + "creator": { + "$ref": "#/components/schemas/account" + }, + "repository": { + "$ref": "#/components/schemas/repository" + }, + "state": { + "$ref": "#/components/schemas/pipeline_state" + }, + "target": { + "$ref": "#/components/schemas/pipeline_target" + }, + "trigger": { + "$ref": "#/components/schemas/pipeline_trigger" + }, + "uuid": { + "type": "string", + "description": "The UUID identifying the pipeline." + } + } + } + ], + "title": "Pipeline", + "description": "A Bitbucket Pipeline. This represents an actual pipeline result." + }, + "pipeline_build_number": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "additionalProperties": true, + "type": "object", + "properties": { + "next": { + "type": "integer", + "description": "The next number that will be used as build number." + } + } + } + ], + "title": "Pipeline Build Number", + "description": "A Pipelines build number." + }, + "pipeline_cache": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "additionalProperties": true, + "type": "object", + "properties": { + "created_on": { + "type": "string", + "format": "date-time", + "description": "The timestamp when the cache was created." + }, + "file_size_bytes": { + "type": "integer", + "description": "The size of the file containing the archive of the cache." + }, + "name": { + "type": "string", + "description": "The name of the cache." + }, + "path": { + "type": "string", + "description": "The path where the cache contents were retrieved from." + }, + "pipeline_uuid": { + "type": "string", + "description": "The UUID of the pipeline that created the cache." + }, + "step_uuid": { + "type": "string", + "description": "The uuid of the step that created the cache." + }, + "uuid": { + "type": "string", + "description": "The UUID identifying the pipeline cache." + } + } + } + ], + "title": "Pipeline Cache", + "description": "A representation of metadata for a pipeline cache for given repository." + }, + "pipeline_cache_content_uri": { + "type": "object", + "title": "Pipeline Cache Content URI", + "description": "A representation of the location of pipeline cache content.", + "properties": { + "uri": { + "type": "string", + "format": "uri", + "description": "The uri for pipeline cache content." + } + } + }, + "pipeline_command": { + "type": "object", + "title": "Pipeline Command", + "description": "An executable pipeline command.", + "properties": { + "command": { + "type": "string", + "description": "The executable command." + }, + "name": { + "type": "string", + "description": "The name of the command." + } + } + }, + "pipeline_commit_target": { + "allOf": [ + { + "$ref": "#/components/schemas/pipeline_target" + }, + { + "additionalProperties": true, + "type": "object", + "properties": { + "commit": { + "$ref": "#/components/schemas/commit" + }, + "selector": { + "$ref": "#/components/schemas/pipeline_selector" + } + } + } + ], + "title": "Pipeline Commit Target", + "description": "A Bitbucket Pipelines commit target." + }, + "pipeline_error": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "additionalProperties": true, + "type": "object", + "properties": { + "key": { + "type": "string", + "description": "The error key." + }, + "message": { + "type": "string", + "description": "The error message." + } + } + } + ], + "title": "Pipeline Error", + "description": "An error causing a pipeline failure." + }, + "pipeline_image": { + "type": "object", + "title": "Pipeline Image", + "description": "The definition of a Docker image that can be used for a Bitbucket Pipelines step execution context.", + "properties": { + "email": { + "type": "string", + "description": "The email needed to authenticate with the Docker registry. Only required when using a private Docker image." + }, + "name": { + "type": "string", + "description": "The name of the image. If the image is hosted on DockerHub the short name can be used, otherwise the fully qualified name is required here." + }, + "password": { + "type": "string", + "description": "The password needed to authenticate with the Docker registry. Only required when using a private Docker image." + }, + "username": { + "type": "string", + "description": "The username needed to authenticate with the Docker registry. Only required when using a private Docker image." + } + } + }, + "pipeline_known_host": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "additionalProperties": true, + "type": "object", + "properties": { + "hostname": { + "type": "string", + "description": "The hostname of the known host." + }, + "public_key": { + "$ref": "#/components/schemas/pipeline_ssh_public_key" + }, + "uuid": { + "type": "string", + "description": "The UUID identifying the known host." + } + } + } + ], + "title": "Pipeline Known Host", + "description": "A Pipelines known host." + }, + "pipeline_ref_target": { + "allOf": [ + { + "$ref": "#/components/schemas/pipeline_target" + }, + { + "additionalProperties": true, + "type": "object", + "properties": { + "commit": { + "$ref": "#/components/schemas/commit" + }, + "ref_name": { + "type": "string", + "description": "The name of the reference." + }, + "ref_type": { + "enum": ["branch", "tag", "named_branch", "bookmark"], + "type": "string", + "description": "The type of reference (branch/tag)." + }, + "selector": { + "$ref": "#/components/schemas/pipeline_selector" + } + } + } + ], + "title": "Pipeline Ref Target", + "description": "A Bitbucket Pipelines reference target." + }, + "pipeline_schedule": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "additionalProperties": true, + "type": "object", + "properties": { + "created_on": { + "type": "string", + "format": "date-time", + "description": "The timestamp when the schedule was created." + }, + "cron_pattern": { + "type": "string", + "description": "The cron expression that the schedule applies." + }, + "enabled": { + "type": "boolean", + "description": "Whether the schedule is enabled." + }, + "selector": { + "$ref": "#/components/schemas/pipeline_selector" + }, + "target": { + "$ref": "#/components/schemas/pipeline_target" + }, + "updated_on": { + "type": "string", + "format": "date-time", + "description": "The timestamp when the schedule was updated." + }, + "uuid": { + "type": "string", + "description": "The UUID identifying the schedule." + } + } + } + ], + "title": "Pipeline Schedule", + "description": "A Pipelines schedule." + }, + "pipeline_schedule_execution": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "additionalProperties": true, + "type": "object", + "properties": {} + } + ], + "title": "Pipeline Schedule Execution", + "description": "A Pipelines schedule execution." + }, + "pipeline_schedule_execution_errored": { + "allOf": [ + { + "$ref": "#/components/schemas/pipeline_schedule_execution" + }, + { + "additionalProperties": true, + "type": "object", + "description": "A Pipelines schedule execution that failed to be executed.", + "properties": { + "error": { + "$ref": "#/components/schemas/pipeline_error" + } + } + } + ] + }, + "pipeline_schedule_execution_executed": { + "allOf": [ + { + "$ref": "#/components/schemas/pipeline_schedule_execution" + }, + { + "additionalProperties": true, + "type": "object", + "description": "A Pipelines executed schedule execution.", + "properties": { + "pipeline": { + "$ref": "#/components/schemas/pipeline" + } + } + } + ] + }, + "pipeline_selector": { + "title": "Pipeline Selector", + "description": "A representation of the selector that was used to identify the pipeline in the YML file.", + "additionalProperties": true, + "type": "object", + "properties": { + "pattern": { + "type": "string", + "description": "The name of the matching pipeline definition." + }, + "type": { + "enum": ["branches", "tags", "bookmarks", "default", "custom"], + "type": "string", + "description": "The type of selector." + } + } + }, + "pipeline_ssh_key_pair": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "additionalProperties": true, + "type": "object", + "properties": { + "private_key": { + "type": "string", + "description": "The SSH private key. This value will be empty when retrieving the SSH key pair." + }, + "public_key": { + "type": "string", + "description": "The SSH public key." + } + } + } + ], + "title": "Pipeline SSH Key Pair", + "description": "A Pipelines SSH key pair." + }, + "pipeline_ssh_public_key": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "additionalProperties": true, + "type": "object", + "properties": { + "key": { + "type": "string", + "description": "The base64 encoded public key." + }, + "key_type": { + "type": "string", + "description": "The type of the public key." + }, + "md5_fingerprint": { + "type": "string", + "description": "The MD5 fingerprint of the public key." + }, + "sha256_fingerprint": { + "type": "string", + "description": "The SHA-256 fingerprint of the public key." + } + } + } + ], + "title": "Pipeline SSH Public Key", + "description": "A Pipelines known host public key." + }, + "pipeline_state": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "additionalProperties": true, + "type": "object", + "properties": {} + } + ], + "title": "Pipeline State", + "description": "The representation of the progress state of a pipeline." + }, + "pipeline_state_completed": { + "allOf": [ + { + "$ref": "#/components/schemas/pipeline_state" + }, + { + "additionalProperties": true, + "type": "object", + "properties": { + "name": { + "enum": ["COMPLETED"], + "type": "string", + "description": "The name of pipeline state (COMPLETED)." + }, + "result": { + "$ref": "#/components/schemas/pipeline_state_completed_result" + } + } + } + ], + "title": "Pipeline Completed State", + "description": "A Bitbucket Pipelines COMPLETED pipeline state." + }, + "pipeline_state_completed_error": { + "allOf": [ + { + "$ref": "#/components/schemas/pipeline_state_completed_result" + }, + { + "additionalProperties": true, + "type": "object", + "properties": { + "error": { + "$ref": "#/components/schemas/pipeline_error" + }, + "name": { + "enum": ["ERROR"], + "type": "string", + "description": "The name of the result (ERROR)" + } + } + } + ], + "title": "Pipeline Completed Error", + "description": "A Bitbucket Pipelines ERROR pipeline result." + }, + "pipeline_state_completed_expired": { + "allOf": [ + { + "$ref": "#/components/schemas/pipeline_state_completed_result" + }, + { + "additionalProperties": true, + "type": "object", + "description": "A Bitbucket Pipelines EXPIRED pipeline result.", + "properties": { + "name": { + "enum": ["EXPIRED"], + "type": "string", + "description": "The name of the stopped result (EXPIRED)." + } + } + } + ] + }, + "pipeline_state_completed_failed": { + "allOf": [ + { + "$ref": "#/components/schemas/pipeline_state_completed_result" + }, + { + "additionalProperties": true, + "type": "object", + "description": "A Bitbucket Pipelines FAILED pipeline result.", + "properties": { + "name": { + "enum": ["FAILED"], + "type": "string", + "description": "The name of the failed result (FAILED)." + } + } + } + ] + }, + "pipeline_state_completed_result": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "additionalProperties": true, + "type": "object", + "properties": {} + } + ], + "title": "Pipeline Completed Result", + "description": "A result of a completed pipeline state." + }, + "pipeline_state_completed_stopped": { + "allOf": [ + { + "$ref": "#/components/schemas/pipeline_state_completed_result" + }, + { + "additionalProperties": true, + "type": "object", + "description": "A Bitbucket Pipelines STOPPED pipeline result.", + "properties": { + "name": { + "enum": ["STOPPED"], + "type": "string", + "description": "The name of the stopped result (STOPPED)." + } + } + } + ] + }, + "pipeline_state_completed_successful": { + "allOf": [ + { + "$ref": "#/components/schemas/pipeline_state_completed_result" + }, + { + "additionalProperties": true, + "type": "object", + "description": "A Bitbucket Pipelines SUCCESSFUL pipeline result.", + "properties": { + "name": { + "enum": ["SUCCESSFUL"], + "type": "string", + "description": "The name of the successful result (SUCCESSFUL)." + } + } + } + ] + }, + "pipeline_state_in_progress": { + "allOf": [ + { + "$ref": "#/components/schemas/pipeline_state" + }, + { + "additionalProperties": true, + "type": "object", + "properties": { + "name": { + "enum": ["IN_PROGRESS"], + "type": "string", + "description": "The name of pipeline state (IN_PROGRESS)." + }, + "stage": { + "$ref": "#/components/schemas/pipeline_state_in_progress_stage" + } + } + } + ], + "title": "Pipeline In-Progress State", + "description": "A Bitbucket Pipelines IN_PROGRESS pipeline state." + }, + "pipeline_state_in_progress_paused": { + "allOf": [ + { + "$ref": "#/components/schemas/pipeline_state_in_progress_stage" + }, + { + "additionalProperties": true, + "type": "object", + "description": "A Bitbucket Pipelines PAUSED stage of a pipeline that is in progress.", + "properties": { + "name": { + "enum": ["PAUSED"], + "type": "string", + "description": "The name of the stage (PAUSED)" + } + } + } + ] + }, + "pipeline_state_in_progress_running": { + "allOf": [ + { + "$ref": "#/components/schemas/pipeline_state_in_progress_stage" + }, + { + "additionalProperties": true, + "type": "object", + "description": "A Bitbucket Pipelines RUNNING stage of a pipeline that is in progress.", + "properties": { + "name": { + "enum": ["RUNNING"], + "type": "string", + "description": "The name of the stage (RUNNING)" + } + } + } + ] + }, + "pipeline_state_in_progress_stage": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "additionalProperties": true, + "type": "object", + "properties": {} + } + ], + "title": "Pipeline In-Progress Stage", + "description": "A result of an in progress pipeline state." + }, + "pipeline_state_pending": { + "allOf": [ + { + "$ref": "#/components/schemas/pipeline_state" + }, + { + "additionalProperties": true, + "type": "object", + "properties": { + "name": { + "enum": ["PENDING"], + "type": "string", + "description": "The name of pipeline state (PENDING)." + } + } + } + ], + "title": "Pipeline Pending State", + "description": "A Bitbucket Pipelines PENDING pipeline state." + }, + "pipeline_step": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "additionalProperties": true, + "type": "object", + "properties": { + "completed_on": { + "type": "string", + "format": "date-time", + "description": "The timestamp when the step execution was completed. This is not set if the step is still in progress." + }, + "image": { + "$ref": "#/components/schemas/pipeline_image" + }, + "script_commands": { + "type": "array", + "items": { + "$ref": "#/components/schemas/pipeline_command" + }, + "description": "The list of build commands. These commands are executed in the build container." + }, + "setup_commands": { + "type": "array", + "items": { + "$ref": "#/components/schemas/pipeline_command" + }, + "description": "The list of commands that are executed as part of the setup phase of the build. These commands are executed outside the build container." + }, + "started_on": { + "type": "string", + "format": "date-time", + "description": "The timestamp when the step execution was started. This is not set when the step hasn't executed yet." + }, + "state": { + "$ref": "#/components/schemas/pipeline_step_state" + }, + "uuid": { + "type": "string", + "description": "The UUID identifying the step." + } + } + } + ], + "x-bb-default-fields": ["uuid"], + "x-bb-url": "/rest/1.0/accounts/{target_user.uuid}/repositories/{repository.uuid}/pipelines/{pipeline.uuid}/steps/{uuid}", + "x-bb-batch-url": "/rest/1.0/accounts/{target_user.uuid}/repositories/{repository.uuid}/pipelines/steps_batch", + "x-bb-batch-max-size": 100, + "title": "Pipeline Step", + "description": "A step of a Bitbucket pipeline. This represents the actual result of the step execution." + }, + "pipeline_step_error": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "additionalProperties": true, + "type": "object", + "properties": { + "key": { + "type": "string", + "description": "The error key." + }, + "message": { + "type": "string", + "description": "The error message." + } + } + } + ], + "title": "Pipeline Step Error", + "description": "An error causing a step failure." + }, + "pipeline_step_state": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "additionalProperties": true, + "type": "object", + "properties": {} + } + ], + "title": "Pipeline Step State", + "description": "The representation of the progress state of a pipeline step." + }, + "pipeline_step_state_completed": { + "allOf": [ + { + "$ref": "#/components/schemas/pipeline_step_state" + }, + { + "additionalProperties": true, + "type": "object", + "description": "A Bitbucket Pipelines COMPLETED pipeline step state.", + "properties": { + "name": { + "enum": ["COMPLETED"], + "type": "string", + "description": "The name of pipeline step state (COMPLETED)." + }, + "result": { + "$ref": "#/components/schemas/pipeline_step_state_completed_result" + } + } + } + ] + }, + "pipeline_step_state_completed_error": { + "allOf": [ + { + "$ref": "#/components/schemas/pipeline_step_state_completed_result" + }, + { + "additionalProperties": true, + "type": "object", + "description": "A Bitbucket Pipelines ERROR pipeline step result.", + "properties": { + "error": { + "$ref": "#/components/schemas/pipeline_step_error" + }, + "name": { + "enum": ["ERROR"], + "type": "string", + "description": "The name of the result (ERROR)" + } + } + } + ] + }, + "pipeline_step_state_completed_expired": { + "allOf": [ + { + "$ref": "#/components/schemas/pipeline_step_state_completed_result" + }, + { + "additionalProperties": true, + "type": "object", + "description": "A Bitbucket Pipelines EXPIRED pipeline step result.", + "properties": { + "name": { + "enum": ["EXPIRED"], + "type": "string", + "description": "The name of the result (EXPIRED)" + } + } + } + ] + }, + "pipeline_step_state_completed_failed": { + "allOf": [ + { + "$ref": "#/components/schemas/pipeline_step_state_completed_result" + }, + { + "additionalProperties": true, + "type": "object", + "description": "A Bitbucket Pipelines FAILED pipeline step result.", + "properties": { + "name": { + "enum": ["FAILED"], + "type": "string", + "description": "The name of the result (FAILED)" + } + } + } + ] + }, + "pipeline_step_state_completed_not_run": { + "allOf": [ + { + "$ref": "#/components/schemas/pipeline_step_state_completed_result" + }, + { + "additionalProperties": true, + "type": "object", + "description": "A Bitbucket Pipelines NOT_RUN pipeline step result.", + "properties": { + "name": { + "enum": ["NOT_RUN"], + "type": "string", + "description": "The name of the result (NOT_RUN)" + } + } + } + ] + }, + "pipeline_step_state_completed_result": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "additionalProperties": true, + "type": "object", + "properties": {} + } + ], + "title": "Pipeline Completed Step Result", + "description": "A result of a completed pipeline step state." + }, + "pipeline_step_state_completed_stopped": { + "allOf": [ + { + "$ref": "#/components/schemas/pipeline_step_state_completed_result" + }, + { + "additionalProperties": true, + "type": "object", + "description": "A Bitbucket Pipelines STOPPED pipeline step result.", + "properties": { + "name": { + "enum": ["STOPPED"], + "type": "string", + "description": "The name of the result (STOPPED)" + } + } + } + ] + }, + "pipeline_step_state_completed_successful": { + "allOf": [ + { + "$ref": "#/components/schemas/pipeline_step_state_completed_result" + }, + { + "additionalProperties": true, + "type": "object", + "description": "A Bitbucket Pipelines SUCCESSFUL pipeline step result.", + "properties": { + "name": { + "enum": ["SUCCESSFUL"], + "type": "string", + "description": "The name of the result (SUCCESSFUL)" + } + } + } + ] + }, + "pipeline_step_state_in_progress": { + "allOf": [ + { + "$ref": "#/components/schemas/pipeline_step_state" + }, + { + "additionalProperties": true, + "type": "object", + "description": "A Bitbucket Pipelines IN_PROGRESS pipeline step state.", + "properties": { + "name": { + "enum": ["IN_PROGRESS"], + "type": "string", + "description": "The name of pipeline step state (IN_PROGRESS)." + } + } + } + ] + }, + "pipeline_step_state_pending": { + "allOf": [ + { + "$ref": "#/components/schemas/pipeline_step_state" + }, + { + "additionalProperties": true, + "type": "object", + "description": "A Bitbucket Pipelines PENDING pipeline step state.", + "properties": { + "name": { + "enum": ["PENDING"], + "type": "string", + "description": "The name of pipeline step state (PENDING)." + } + } + } + ] + }, + "pipeline_step_state_ready": { + "allOf": [ + { + "$ref": "#/components/schemas/pipeline_step_state" + }, + { + "additionalProperties": true, + "type": "object", + "description": "A Bitbucket Pipelines READY pipeline step state.", + "properties": { + "name": { + "enum": ["READY"], + "type": "string", + "description": "The name of pipeline step state (READY)." + } + } + } + ] + }, + "pipeline_target": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "additionalProperties": true, + "type": "object", + "properties": {} + } + ], + "title": "Pipeline Target", + "description": "A representation of the target that a pipeline executes on." + }, + "pipeline_trigger": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "additionalProperties": true, + "type": "object", + "properties": {} + } + ], + "title": "Pipeline Trigger", + "description": "A representation of the trigger used for a pipeline." + }, + "pipeline_trigger_manual": { + "allOf": [ + { + "$ref": "#/components/schemas/pipeline_trigger" + }, + { + "additionalProperties": true, + "type": "object", + "properties": {} + } + ], + "title": "Pipeline Manual Trigger", + "description": "A Bitbucket Pipelines MANUAL trigger." + }, + "pipeline_trigger_push": { + "allOf": [ + { + "$ref": "#/components/schemas/pipeline_trigger" + }, + { + "additionalProperties": true, + "type": "object", + "properties": {} + } + ], + "title": "Pipeline Push Trigger", + "description": "A Bitbucket Pipelines PUSH trigger." + }, + "pipeline_variable": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "additionalProperties": true, + "type": "object", + "properties": { + "key": { + "type": "string", + "description": "The unique name of the variable." + }, + "secured": { + "type": "boolean", + "description": "If true, this variable will be treated as secured. The value will never be exposed in the logs or the REST API." + }, + "uuid": { + "type": "string", + "description": "The UUID identifying the variable." + }, + "value": { + "type": "string", + "description": "The value of the variable. If the variable is secured, this will be empty." + } + } + } + ], + "title": "Pipeline Variable", + "description": "A Pipelines variable." + }, + "pipelines_config": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "additionalProperties": true, + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "Whether Pipelines is enabled for the repository." + }, + "repository": { + "$ref": "#/components/schemas/repository" + } + } + } + ], + "title": "Pipelines Configuration", + "description": "The Pipelines configuration for a repository." + }, + "pipelines_ddev_pipeline_step": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "additionalProperties": true, + "type": "object", + "description": "A step of a Bitbucket pipeline. This represents the actual result of the step execution." + } + ], + "x-bb-default-fields": ["uuid"], + "x-bb-url": "/rest/1.0/accounts/{target_user.uuid}/repositories/{repository.uuid}/pipelines/{pipeline.uuid}/steps/{uuid}", + "x-bb-batch-url": "/rest/1.0/accounts/{target_user.uuid}/repositories/{repository.uuid}/pipelines/steps_batch", + "x-bb-batch-max-size": 100 + }, + "pipelines_stg_west_pipeline_step": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "additionalProperties": true, + "type": "object", + "description": "A step of a Bitbucket pipeline. This represents the actual result of the step execution." + } + ], + "x-bb-default-fields": ["uuid"], + "x-bb-url": "/rest/1.0/accounts/{target_user.uuid}/repositories/{repository.uuid}/pipelines/{pipeline.uuid}/steps/{uuid}", + "x-bb-batch-url": "/rest/1.0/accounts/{target_user.uuid}/repositories/{repository.uuid}/pipelines/steps_batch", + "x-bb-batch-max-size": 100 + }, + "project": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "type": "object", + "properties": { + "created_on": { + "type": "string", + "format": "date-time" + }, + "description": { + "type": "string" + }, + "has_publicly_visible_repos": { + "type": "boolean", + "description": "\nIndicates whether the project contains publicly visible repositories.\nNote that private projects cannot contain public repositories." + }, + "is_private": { + "type": "boolean", + "description": "\nIndicates whether the project is publicly accessible, or whether it is\nprivate to the team and consequently only visible to team members.\nNote that private projects cannot contain public repositories." + }, + "key": { + "type": "string", + "description": "The project's key." + }, + "links": { + "type": "object", + "properties": { + "avatar": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + }, + "html": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "name": { + "type": "string", + "description": "The name of the project." + }, + "owner": { + "$ref": "#/components/schemas/team" + }, + "updated_on": { + "type": "string", + "format": "date-time" + }, + "uuid": { + "type": "string", + "description": "The project's immutable id." + } + }, + "additionalProperties": true + } + ], + "title": "Project", + "description": "A Bitbucket project.\n Projects are used by teams to organize repositories." + }, + "pullrequest": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "type": "object", + "properties": { + "author": { + "$ref": "#/components/schemas/account" + }, + "close_source_branch": { + "type": "boolean", + "description": "A boolean flag indicating if merging the pull request closes the source branch." + }, + "closed_by": { + "$ref": "#/components/schemas/account" + }, + "comment_count": { + "type": "integer", + "description": "The number of comments for a specific pull request.", + "minimum": 0 + }, + "created_on": { + "type": "string", + "description": "The ISO8601 timestamp the request was created.", + "format": "date-time" + }, + "destination": { + "$ref": "#/components/schemas/pullrequest_endpoint" + }, + "id": { + "type": "integer", + "description": "The pull request's unique ID. Note that pull request IDs are only unique within their associated repository." + }, + "links": { + "type": "object", + "properties": { + "activity": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + }, + "approve": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + }, + "comments": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + }, + "commits": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + }, + "decline": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + }, + "diff": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + }, + "diffstat": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + }, + "html": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + }, + "merge": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + }, + "self": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "merge_commit": { + "type": "object", + "title": "Pull Request Commit", + "properties": { + "hash": { + "type": "string", + "pattern": "[0-9a-f]{7,}?" + } + }, + "additionalProperties": false + }, + "participants": { + "type": "array", + "description": " The list of users that are collaborating on this pull request.\n Collaborators are user that:\n\n * are added to the pull request as a reviewer (part of the reviewers\n list)\n * are not explicit reviewers, but have commented on the pull request\n * are not explicit reviewers, but have approved the pull request\n\n Each user is wrapped in an object that indicates the user's role and\n whether they have approved the pull request. For performance reasons,\n the API only returns this list when an API requests a pull request by\n id.\n ", + "items": { + "$ref": "#/components/schemas/participant" + } + }, + "reason": { + "type": "string", + "description": "Explains why a pull request was declined. This field is only applicable to pull requests in rejected state." + }, + "rendered": { + "type": "object", + "title": "Rendered Pull Request Markup", + "description": "User provided pull request text, interpreted in a markup language and rendered in HTML", + "properties": { + "description": { + "type": "object", + "properties": { + "html": { + "type": "string", + "description": "The user's content rendered as HTML." + }, + "markup": { + "type": "string", + "description": "The type of markup language the raw content is to be interpreted in.", + "enum": ["markdown", "creole", "plaintext"] + }, + "raw": { + "type": "string", + "description": "The text as it was typed by a user." + } + }, + "additionalProperties": false + }, + "reason": { + "type": "object", + "properties": { + "html": { + "type": "string", + "description": "The user's content rendered as HTML." + }, + "markup": { + "type": "string", + "description": "The type of markup language the raw content is to be interpreted in.", + "enum": ["markdown", "creole", "plaintext"] + }, + "raw": { + "type": "string", + "description": "The text as it was typed by a user." + } + }, + "additionalProperties": false + }, + "title": { + "type": "object", + "properties": { + "html": { + "type": "string", + "description": "The user's content rendered as HTML." + }, + "markup": { + "type": "string", + "description": "The type of markup language the raw content is to be interpreted in.", + "enum": ["markdown", "creole", "plaintext"] + }, + "raw": { + "type": "string", + "description": "The text as it was typed by a user." + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "reviewers": { + "type": "array", + "description": "The list of users that were added as reviewers on this pull request when it was created. For performance reasons, the API only includes this list on a pull request's `self` URL.", + "items": { + "$ref": "#/components/schemas/account" + } + }, + "source": { + "$ref": "#/components/schemas/pullrequest_endpoint" + }, + "state": { + "type": "string", + "description": "The pull request's current status.", + "enum": ["MERGED", "SUPERSEDED", "OPEN", "DECLINED"] + }, + "summary": { + "type": "object", + "properties": { + "html": { + "type": "string", + "description": "The user's content rendered as HTML." + }, + "markup": { + "type": "string", + "description": "The type of markup language the raw content is to be interpreted in.", + "enum": ["markdown", "creole", "plaintext"] + }, + "raw": { + "type": "string", + "description": "The text as it was typed by a user." + } + }, + "additionalProperties": false + }, + "task_count": { + "type": "integer", + "description": "The number of open tasks for a specific pull request.", + "minimum": 0 + }, + "title": { + "type": "string", + "description": "Title of the pull request." + }, + "updated_on": { + "type": "string", + "description": "The ISO8601 timestamp the request was last updated.", + "format": "date-time" + } + }, + "additionalProperties": true + } + ], + "title": "Pull Request", + "description": "A pull request object." + }, + "pullrequest_comment": { + "allOf": [ + { + "$ref": "#/components/schemas/comment" + }, + { + "type": "object", + "properties": { + "pullrequest": { + "$ref": "#/components/schemas/pullrequest" + } + }, + "additionalProperties": true + } + ], + "title": "Pull Request Comment", + "description": "A pullrequest comment." + }, + "pullrequest_endpoint": { + "type": "object", + "title": "Pull Request Endpoint", + "properties": { + "branch": { + "type": "object", + "title": "Pull Request Branch", + "properties": { + "default_merge_strategy": { + "type": "string", + "description": "The default merge strategy, when this endpoint is the destination of the pull request." + }, + "merge_strategies": { + "type": "array", + "description": "Available merge strategies, when this endpoint is the destination of the pull request.", + "items": { + "type": "string", + "enum": ["merge_commit", "squash", "fast_forward"] + } + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + }, + "commit": { + "type": "object", + "title": "Pull Request Commit", + "properties": { + "hash": { + "type": "string", + "pattern": "[0-9a-f]{7,}?" + } + }, + "additionalProperties": false + }, + "repository": { + "$ref": "#/components/schemas/repository" + } + }, + "additionalProperties": false + }, + "pullrequest_merge_parameters": { + "type": "object", + "title": "Pull Request Merge Parameters", + "description": "The metadata that describes a pull request merge.", + "properties": { + "close_source_branch": { + "type": "boolean", + "description": "Whether the source branch should be deleted. If this is not provided, we fallback to the value used when the pull request was created, which defaults to False" + }, + "merge_strategy": { + "type": "string", + "description": "The merge strategy that will be used to merge the pull request.", + "enum": ["merge_commit", "squash", "fast_forward"], + "default": "merge_commit" + }, + "message": { + "type": "string", + "description": "The commit message that will be used on the resulting commit." + }, + "type": { + "type": "string" + } + }, + "required": ["type"], + "additionalProperties": true + }, + "ref": { + "type": "object", + "title": "Ref", + "description": "A ref object, representing a branch or tag in a repository.", + "properties": { + "links": { + "type": "object", + "properties": { + "commits": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + }, + "html": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + }, + "self": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "name": { + "type": "string", + "description": "The name of the ref." + }, + "target": { + "$ref": "#/components/schemas/commit" + }, + "type": { + "type": "string" + } + }, + "required": ["type"], + "additionalProperties": true + }, + "report": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "additionalProperties": true, + "type": "object", + "properties": { + "created_on": { + "type": "string", + "format": "date-time", + "description": "The timestamp when the report was created." + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/report_data" + }, + "description": "An array of data fields to display information on the report. Maximum 10." + }, + "details": { + "type": "string", + "description": "A string to describe the purpose of the report." + }, + "external_id": { + "type": "string", + "description": "ID of the report provided by the report creator. It can be used to identify the report as an alternative to it's generated uuid. It is not used by Bitbucket, but only by the report creator for updating or deleting this specific report. Needs to be unique." + }, + "link": { + "type": "string", + "format": "uri", + "description": "A URL linking to the results of the report in an external tool." + }, + "logo_url": { + "type": "string", + "format": "uri", + "description": "A URL to the report logo. If none is provided, the default insights logo will be used." + }, + "remote_link_enabled": { + "type": "boolean", + "description": "If enabled, a remote link is created in Jira for the issue associated with the commit the report belongs to." + }, + "report_type": { + "enum": ["SECURITY", "COVERAGE", "TEST", "BUG"], + "type": "string", + "description": "The type of the report." + }, + "reporter": { + "type": "string", + "description": "A string to describe the tool or company who created the report." + }, + "result": { + "enum": ["PASSED", "FAILED", "PENDING"], + "type": "string", + "description": "The state of the report. May be set to PENDING and later updated." + }, + "title": { + "type": "string", + "description": "The title of the report." + }, + "updated_on": { + "type": "string", + "format": "date-time", + "description": "The timestamp when the report was updated." + }, + "uuid": { + "type": "string", + "description": "The UUID that can be used to identify the report." + } + } + } + ], + "x-bb-default-fields": ["uuid", "commitHash"], + "x-bb-url": "/rest/2.0/accounts/{target_user.uuid}/repositories/{repository.uuid}/commits/{commitHash}/reports/{uuid}", + "title": "Commit Report", + "description": "A report for a commit." + }, + "report_annotation": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "additionalProperties": true, + "type": "object", + "properties": { + "annotation_type": { + "enum": ["VULNERABILITY", "CODE_SMELL", "BUG"], + "type": "string", + "description": "The type of the report." + }, + "created_on": { + "type": "string", + "format": "date-time", + "description": "The timestamp when the report was created." + }, + "details": { + "type": "string", + "description": "The details to show to users when clicking on the annotation." + }, + "external_id": { + "type": "string", + "description": "ID of the annotation provided by the annotation creator. It can be used to identify the annotation as an alternative to it's generated uuid. It is not used by Bitbucket, but only by the annotation creator for updating or deleting this specific annotation. Needs to be unique." + }, + "line": { + "type": "integer", + "description": "The line number that the annotation should belong to. If no line number is provided, then it will default to 0 and in a pull request it will appear at the top of the file specified by the path field.", + "minimum": 1 + }, + "link": { + "type": "string", + "format": "uri", + "description": "A URL linking to the annotation in an external tool." + }, + "path": { + "type": "string", + "description": "The path of the file on which this annotation should be placed. This is the path of the file relative to the git repository. If no path is provided, then it will appear in the overview modal on all pull requests where the tip of the branch is the given commit, regardless of which files were modified." + }, + "result": { + "enum": ["PASSED", "FAILED", "SKIPPED", "IGNORED"], + "type": "string", + "description": "The state of the report. May be set to PENDING and later updated." + }, + "severity": { + "enum": ["CRITICAL", "HIGH", "MEDIUM", "LOW"], + "type": "string", + "description": "The severity of the annotation." + }, + "summary": { + "type": "string", + "description": "The message to display to users." + }, + "updated_on": { + "type": "string", + "format": "date-time", + "description": "The timestamp when the report was updated." + }, + "uuid": { + "type": "string", + "description": "The UUID that can be used to identify the annotation." + } + } + } + ], + "x-bb-default-fields": ["uuid"], + "x-bb-url": "/rest/2.0/accounts/{target_user.uuid}/repositories/{repository.uuid}/commits/{commit.hash}/reports/{reportUuid}/annotations/{uuid}", + "title": "Report Annotation", + "description": "A report for a commit." + }, + "report_data": { + "type": "object", + "title": "Report Data", + "description": "A key-value element that will be displayed along with the report.", + "properties": { + "title": { + "type": "string", + "description": "A string describing what this data field represents." + }, + "type": { + "enum": [ + "BOOLEAN", + "DATE", + "DURATION", + "LINK", + "NUMBER", + "PERCENTAGE", + "TEXT" + ], + "type": "string", + "description": "The type of data contained in the value field. If not provided, then the value will be detected as a boolean, number or string." + }, + "value": { + "type": "object", + "description": "The value of the data element." + } + } + }, + "repository": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "type": "object", + "properties": { + "created_on": { + "type": "string", + "format": "date-time" + }, + "description": { + "type": "string" + }, + "fork_policy": { + "type": "string", + "description": "\nControls the rules for forking this repository.\n\n* **allow_forks**: unrestricted forking\n* **no_public_forks**: restrict forking to private forks (forks cannot\n be made public later)\n* **no_forks**: deny all forking\n", + "enum": ["allow_forks", "no_public_forks", "no_forks"] + }, + "full_name": { + "type": "string", + "description": "The concatenation of the repository owner's username and the slugified name, e.g. \"evzijst/interruptingcow\". This is the same string used in Bitbucket URLs." + }, + "has_issues": { + "type": "boolean" + }, + "has_wiki": { + "type": "boolean" + }, + "is_private": { + "type": "boolean" + }, + "language": { + "type": "string" + }, + "links": { + "type": "object", + "properties": { + "avatar": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + }, + "clone": { + "type": "array", + "items": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "commits": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + }, + "downloads": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + }, + "forks": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + }, + "hooks": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + }, + "html": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + }, + "pullrequests": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + }, + "self": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + }, + "watchers": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "mainbranch": { + "$ref": "#/components/schemas/branch" + }, + "name": { + "type": "string" + }, + "owner": { + "$ref": "#/components/schemas/account" + }, + "parent": { + "$ref": "#/components/schemas/repository" + }, + "project": { + "$ref": "#/components/schemas/project" + }, + "scm": { + "type": "string", + "enum": ["git"] + }, + "size": { + "type": "integer" + }, + "slug": { + "type": "string", + "description": "The \"sluggified\" version of the repository's name. This contains only ASCII characters and can therefore be slightly different than the name" + }, + "updated_on": { + "type": "string", + "format": "date-time" + }, + "uuid": { + "type": "string", + "description": "The repository's immutable id. This can be used as a substitute for the slug segment in URLs. Doing this guarantees your URLs will survive renaming of the repository by its owner, or even transfer of the repository to a different user." + } + }, + "additionalProperties": true + } + ], + "title": "Repository", + "description": "A Bitbucket repository." + }, + "repository_group_permission": { + "type": "object", + "title": "Repository Group Permission", + "description": "A group's permission for a given repository.", + "properties": { + "group": { + "$ref": "#/components/schemas/group" + }, + "links": { + "type": "object", + "properties": { + "self": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "permission": { + "type": "string", + "enum": ["admin", "write", "read", "none"] + }, + "repository": { + "$ref": "#/components/schemas/repository" + }, + "type": { + "type": "string" + } + }, + "required": ["type"], + "additionalProperties": true + }, + "repository_permission": { + "type": "object", + "title": "Repository Permission", + "description": "A user's permission for a given repository.", + "properties": { + "permission": { + "type": "string", + "enum": ["admin", "write", "read", "none"] + }, + "repository": { + "$ref": "#/components/schemas/repository" + }, + "type": { + "type": "string" + }, + "user": { + "$ref": "#/components/schemas/user" + } + }, + "required": ["type"], + "additionalProperties": true + }, + "repository_user_permission": { + "type": "object", + "title": "Repository User Permission", + "description": "A user's direct permission for a given repository.", + "properties": { + "links": { + "type": "object", + "properties": { + "self": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "permission": { + "type": "string", + "enum": ["admin", "write", "read", "none"] + }, + "repository": { + "$ref": "#/components/schemas/repository" + }, + "type": { + "type": "string" + }, + "user": { + "$ref": "#/components/schemas/user" + } + }, + "required": ["type"], + "additionalProperties": true + }, + "search_code_search_result": { + "type": "object", + "properties": { + "content_match_count": { + "type": "integer", + "format": "int64", + "readOnly": true + }, + "content_matches": { + "type": "array", + "readOnly": true, + "items": { + "$ref": "#/components/schemas/search_content_match" + } + }, + "file": { + "$ref": "#/components/schemas/commit_file" + }, + "path_matches": { + "type": "array", + "readOnly": true, + "items": { + "$ref": "#/components/schemas/search_segment" + } + }, + "type": { + "type": "string", + "readOnly": true + } + } + }, + "search_content_match": { + "type": "object", + "properties": { + "lines": { + "type": "array", + "readOnly": true, + "items": { + "$ref": "#/components/schemas/search_line" + } + } + } + }, + "search_line": { + "type": "object", + "properties": { + "line": { + "type": "integer", + "format": "int32", + "readOnly": true + }, + "segments": { + "type": "array", + "readOnly": true, + "items": { + "$ref": "#/components/schemas/search_segment" + } + } + } + }, + "search_result_page": { + "allOf": [ + { + "$ref": "#/components/schemas/paginated" + }, + { + "type": "object", + "properties": { + "query_substituted": { + "type": "boolean", + "readOnly": true + }, + "values": { + "type": "array", + "readOnly": true, + "items": { + "$ref": "#/components/schemas/search_code_search_result" + }, + "description": "The values of the current page." + } + } + } + ] + }, + "search_segment": { + "type": "object", + "properties": { + "match": { + "type": "boolean", + "readOnly": true + }, + "text": { + "type": "string", + "readOnly": true + } + } + }, + "snippet": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "type": "object", + "properties": { + "created_on": { + "type": "string", + "format": "date-time" + }, + "creator": { + "$ref": "#/components/schemas/account" + }, + "id": { + "type": "integer", + "minimum": 0 + }, + "is_private": { + "type": "boolean" + }, + "owner": { + "$ref": "#/components/schemas/account" + }, + "scm": { + "type": "string", + "description": "The DVCS used to store the snippet.", + "enum": ["git"] + }, + "title": { + "type": "string" + }, + "updated_on": { + "type": "string", + "format": "date-time" + } + }, + "additionalProperties": true + } + ], + "title": "Snippet", + "description": "A snippet object." + }, + "snippet_comment": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "type": "object", + "properties": { + "links": { + "type": "object", + "properties": { + "html": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + }, + "self": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "snippet": { + "$ref": "#/components/schemas/snippet" + } + }, + "additionalProperties": true + } + ], + "title": "Snippet Comment", + "description": "A comment on a snippet." + }, + "snippet_commit": { + "allOf": [ + { + "$ref": "#/components/schemas/base_commit" + }, + { + "type": "object", + "properties": { + "links": { + "type": "object", + "properties": { + "diff": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + }, + "html": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + }, + "self": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "snippet": { + "$ref": "#/components/schemas/snippet" + } + }, + "additionalProperties": true + } + ], + "title": "Snippet Commit", + "description": "" + }, + "ssh_account_key": { + "allOf": [ + { + "$ref": "#/components/schemas/ssh_key" + }, + { + "type": "object", + "properties": { + "owner": { + "$ref": "#/components/schemas/account" + } + }, + "additionalProperties": true + } + ], + "title": "SSH Account Key", + "description": "Represents an SSH public key for a user." + }, + "ssh_key": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "type": "object", + "properties": { + "comment": { + "type": "string", + "description": "The comment parsed from the SSH key (if present)" + }, + "created_on": { + "type": "string", + "format": "date-time" + }, + "key": { + "type": "string", + "description": "The SSH public key value in OpenSSH format." + }, + "label": { + "type": "string", + "description": "The user-defined label for the SSH key" + }, + "last_used": { + "type": "string", + "format": "date-time" + }, + "links": { + "type": "object", + "properties": { + "self": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "uuid": { + "type": "string", + "description": "The SSH key's immutable ID." + } + }, + "additionalProperties": true + } + ], + "title": "SSH Key", + "description": "Base type for representing SSH public keys." + }, + "stg_west_report": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "additionalProperties": true, + "type": "object", + "description": "A report for a commit." + } + ], + "x-bb-default-fields": ["uuid", "commitHash"], + "x-bb-url": "/rest/2.0/accounts/{target_user.uuid}/repositories/{repository.uuid}/commits/{commitHash}/reports/{uuid}" + }, + "subject_types": { + "type": "object", + "title": "Subject Types", + "description": "The mapping of resource/subject types pointing to their individual event types.", + "properties": { + "repository": { + "type": "object", + "properties": { + "events": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "team": { + "type": "object", + "properties": { + "events": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "user": { + "type": "object", + "properties": { + "events": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "tag": { + "allOf": [ + { + "$ref": "#/components/schemas/ref" + }, + { + "type": "object", + "properties": { + "date": { + "type": "string", + "description": "The date that the tag was created, if available", + "format": "date-time" + }, + "message": { + "type": "string", + "description": "The message associated with the tag, if available." + }, + "tagger": { + "$ref": "#/components/schemas/author" + } + }, + "additionalProperties": true + } + ], + "title": "Tag", + "description": "A tag object, representing a tag in a repository." + }, + "team": { + "allOf": [ + { + "$ref": "#/components/schemas/account" + }, + { + "type": "object", + "properties": {}, + "additionalProperties": true + } + ], + "title": "Team", + "description": "A team object." + }, + "team_permission": { + "type": "object", + "title": "Team Permission", + "description": "A user's permission for a given team.", + "properties": { + "permission": { + "type": "string", + "enum": ["admin", "collaborator", "member"] + }, + "team": { + "$ref": "#/components/schemas/team" + }, + "type": { + "type": "string" + }, + "user": { + "$ref": "#/components/schemas/user" + } + }, + "required": ["type"], + "additionalProperties": true + }, + "treeentry": { + "type": "object", + "title": "Tree Entry", + "description": "Base type for most resource objects. It defines the common `type` element that identifies an object's type. It also identifies the element as Swagger's `discriminator`.", + "properties": { + "commit": { + "$ref": "#/components/schemas/commit" + }, + "path": { + "type": "string", + "description": "The path in the repository" + }, + "type": { + "type": "string" + } + }, + "required": ["type"], + "additionalProperties": true + }, + "user": { + "allOf": [ + { + "$ref": "#/components/schemas/account" + }, + { + "type": "object", + "properties": { + "account_id": { + "type": "string", + "description": "The user's Atlassian account ID." + }, + "is_staff": { + "type": "boolean" + } + }, + "additionalProperties": true + } + ], + "title": "User", + "description": "A user object." + }, + "version": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "links": { + "type": "object", + "properties": { + "self": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "name": { + "type": "string" + } + }, + "additionalProperties": true + } + ], + "title": "Version", + "description": "A version as defined in a repository's issue tracker." + }, + "webhook_subscription": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "type": "object", + "properties": { + "active": { + "type": "boolean" + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "description": { + "type": "string", + "description": "A user-defined description of the webhook." + }, + "events": { + "type": "array", + "description": "The events this webhook is subscribed to.", + "items": { + "type": "string", + "enum": [ + "pullrequest:unapproved", + "issue:comment_created", + "repo:imported", + "repo:created", + "repo:commit_comment_created", + "pullrequest:approved", + "pullrequest:comment_updated", + "issue:updated", + "project:updated", + "repo:deleted", + "pullrequest:changes_request_created", + "pullrequest:comment_created", + "repo:commit_status_updated", + "pullrequest:updated", + "issue:created", + "repo:fork", + "pullrequest:comment_deleted", + "repo:commit_status_created", + "repo:updated", + "pullrequest:rejected", + "pullrequest:fulfilled", + "pullrequest:created", + "pullrequest:changes_request_removed", + "repo:transfer", + "repo:push" + ] + }, + "minItems": 1, + "uniqueItems": true + }, + "subject": { + "$ref": "#/components/schemas/object" + }, + "subject_type": { + "type": "string", + "description": "The type of entity. Set to either `repository` or `workspace` based on where the subscription is defined.", + "enum": ["workspace", "user", "repository", "team"] + }, + "url": { + "type": "string", + "description": "The URL events get delivered to.", + "format": "uri" + }, + "uuid": { + "type": "string", + "description": "The webhook's id" + } + }, + "additionalProperties": true + } + ], + "title": "Webhook Subscription", + "description": "A Webhook subscription." + }, + "workspace": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "type": "object", + "properties": { + "created_on": { + "type": "string", + "format": "date-time" + }, + "is_private": { + "type": "boolean", + "description": "Indicates whether the workspace is publicly accessible, or whether it is\nprivate to the members and consequently only visible to members." + }, + "links": { + "type": "object", + "properties": { + "avatar": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + }, + "html": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + }, + "members": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + }, + "owners": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + }, + "projects": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + }, + "repositories": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + }, + "self": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + }, + "snippets": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "name": { + "type": "string", + "description": "The name of the workspace." + }, + "slug": { + "type": "string", + "description": "The short label that identifies this workspace." + }, + "updated_on": { + "type": "string", + "format": "date-time" + }, + "uuid": { + "type": "string", + "description": "The workspace's immutable id." + } + }, + "additionalProperties": true + } + ], + "title": "Workspace", + "description": "A Bitbucket workspace.\n Workspaces are used to organize repositories." + }, + "workspace_membership": { + "allOf": [ + { + "$ref": "#/components/schemas/object" + }, + { + "type": "object", + "properties": { + "links": { + "type": "object", + "properties": { + "self": { + "type": "object", + "title": "Link", + "description": "A link to a resource related to this object.", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "user": { + "$ref": "#/components/schemas/account" + }, + "workspace": { + "$ref": "#/components/schemas/workspace" + } + }, + "additionalProperties": true + } + ], + "title": "Workspace Membership", + "description": "A Bitbucket workspace membership.\n Links a user to a workspace." + } + } + } +} diff --git a/plugins/bitbucket-cloud-common/openapitools.json b/plugins/bitbucket-cloud-common/openapitools.json new file mode 100644 index 0000000000..c5bd3a2d0e --- /dev/null +++ b/plugins/bitbucket-cloud-common/openapitools.json @@ -0,0 +1,22 @@ +{ + "$schema": "../../node_modules/@openapitools/openapi-generator-cli/config.schema.json", + "spaces": 2, + "generator-cli": { + "version": "6.0.0-beta", + "generators": { + "backstage": { + "generatorName": "typescript-fetch", + "glob": "bitbucket-cloud.oas.json", + "output": "src", + "additionalProperties": { + "disallowAdditionalPropertiesIfNotPresent": false, + "enumPropertyNaming": "PascalCase", + "legacyDiscriminatorBehavior": false, + "modelPropertyNaming": "original", + "withoutRuntimeChecks": true + }, + "templateDir": "templates" + } + } + } +} diff --git a/plugins/bitbucket-cloud-common/package.json b/plugins/bitbucket-cloud-common/package.json new file mode 100644 index 0000000000..a02196bc39 --- /dev/null +++ b/plugins/bitbucket-cloud-common/package.json @@ -0,0 +1,43 @@ +{ + "name": "@backstage/plugin-bitbucket-cloud-common", + "description": "Common functionalities for bitbucket-cloud plugins", + "version": "0.1.0", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "private": false, + "publishConfig": { + "access": "public", + "main": "dist/index.cjs.js", + "module": "dist/index.esm.js", + "types": "dist/index.d.ts" + }, + "backstage": { + "role": "common-library" + }, + "scripts": { + "build": "backstage-cli package build", + "lint": "backstage-cli package lint", + "test": "backstage-cli package test", + "clean": "backstage-cli package clean", + "prepack": "backstage-cli package prepack", + "postpack": "backstage-cli package postpack", + "refresh-schema": "scripts/prepare-schema.js && prettier --check bitbucket-cloud.oas.json -w", + "generate-models": "scripts/generate-models.sh", + "reduce-models": "scripts/reduce-models.js", + "update-models": "yarn refresh-schema && yarn generate-models && yarn reduce-models" + }, + "dependencies": { + "@backstage/integration": "^1.2.1", + "cross-fetch": "^3.1.5" + }, + "devDependencies": { + "@backstage/cli": "^0.17.2", + "@openapitools/openapi-generator-cli": "^2.4.26", + "msw": "^0.42.0", + "ts-morph": "^15.0.0" + }, + "files": [ + "dist" + ] +} diff --git a/plugins/bitbucket-cloud-common/scripts/adjust-models.js b/plugins/bitbucket-cloud-common/scripts/adjust-models.js new file mode 100755 index 0000000000..5d6eea845b --- /dev/null +++ b/plugins/bitbucket-cloud-common/scripts/adjust-models.js @@ -0,0 +1,115 @@ +#!/usr/bin/env node +'use strict'; +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const tsMorph = require('ts-morph'); + +function cleanupWrongAllOfModels(modelsModule) { + const allOfInterfaces = modelsModule + .getInterfaces() + .filter(i => i.getName().includes('AllOf')); + allOfInterfaces.forEach(i => { + const name = i.getName(); + const realName = name.replace('AllOf', ''); + + const realInterface = modelsModule.getInterface(realName); + if (realInterface) { + i.remove(); + } else { + i.rename(realName); + } + }); + + const allOfTypes = modelsModule + .getTypeAliases() + .filter(t => t.getName().includes('AllOf')); + allOfTypes.forEach(t => { + const name = t.getName(); + const realName = name.replace('AllOf', ''); + + const varStmt = modelsModule.getVariableStatementOrThrow(t.getName()); + + const realType = modelsModule.getTypeAlias(realName); + if (realType) { + t.remove(); + varStmt.remove(); + } else { + t.rename(realName); + varStmt.getDeclarationList().getDeclarations()[0].rename(realName); + } + }); +} + +function makePaginatedGeneric(modelsModule) { + const paginated = modelsModule.getInterface('Paginated'); + + if (paginated.getTypeParameters().length === 0) { + paginated.addTypeParameter('TResultItem'); + } + + const valuesProperty = paginated.getPropertyOrThrow('values'); + let valuesType = valuesProperty + .getType() + .getText() + .replace('| null ', '') + .replace('any[]', 'Array') + .replaceAll('any', 'TResultItem'); + if (valuesProperty.hasQuestionToken()) { + valuesType = valuesType.replace(' | undefined', ''); + } + valuesProperty.setType(valuesType); +} + +function setPaginatedResultItemType(modelsModule) { + modelsModule + .getInterfaces() + .filter(i => + i.getExtends().find(it => it.getExpression().getText() === 'Paginated'), + ) + .forEach(i => { + const paginatedExtends = i + .getExtends() + .find(it => it.getExpression().getText() === 'Paginated'); + + const valuesProperty = i.getPropertyOrThrow('values'); + const resultItemType = valuesProperty + .getType() + .getUnionTypes() + .map(it => it.getText()) + .find(it => it !== 'undefined') + .replaceAll(/import[^ ]+.Models./g, '') + .replace('[]', '') + .replace(/(?:Array|Set)<(.*)>/, '$1'); + + paginatedExtends.setExpression(`Paginated<${resultItemType}>`); + }); +} + +const project = new tsMorph.Project({ + tsConfigFilePath: '../../tsconfig.json', + skipAddingFilesFromTsConfig: true, +}); +project.addSourceFilesAtPaths('src/**'); + +const modelsFile = project.getSourceFile('src/models/index.ts'); +const modelsModule = modelsFile.getModuleOrThrow('Models'); + +cleanupWrongAllOfModels(modelsModule); +makePaginatedGeneric(modelsModule); +setPaginatedResultItemType(modelsModule); + +project.saveSync(); diff --git a/plugins/bitbucket-cloud-common/scripts/generate-models.sh b/plugins/bitbucket-cloud-common/scripts/generate-models.sh new file mode 100755 index 0000000000..7dd3170fd2 --- /dev/null +++ b/plugins/bitbucket-cloud-common/scripts/generate-models.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash + +# Copyright 2022 The Backstage Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -e + +SCRIPT_DIR=$(dirname $0) +PLUGIN_DIR="${SCRIPT_DIR}/.." + +yarn --cwd "${PLUGIN_DIR}" openapi-generator-cli generate --generator-key backstage +rm -d "${PLUGIN_DIR}/src/apis" # empty dir or fails +"${SCRIPT_DIR}"/adjust-models.js +yarn --cwd "${PLUGIN_DIR}" prettier --check . -w diff --git a/plugins/bitbucket-cloud-common/scripts/prepare-schema.js b/plugins/bitbucket-cloud-common/scripts/prepare-schema.js new file mode 100755 index 0000000000..7454aecb40 --- /dev/null +++ b/plugins/bitbucket-cloud-common/scripts/prepare-schema.js @@ -0,0 +1,279 @@ +#!/usr/bin/env node +'use strict'; +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const BASE_DOMAIN = 'https://developer.atlassian.com'; +const SCHEMA_SOURCE = `${BASE_DOMAIN}/cloud/bitbucket/swagger.v3.json`; + +const fetch = require('cross-fetch'); +const fs = require('fs'); + +const destFile = `${__dirname}/../bitbucket-cloud.oas.json`; + +const sortSelectedProperties = (key, value) => { + if (key !== 'schemas' && key !== 'properties') { + return value; + } + + if (value && typeof value === 'object') { + return Object.keys(value) + .sort() + .reduce((o, k) => { + o[k] = value[k]; + return o; + }, {}); + } + + return value; +}; + +// similar to definition of "slug" at ""#/components/schemas/group" +const repoSlugDefinition = { + type: 'string', + description: + 'The "sluggified" version of the repository\'s name. This contains only ASCII characters and can therefore be slightly different than the name', +}; + +const paginatedDefinition = { + type: 'object', + title: 'Paginated', + description: 'A generic paginated list.', + discriminator: { + propertyName: 'type', + }, + properties: { + next: { + type: 'string', + format: 'uri', + description: + 'Link to the next page if it exists. The last page of a collection does not have this value. Use this link to navigate the result set and refrain from constructing your own URLs.', + }, + page: { + type: 'integer', + description: + 'Page number of the current results. This is an optional element that is not provided in all responses.', + }, + pagelen: { + type: 'integer', + description: + 'Current number of objects on the existing page. The default value is 10 with 100 being the maximum allowed value. Individual APIs may enforce different values.', + }, + previous: { + type: 'string', + format: 'uri', + description: + 'Link to previous page if it exists. A collections first page does not have this value. This is an optional element that is not provided in all responses. Some result sets strictly support forward navigation and never provide previous links. Clients must anticipate that backwards navigation is not always available. Use this link to navigate the result set and refrain from constructing your own URLs.', + }, + size: { + type: 'integer', + description: + 'Total number of objects in the response. This is an optional element that is not provided in all responses, as it can be expensive to compute.', + }, + values: { + description: 'The values of the current page.', + oneOf: [ + { + type: 'array', + minItems: 0, + items: {}, + uniqueItems: false, + }, + { + type: 'array', + minItems: 0, + items: {}, + uniqueItems: true, + }, + ], + }, + }, +}; + +const addMissingRepoSlug = json => { + const repoProperties = json.components.schemas.repository.allOf.find( + item => item.properties, + ).properties; + if (repoProperties.slug) { + // eslint-disable-next-line no-console + console.log( + '[WARN] repository schema already contains slug property. Patch got obsolete.', + ); + } else { + repoProperties.slug = repoSlugDefinition; + } + + return json; +}; + +const removePageDefinition = json => { + delete json.components.schemas.page; + return json; +}; + +const renamePaginatedSnippetCommitToPlural = json => { + if (!json.components.schemas.paginated_snippet_commit) { + // eslint-disable-next-line no-console + console.log( + '[WARN] $.components.schemas.paginated_snippet_commit does not exist anymore. Patch got obsolete.', + ); + return json; + } + + json.components.schemas.paginated_snippet_commits = + json.components.schemas.paginated_snippet_commit; + delete json.components.schemas.paginated_snippet_commit; + + return JSON.parse( + JSON.stringify(json).replace( + '"#/components/schemas/paginated_snippet_commit"', + '"#/components/schemas/paginated_snippet_commits"', + ), + ); +}; + +const addPaginatedDefinition = json => { + json.components.schemas.paginated = paginatedDefinition; + return json; +}; + +// Changes "interface PaginatedXyz {" to "interface PaginatedXyz extends Paginated {" +// (generic type gets extracted from "values" property) +const paginatedDefinitionsExtendPaginated = json => { + // exception to the standard naming pattern PaginatedXyz / paginated_xyz + const exceptions = [ + 'deployments_ddev_paginated_environments', + 'deployments_stg_west_paginated_environments', + 'search_result_page', + ]; + + Object.keys(json.components.schemas) + .filter(name => name.startsWith('paginated_') || exceptions.includes(name)) + .forEach(name => { + // modify other paginated_[...] schemas + const old = json.components.schemas[name]; + const title = old.title; + const description = old.description; + delete old.title; + delete old.description; + delete old.properties.page; + delete old.properties.pagelen; + delete old.properties.size; + delete old.properties.previous; + delete old.properties.next; + delete old.additionalProperties; + old.properties.values.description = + old.properties.values.description ?? + paginatedDefinition.properties.values.description; + + json.components.schemas[name] = { + title: title, + description: description, + allOf: [ + { + $ref: '#/components/schemas/paginated', + }, + old, + ], + }; + }); + + return json; +}; + +const preventHardToDetectDuplicateInterfacesDueToAllOf = json => { + Object.keys(json.components.schemas).forEach(name => { + const schema = json.components.schemas[name]; + if (!schema.allOf) { + return; + } + + schema.allOf.forEach(allOfItem => { + if (allOfItem.title) { + schema.title = schema.title ?? allOfItem.title; + schema.description = schema.description ?? allOfItem.description; + delete allOfItem.title; + delete allOfItem.description; + } + }); + }); + + return json; +}; + +const removeBuggyDescription = json => { + const valueProp = + json.components.schemas.branchrestriction.allOf[1].properties.value; + if (!valueProp.description.startsWith(' { + const schema = json.components.schemas.pipeline_selector; + const extension = schema.allOf[1]; + delete schema.allOf; + + json.components.schemas.pipeline_selector = { + ...schema, + ...extension, + }; + + return json; +}; + +const escapeTsdocInDescription = json => { + const prop = json.components.schemas.commitstatus.allOf[1].properties.url; + prop.description = prop.description.replaceAll(/(\S*[{]\S+[}]\S*)/g, '`$1`'); + + return json; +}; + +const relativeToAbsoluteUrls = json => { + Object.keys(json.components.schemas).forEach(name => { + const schema = json.components.schemas[name]; + if (schema.description) { + schema.description = schema.description.replace( + /]\(\/cloud\/bitbucket\//g, + `](${BASE_DOMAIN}/cloud/bitbucket/`, + ); + } + }); + + return json; +}; + +fetch(SCHEMA_SOURCE) + .then(res => res.json()) + .then(addMissingRepoSlug) + .then(removePageDefinition) + .then(renamePaginatedSnippetCommitToPlural) + .then(addPaginatedDefinition) + .then(paginatedDefinitionsExtendPaginated) + .then(preventHardToDetectDuplicateInterfacesDueToAllOf) + .then(removeBuggyDescription) + .then(resolveConflictingInheritance) + .then(escapeTsdocInDescription) + .then(relativeToAbsoluteUrls) + .then(json => { + fs.writeFileSync(destFile, JSON.stringify(json, sortSelectedProperties, 2)); + return json; + }); diff --git a/plugins/bitbucket-cloud-common/scripts/reduce-models.js b/plugins/bitbucket-cloud-common/scripts/reduce-models.js new file mode 100755 index 0000000000..9d458f4e74 --- /dev/null +++ b/plugins/bitbucket-cloud-common/scripts/reduce-models.js @@ -0,0 +1,100 @@ +#!/usr/bin/env node +'use strict'; +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const tsMorph = require('ts-morph'); + +const project = new tsMorph.Project({ + tsConfigFilePath: '../../tsconfig.json', + skipAddingFilesFromTsConfig: true, +}); +project.addSourceFilesAtPaths('src/**'); + +const modelsFile = project.getSourceFile('src/models/index.ts'); +const modelsModule = modelsFile.getModuleOrThrow('Models'); + +const clientFile = project.getSourceFile('src/BitbucketCloudClient.ts'); +const clientClass = clientFile.getClassOrThrow('BitbucketCloudClient'); + +/** + * Returns an array of the unique items of the provided array. + * + * @param {string[]} array array with potentially non-unique items. + * @returns {string[]} array with unique items. + */ +function unique(array) { + return [...new Set(array)]; +} + +/** + * + * @param {tsMorph.ClassDeclaration | tsMorph.InterfaceDeclaration | tsMorph.TypeAliasDeclaration} stmt Statement like interface or type alias. + * @param {string[]=} processed Keeps track of which statement was already processed. + * @returns {string[]} + */ +function referencedModelsIdentifiers(stmt, processed) { + // eslint-disable-next-line no-param-reassign + processed = processed ?? []; + const name = stmt.getName(); + + if (processed.includes(name)) { + return []; + } + + const referenced = unique( + stmt + .getDescendantsOfKind(tsMorph.SyntaxKind.Identifier) + .map(it => it.getSymbol()) + .filter(it => it) + .map(it => it.getFullyQualifiedName()) + .filter(it => it.includes('Models.')) + .map(it => it.substring(it.indexOf('Models.') + 7)) + .filter(it => !it.includes('.')) + .filter(it => it !== name), + ); + processed.push(name); + + const transitivelyReferenced = referenced + .map( + it => + modelsModule.getInterface(it) ?? modelsModule.getTypeAliasOrThrow(it), + ) + .flatMap(it => referencedModelsIdentifiers(it, processed)); + + return unique([...referenced, ...transitivelyReferenced]); +} + +// all directly or transitively referenced/used `Models.[...]` are allowed to stay +const allowed = referencedModelsIdentifiers(clientClass); + +// remove everything not part of the "allow list" +modelsModule + .getInterfaces() + .filter(it => !allowed.includes(it.getName())) + .forEach(it => it.remove()); + +modelsModule + .getTypeAliases() + .filter(it => !allowed.includes(it.getName())) + .forEach(it => { + const varStmt = modelsModule.getVariableStatementOrThrow(it.getName()); + + it.remove(); + varStmt.remove(); + }); + +project.saveSync(); diff --git a/plugins/bitbucket-cloud-common/src/.openapi-generator-ignore b/plugins/bitbucket-cloud-common/src/.openapi-generator-ignore new file mode 100644 index 0000000000..aec8c32345 --- /dev/null +++ b/plugins/bitbucket-cloud-common/src/.openapi-generator-ignore @@ -0,0 +1,9 @@ +# OpenAPI Generator Ignore +# Generated by openapi-generator https://github.com/openapitools/openapi-generator + +# we maintain this file manually +index.ts + +# we only want the models to be generated +apis/ +runtime.ts diff --git a/plugins/bitbucket-cloud-common/src/.openapi-generator/FILES b/plugins/bitbucket-cloud-common/src/.openapi-generator/FILES new file mode 100644 index 0000000000..a93701aec1 --- /dev/null +++ b/plugins/bitbucket-cloud-common/src/.openapi-generator/FILES @@ -0,0 +1 @@ +models/index.ts diff --git a/plugins/bitbucket-cloud-common/src/.openapi-generator/VERSION b/plugins/bitbucket-cloud-common/src/.openapi-generator/VERSION new file mode 100644 index 0000000000..ec76f3788a --- /dev/null +++ b/plugins/bitbucket-cloud-common/src/.openapi-generator/VERSION @@ -0,0 +1 @@ +6.0.0-beta \ No newline at end of file diff --git a/plugins/bitbucket-cloud-common/src/BitbucketCloudClient.test.ts b/plugins/bitbucket-cloud-common/src/BitbucketCloudClient.test.ts new file mode 100644 index 0000000000..c5c0d604be --- /dev/null +++ b/plugins/bitbucket-cloud-common/src/BitbucketCloudClient.test.ts @@ -0,0 +1,110 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { BitbucketCloudIntegrationConfig } from '@backstage/integration'; +import { rest } from 'msw'; +import { setupServer } from 'msw/node'; +import { BitbucketCloudClient } from './BitbucketCloudClient'; +import { Models } from './models'; + +const server = setupServer(); + +describe('BitbucketCloudClient', () => { + const config: BitbucketCloudIntegrationConfig = { + host: 'bitbucket.org', + apiBaseUrl: 'https://api.bitbucket.org/2.0', + username: 'test-user', + appPassword: 'test-pw', + }; + const client = BitbucketCloudClient.fromConfig(config); + + beforeAll(() => server.listen({ onUnhandledRequest: 'error' })); + afterAll(() => server.close()); + afterEach(() => server.resetHandlers()); + + it('searchCode', async () => { + server.use( + rest.get( + `https://api.bitbucket.org/2.0/workspaces/ws/search/code`, + (req, res, ctx) => { + if ( + req.headers.get('authorization') !== + 'Basic dGVzdC11c2VyOnRlc3QtcHc=' + ) { + return res(ctx.status(400)); + } + + const query = req.url.searchParams.get('search_query'); + if (query !== 'query') { + return res(ctx.json({ values: [] } as Models.SearchResultPage)); + } + + const response: Models.SearchResultPage = { + values: [ + { + content_match_count: 1, + file: { + type: 'commit_file', + path: 'path/to/file', + }, + }, + ], + }; + return res(ctx.json(response)); + }, + ), + ); + + const pagination = client.searchCode('ws', 'query'); + + const results = []; + for await (const result of pagination.iterateResults()) { + results.push(result); + } + + expect(results).toHaveLength(1); + expect(results[0].file!.path).toEqual('path/to/file'); + }); + + it('listRepositoriesByWorkspace', async () => { + server.use( + rest.get( + 'https://api.bitbucket.org/2.0/repositories/ws', + (_, res, ctx) => { + const response = { + values: [ + { + type: 'repository', + slug: 'repo1', + } as Models.Repository, + ], + }; + return res(ctx.json(response)); + }, + ), + ); + + const pagination = client.listRepositoriesByWorkspace('ws'); + + const results = []; + for await (const result of pagination.iterateResults()) { + results.push(result); + } + + expect(results).toHaveLength(1); + expect(results[0].slug).toEqual('repo1'); + }); +}); diff --git a/plugins/bitbucket-cloud-common/src/BitbucketCloudClient.ts b/plugins/bitbucket-cloud-common/src/BitbucketCloudClient.ts new file mode 100644 index 0000000000..3ac005fe2b --- /dev/null +++ b/plugins/bitbucket-cloud-common/src/BitbucketCloudClient.ts @@ -0,0 +1,120 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { BitbucketCloudIntegrationConfig } from '@backstage/integration'; +import fetch, { Request } from 'cross-fetch'; +import { Models } from './models'; +import { WithPagination } from './pagination'; +import { + FilterAndSortOptions, + PartialResponseOptions, + RequestOptions, +} from './types'; + +/** @public */ +export class BitbucketCloudClient { + static fromConfig( + config: BitbucketCloudIntegrationConfig, + ): BitbucketCloudClient { + return new BitbucketCloudClient(config); + } + + private constructor( + private readonly config: BitbucketCloudIntegrationConfig, + ) {} + + searchCode( + workspace: string, + query: string, + options?: FilterAndSortOptions & PartialResponseOptions, + ): WithPagination { + const workspaceEnc = encodeURIComponent(workspace); + return new WithPagination( + paginationOptions => + this.createUrl(`/workspaces/${workspaceEnc}/search/code`, { + ...paginationOptions, + ...options, + search_query: query, + }), + url => this.getTypeMapped(url), + ); + } + + listRepositoriesByWorkspace( + workspace: string, + options?: FilterAndSortOptions & PartialResponseOptions, + ): WithPagination { + const workspaceEnc = encodeURIComponent(workspace); + + return new WithPagination( + paginationOptions => + this.createUrl(`/repositories/${workspaceEnc}`, { + ...paginationOptions, + ...options, + }), + url => this.getTypeMapped(url), + ); + } + + private createUrl(endpoint: string, options?: RequestOptions): URL { + const request = new URL(this.config.apiBaseUrl + endpoint); + for (const key in options) { + if (options[key]) { + request.searchParams.append(key, options[key]!.toString()); + } + } + + return request; + } + + private async getTypeMapped(url: URL): Promise { + return this.get(url).then( + (response: Response) => response.json() as Promise, + ); + } + + private async get(url: URL): Promise { + return this.request(new Request(url.toString(), { method: 'GET' })); + } + + private async request(req: Request): Promise { + return fetch(req, { headers: this.getAuthHeaders() }).then( + (response: Response) => { + if (!response.ok) { + throw new Error( + `Unexpected response for ${req.method} ${req.url}. Expected 200 but got ${response.status} - ${response.statusText}`, + ); + } + + return response; + }, + ); + } + + private getAuthHeaders(): Record { + const headers: Record = {}; + + if (this.config.username) { + const buffer = Buffer.from( + `${this.config.username}:${this.config.appPassword}`, + 'utf8', + ); + headers.Authorization = `Basic ${buffer.toString('base64')}`; + } + + return headers; + } +} diff --git a/plugins/bitbucket-cloud-common/src/index.ts b/plugins/bitbucket-cloud-common/src/index.ts new file mode 100644 index 0000000000..4edf148240 --- /dev/null +++ b/plugins/bitbucket-cloud-common/src/index.ts @@ -0,0 +1,26 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Common functionalities for bitbucket-cloud plugins. + * + * @packageDocumentation + */ + +export * from './BitbucketCloudClient'; +export * from './models'; +export * from './pagination'; +export * from './types'; diff --git a/plugins/bitbucket-cloud-common/src/models/index.ts b/plugins/bitbucket-cloud-common/src/models/index.ts new file mode 100644 index 0000000000..14cb8865b8 --- /dev/null +++ b/plugins/bitbucket-cloud-common/src/models/index.ts @@ -0,0 +1,532 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Bitbucket API + * Code against the Bitbucket API to automate simple tasks, embed Bitbucket data into your own site, build mobile or desktop apps, or even add custom UI add-ons into Bitbucket itself using the Connect framework. + * + * The version of the OpenAPI document: 2.0 + * Contact: support@bitbucket.org + * + * NOTE: This file was auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +/** @public */ +export namespace Models { + /** + * An account object. + * @public + */ + export interface Account extends ModelObject { + /** + * The status of the account. Currently the only possible value is "active", but more values may be added in the future. + */ + account_status?: string; + created_on?: string; + display_name?: string; + has_2fa_enabled?: boolean; + links?: AccountLinks; + /** + * Account name defined by the owner. Should be used instead of the "username" field. Note that "nickname" cannot be used in place of "username" in URLs and queries, as "nickname" is not guaranteed to be unique. + */ + nickname?: string; + username?: string; + uuid?: string; + website?: string; + } + + /** + * @public + */ + export interface AccountLinks { + avatar?: Link; + followers?: Link; + following?: Link; + html?: Link; + repositories?: Link; + self?: Link; + } + + /** + * The author of a change in a repository + * @public + */ + export interface Author extends ModelObject { + /** + * The raw author value from the repository. This may be the only value available if the author does not match a user in Bitbucket. + */ + raw?: string; + user?: Account; + } + + /** + * The common base type for both repository and snippet commits. + * @public + */ + export interface BaseCommit extends ModelObject { + author?: Author; + date?: string; + hash?: string; + message?: string; + parents?: Array; + summary?: BaseCommitSummary; + } + + /** + * @public + */ + export interface BaseCommitSummary { + /** + * The user's content rendered as HTML. + */ + html?: string; + /** + * The type of markup language the raw content is to be interpreted in. + */ + markup?: BaseCommitSummaryMarkupEnum; + /** + * The text as it was typed by a user. + */ + raw?: string; + } + + /** + * The type of markup language the raw content is to be interpreted in. + * @public + */ + export const BaseCommitSummaryMarkupEnum = { + Markdown: 'markdown', + Creole: 'creole', + Plaintext: 'plaintext', + } as const; + + /** + * The type of markup language the raw content is to be interpreted in. + * @public + */ + export type BaseCommitSummaryMarkupEnum = + typeof BaseCommitSummaryMarkupEnum[keyof typeof BaseCommitSummaryMarkupEnum]; + + /** + * A branch object, representing a branch in a repository. + * @public + */ + export interface Branch { + links?: RefLinks; + /** + * The name of the ref. + */ + name?: string; + target?: Commit; + type: string; + /** + * The default merge strategy for pull requests targeting this branch. + */ + default_merge_strategy?: string; + /** + * Available merge strategies for pull requests targeting this branch. + */ + merge_strategies?: Array; + } + + /** + * Available merge strategies for pull requests targeting this branch. + * @public + */ + export const BranchMergeStrategiesEnum = { + MergeCommit: 'merge_commit', + Squash: 'squash', + FastForward: 'fast_forward', + } as const; + + /** + * Available merge strategies for pull requests targeting this branch. + * @public + */ + export type BranchMergeStrategiesEnum = + typeof BranchMergeStrategiesEnum[keyof typeof BranchMergeStrategiesEnum]; + + /** + * A repository commit object. + * @public + */ + export interface Commit extends BaseCommit { + participants?: Array; + repository?: Repository; + } + + /** + * A file object, representing a file at a commit in a repository + * @public + */ + export interface CommitFile { + [key: string]: unknown; + attributes?: CommitFileAttributesEnum; + commit?: Commit; + /** + * The escaped version of the path as it appears in a diff. If the path does not require escaping this will be the same as path. + */ + escaped_path?: string; + /** + * The path in the repository + */ + path?: string; + type: string; + } + + /** + * @public + */ + export const CommitFileAttributesEnum = { + Link: 'link', + Executable: 'executable', + Subrepository: 'subrepository', + Binary: 'binary', + Lfs: 'lfs', + } as const; + + /** + * @public + */ + export type CommitFileAttributesEnum = + typeof CommitFileAttributesEnum[keyof typeof CommitFileAttributesEnum]; + + /** + * A link to a resource related to this object. + * @public + */ + export interface Link { + href?: string; + name?: string; + } + + /** + * Base type for most resource objects. It defines the common `type` element that identifies an object's type. It also identifies the element as Swagger's `discriminator`. + * @public + */ + export interface ModelObject { + [key: string]: unknown; + type: string; + } + + /** + * A generic paginated list. + * @public + */ + export interface Paginated { + /** + * Link to the next page if it exists. The last page of a collection does not have this value. Use this link to navigate the result set and refrain from constructing your own URLs. + */ + next?: string; + /** + * Page number of the current results. This is an optional element that is not provided in all responses. + */ + page?: number; + /** + * Current number of objects on the existing page. The default value is 10 with 100 being the maximum allowed value. Individual APIs may enforce different values. + */ + pagelen?: number; + /** + * Link to previous page if it exists. A collections first page does not have this value. This is an optional element that is not provided in all responses. Some result sets strictly support forward navigation and never provide previous links. Clients must anticipate that backwards navigation is not always available. Use this link to navigate the result set and refrain from constructing your own URLs. + */ + previous?: string; + /** + * Total number of objects in the response. This is an optional element that is not provided in all responses, as it can be expensive to compute. + */ + size?: number; + /** + * The values of the current page. + */ + values?: Array | Set; + } + + /** + * A paginated list of repositories. + * @public + */ + export interface PaginatedRepositories extends Paginated { + /** + * The values of the current page. + */ + values?: Set; + } + + /** + * Object describing a user's role on resources like commits or pull requests. + * @public + */ + export interface Participant extends ModelObject { + approved?: boolean; + /** + * The ISO8601 timestamp of the participant's action. For approvers, this is the time of their approval. For commenters and pull request reviewers who are not approvers, this is the time they last commented, or null if they have not commented. + */ + participated_on?: string; + role?: ParticipantRoleEnum; + state?: ParticipantStateEnum; + user?: User; + } + + /** + * @public + */ + export const ParticipantRoleEnum = { + Participant: 'PARTICIPANT', + Reviewer: 'REVIEWER', + } as const; + + /** + * @public + */ + export type ParticipantRoleEnum = + typeof ParticipantRoleEnum[keyof typeof ParticipantRoleEnum]; + + /** + * @public + */ + export const ParticipantStateEnum = { + Approved: 'approved', + ChangesRequested: 'changes_requested', + Null: 'null', + } as const; + + /** + * @public + */ + export type ParticipantStateEnum = + typeof ParticipantStateEnum[keyof typeof ParticipantStateEnum]; + + /** + * A Bitbucket project. + * Projects are used by teams to organize repositories. + * @public + */ + export interface Project extends ModelObject { + created_on?: string; + description?: string; + /** + * + * Indicates whether the project contains publicly visible repositories. + * Note that private projects cannot contain public repositories. + */ + has_publicly_visible_repos?: boolean; + /** + * + * Indicates whether the project is publicly accessible, or whether it is + * private to the team and consequently only visible to team members. + * Note that private projects cannot contain public repositories. + */ + is_private?: boolean; + /** + * The project's key. + */ + key?: string; + links?: ProjectLinks; + /** + * The name of the project. + */ + name?: string; + owner?: Team; + updated_on?: string; + /** + * The project's immutable id. + */ + uuid?: string; + } + + /** + * @public + */ + export interface ProjectLinks { + avatar?: Link; + html?: Link; + } + + /** + * @public + */ + export interface RefLinks { + commits?: Link; + html?: Link; + self?: Link; + } + + /** + * A Bitbucket repository. + * @public + */ + export interface Repository extends ModelObject { + created_on?: string; + description?: string; + /** + * + * Controls the rules for forking this repository. + * + * * **allow_forks**: unrestricted forking + * * **no_public_forks**: restrict forking to private forks (forks cannot + * be made public later) + * * **no_forks**: deny all forking + */ + fork_policy?: RepositoryForkPolicyEnum; + /** + * The concatenation of the repository owner's username and the slugified name, e.g. "evzijst/interruptingcow". This is the same string used in Bitbucket URLs. + */ + full_name?: string; + has_issues?: boolean; + has_wiki?: boolean; + is_private?: boolean; + language?: string; + links?: RepositoryLinks; + mainbranch?: Branch; + name?: string; + owner?: Account; + parent?: Repository; + project?: Project; + scm?: RepositoryScmEnum; + size?: number; + /** + * The "sluggified" version of the repository's name. This contains only ASCII characters and can therefore be slightly different than the name + */ + slug?: string; + updated_on?: string; + /** + * The repository's immutable id. This can be used as a substitute for the slug segment in URLs. Doing this guarantees your URLs will survive renaming of the repository by its owner, or even transfer of the repository to a different user. + */ + uuid?: string; + } + + /** + * + * Controls the rules for forking this repository. + * + * * **allow_forks**: unrestricted forking + * * **no_public_forks**: restrict forking to private forks (forks cannot + * be made public later) + * * **no_forks**: deny all forking + * @public + */ + export const RepositoryForkPolicyEnum = { + AllowForks: 'allow_forks', + NoPublicForks: 'no_public_forks', + NoForks: 'no_forks', + } as const; + + /** + * + * Controls the rules for forking this repository. + * + * * **allow_forks**: unrestricted forking + * * **no_public_forks**: restrict forking to private forks (forks cannot + * be made public later) + * * **no_forks**: deny all forking + * @public + */ + export type RepositoryForkPolicyEnum = + typeof RepositoryForkPolicyEnum[keyof typeof RepositoryForkPolicyEnum]; + + /** + * @public + */ + export const RepositoryScmEnum = { + Git: 'git', + } as const; + + /** + * @public + */ + export type RepositoryScmEnum = + typeof RepositoryScmEnum[keyof typeof RepositoryScmEnum]; + + /** + * @public + */ + export interface RepositoryLinks { + avatar?: Link; + clone?: Array; + commits?: Link; + downloads?: Link; + forks?: Link; + hooks?: Link; + html?: Link; + pullrequests?: Link; + self?: Link; + watchers?: Link; + } + + /** + * @public + */ + export interface SearchCodeSearchResult { + readonly content_match_count?: number; + readonly content_matches?: Array; + file?: CommitFile; + readonly path_matches?: Array; + readonly type?: string; + } + + /** + * @public + */ + export interface SearchContentMatch { + readonly lines?: Array; + } + + /** + * @public + */ + export interface SearchLine { + readonly line?: number; + readonly segments?: Array; + } + + /** + * @public + */ + export interface SearchResultPage extends Paginated { + readonly query_substituted?: boolean; + /** + * The values of the current page. + */ + readonly values?: Array; + } + + /** + * @public + */ + export interface SearchSegment { + readonly match?: boolean; + readonly text?: string; + } + + /** + * A team object. + * @public + */ + export interface Team extends Account {} + + /** + * A user object. + * @public + */ + export interface User extends Account { + /** + * The user's Atlassian account ID. + */ + account_id?: string; + is_staff?: boolean; + } +} diff --git a/plugins/bitbucket-cloud-common/src/pagination.test.ts b/plugins/bitbucket-cloud-common/src/pagination.test.ts new file mode 100644 index 0000000000..f263f51897 --- /dev/null +++ b/plugins/bitbucket-cloud-common/src/pagination.test.ts @@ -0,0 +1,124 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Models } from './models'; +import { WithPagination } from './pagination'; + +interface TestResultItem { + url: string; +} + +interface TestPage extends Models.Paginated {} + +describe('WithPagination', () => { + const createPagination = () => + new WithPagination( + opts => + new URL( + `http://localhost/create-url?page=${opts.page}&pagelen=${opts.pagelen}`, + ), + async url => { + const currentPage = Number.parseInt(url.searchParams.get('page')!, 10); + const next = new URL(url.toString()); + next.searchParams.set('page', (currentPage + 1).toString()); + + return { + page: currentPage, + ...(currentPage >= 2 ? {} : { next: next.toString() }), + values: [ + { + url: url.toString(), + }, + ], + }; + }, + ); + + it('iterateResults', async () => { + const pagination = createPagination(); + + const urls = []; + for await (const result of pagination.iterateResults()) { + urls.push(result.url.toString()); + } + + expect(urls).toHaveLength(2); + expect(urls).toEqual([ + 'http://localhost/create-url?page=1&pagelen=100', + 'http://localhost/create-url?page=2&pagelen=100', + ]); + }); + + it('iteratePages', async () => { + const pagination = createPagination(); + + const pages = []; + for await (const page of pagination.iteratePages()) { + pages.push(page); + } + + expect(pages).toHaveLength(2); + expect(pages).toEqual([ + { + next: 'http://localhost/create-url?page=2&pagelen=100', + page: 1, + values: [ + { + url: 'http://localhost/create-url?page=1&pagelen=100', + }, + ], + }, + { + page: 2, + values: [ + { + url: 'http://localhost/create-url?page=2&pagelen=100', + }, + ], + }, + ]); + }); + + describe('getPage', () => { + it('default opts', async () => { + const pagination = createPagination(); + + const page = await pagination.getPage(); + + expect(page.page).toEqual(1); + expect(page.next).toEqual( + 'http://localhost/create-url?page=2&pagelen=100', + ); + expect(page.values).toHaveLength(1); + expect((page.values! as TestResultItem[])[0].url).toEqual( + 'http://localhost/create-url?page=1&pagelen=100', + ); + }); + + it('custom opts', async () => { + const pagination = createPagination(); + + const page = await pagination.getPage({ page: 4 }); + + expect(page.page).toEqual(4); + expect(page.next).toBeUndefined(); + expect(page.values).toHaveLength(1); + expect((page.values! as TestResultItem[])[0].url).toEqual( + 'http://localhost/create-url?page=4&pagelen=100', + ); + }); + }); +}); diff --git a/plugins/bitbucket-cloud-common/src/pagination.ts b/plugins/bitbucket-cloud-common/src/pagination.ts new file mode 100644 index 0000000000..98e4f9f9d7 --- /dev/null +++ b/plugins/bitbucket-cloud-common/src/pagination.ts @@ -0,0 +1,66 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Models } from './models'; + +/** @public */ +export type PaginationOptions = { + page?: number; + pagelen?: number; +}; + +/** @public */ +export class WithPagination< + TPage extends Models.Paginated, + TResultItem, +> { + constructor( + private readonly createUrl: (options: PaginationOptions) => URL, + private readonly fetch: (url: URL) => Promise, + ) {} + + getPage(options?: PaginationOptions): Promise { + const opts = { page: 1, pagelen: 100, ...options }; + const url = this.createUrl(opts); + return this.fetch(url); + } + + async *iteratePages( + options?: PaginationOptions, + ): AsyncGenerator { + const opts = { page: 1, pagelen: 100, ...options }; + let url: URL | undefined = this.createUrl(opts); + let res; + do { + res = await this.fetch(url); + url = res.next ? new URL(res.next) : undefined; + yield res; + } while (url); + } + + async *iterateResults(options?: PaginationOptions) { + const opts = { page: 1, pagelen: 100, ...options }; + let url: URL | undefined = this.createUrl(opts); + let res; + do { + res = await this.fetch(url); + url = res.next ? new URL(res.next) : undefined; + for (const item of res.values ?? []) { + yield item; + } + } while (url); + } +} diff --git a/plugins/bitbucket-cloud-common/src/setupTests.ts b/plugins/bitbucket-cloud-common/src/setupTests.ts new file mode 100644 index 0000000000..8b9b6bd586 --- /dev/null +++ b/plugins/bitbucket-cloud-common/src/setupTests.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export {}; diff --git a/plugins/bitbucket-cloud-common/src/types.ts b/plugins/bitbucket-cloud-common/src/types.ts new file mode 100644 index 0000000000..8de4abc6f1 --- /dev/null +++ b/plugins/bitbucket-cloud-common/src/types.ts @@ -0,0 +1,35 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { PaginationOptions } from './pagination'; + +/** @public */ +export type FilterAndSortOptions = { + q?: string; + sort?: string; +}; + +/** @public */ +export type PartialResponseOptions = { + fields?: string; +}; + +/** @public */ +export type RequestOptions = FilterAndSortOptions & + PaginationOptions & + PartialResponseOptions & { + [key: string]: string | number | undefined; + }; diff --git a/plugins/bitbucket-cloud-common/templates/licenseInfo.mustache b/plugins/bitbucket-cloud-common/templates/licenseInfo.mustache new file mode 100644 index 0000000000..118dbbdfdb --- /dev/null +++ b/plugins/bitbucket-cloud-common/templates/licenseInfo.mustache @@ -0,0 +1,11 @@ +/** + * {{{appName}}} + * {{{appDescription}}} + * + * {{#version}}The version of the OpenAPI document: {{{.}}}{{/version}} + * {{#infoEmail}}Contact: {{{.}}}{{/infoEmail}} + * + * NOTE: This file was auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ diff --git a/plugins/bitbucket-cloud-common/templates/modelGenericInterfaces.mustache b/plugins/bitbucket-cloud-common/templates/modelGenericInterfaces.mustache new file mode 100644 index 0000000000..38027d21a7 --- /dev/null +++ b/plugins/bitbucket-cloud-common/templates/modelGenericInterfaces.mustache @@ -0,0 +1,61 @@ +/** +{{#unescapedDescription}} + * {{#lambda.indented_star_1}}{{{unescapedDescription}}}{{/lambda.indented_star_1}} +{{/unescapedDescription}} + * @public +{{#deprecated}} + * @deprecated +{{/deprecated}} + */ +export interface {{classname}} {{#parent}}extends {{{.}}} {{/parent}}{ +{{#additionalPropertiesType}} + [key: string]: unknown; +{{/additionalPropertiesType}} +{{#vars}} +{{#unescapedDescription}} + /** + * {{#lambda.indented_star_4}}{{{unescapedDescription}}}{{/lambda.indented_star_4}} + {{#deprecated}} + * @deprecated + {{/deprecated}} + */ + {{/unescapedDescription}} + {{^unescapedDescription}} + {{#deprecated}} + /** @deprecated */ + {{/deprecated}} + {{/unescapedDescription}} + {{#isReadOnly}}readonly {{/isReadOnly}}{{name}}{{^required}}?{{/required}}: {{#isEnum}}{{{datatypeWithEnum}}}{{/isEnum}}{{^isEnum}}{{{datatype}}}{{#isNullable}} | null{{/isNullable}}{{/isEnum}}; +{{/vars}} +}{{#hasEnums}} +{{#vars}} +{{#isEnum}} + +/** +{{#unescapedDescription}} + * {{#lambda.indented_star_1}}{{{unescapedDescription}}}{{/lambda.indented_star_1}} +{{/unescapedDescription}} + * @public +{{#deprecated}} + * @deprecated +{{/deprecated}} + */ +export const {{classname}}{{enumName}} = { +{{#allowableValues}} + {{#enumVars}} + {{{name}}}: {{{value}}}{{^-last}},{{/-last}} + {{/enumVars}} +{{/allowableValues}} +} as const; + +/** +{{#unescapedDescription}} + * {{#lambda.indented_star_1}}{{{unescapedDescription}}}{{/lambda.indented_star_1}} +{{/unescapedDescription}} + * @public +{{#deprecated}} + * @deprecated +{{/deprecated}} + */ +export type {{classname}}{{enumName}} = typeof {{classname}}{{enumName}}[keyof typeof {{classname}}{{enumName}}]; +{{/isEnum}}{{/vars}}{{/hasEnums}} diff --git a/plugins/bitbucket-cloud-common/templates/models.index.mustache b/plugins/bitbucket-cloud-common/templates/models.index.mustache new file mode 100644 index 0000000000..9e9dbd697e --- /dev/null +++ b/plugins/bitbucket-cloud-common/templates/models.index.mustache @@ -0,0 +1,47 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +{{>licenseInfo}} +/** @public */ +export namespace Models { +{{#models}} +{{#model}} +{{^withoutRuntimeChecks}} +export * from './{{{ classFilename }}}'; +{{#useSagaAndRecords}} +{{^isEnum}} +export * from './{{{ classFilename }}}Record'; +{{/isEnum}} +{{/useSagaAndRecords}} +{{/withoutRuntimeChecks}} +{{#withoutRuntimeChecks}} +{{#isEnum}} +{{>modelEnumInterfaces}} +{{/isEnum}} +{{^isEnum}} +{{#oneOf}} +{{#-first}} +{{>modelOneOfInterfaces}} +{{/-first}} +{{/oneOf}} +{{^oneOf}} +{{>modelGenericInterfaces}} +{{/oneOf}} +{{/isEnum}} +{{/withoutRuntimeChecks}} +{{/model}} +{{/models}} +} diff --git a/plugins/bitrise/CHANGELOG.md b/plugins/bitrise/CHANGELOG.md index 5da5158f2a..0badfe894d 100644 --- a/plugins/bitrise/CHANGELOG.md +++ b/plugins/bitrise/CHANGELOG.md @@ -1,5 +1,35 @@ # @backstage/plugin-bitrise +## 0.1.33 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## 0.1.33-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## 0.1.33-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + ## 0.1.32 ### Patch Changes diff --git a/plugins/bitrise/package.json b/plugins/bitrise/package.json index 26a652d2a0..1d17b5a4b5 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.32", + "version": "0.1.33", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -24,10 +24,10 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-model": "^1.0.2", - "@backstage/core-components": "^0.9.4", - "@backstage/core-plugin-api": "^1.0.2", - "@backstage/plugin-catalog-react": "^1.1.0", + "@backstage/catalog-model": "^1.0.3", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", + "@backstage/plugin-catalog-react": "^1.1.1", "@backstage/theme": "^0.2.15", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -43,17 +43,17 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/core-app-api": "^1.0.2", - "@backstage/dev-utils": "^1.0.2", - "@backstage/test-utils": "^1.1.0", + "@backstage/cli": "^0.17.2", + "@backstage/core-app-api": "^1.0.3", + "@backstage/dev-utils": "^1.0.3", + "@backstage/test-utils": "^1.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", "@types/jest": "^26.0.7", "@types/node": "^16.11.26", "@types/recharts": "^1.8.15", - "msw": "^0.35.0" + "msw": "^0.42.0" }, "files": [ "dist" diff --git a/plugins/catalog-backend-module-aws/CHANGELOG.md b/plugins/catalog-backend-module-aws/CHANGELOG.md index 58c5b25492..d246ae9677 100644 --- a/plugins/catalog-backend-module-aws/CHANGELOG.md +++ b/plugins/catalog-backend-module-aws/CHANGELOG.md @@ -1,5 +1,49 @@ # @backstage/plugin-catalog-backend-module-aws +## 0.1.6 + +### Patch Changes + +- eb2544b21b: Inline config interfaces +- Updated dependencies + - @backstage/plugin-catalog-backend@1.2.0 + - @backstage/backend-tasks@0.3.2 + - @backstage/backend-common@0.14.0 + - @backstage/integration@1.2.1 + - @backstage/catalog-model@1.0.3 + +## 0.1.6-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0-next.2 + - @backstage/integration@1.2.1-next.2 + - @backstage/backend-tasks@0.3.2-next.2 + - @backstage/plugin-catalog-backend@1.2.0-next.2 + +## 0.1.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-tasks@0.3.2-next.1 + - @backstage/backend-common@0.13.6-next.1 + - @backstage/integration@1.2.1-next.1 + - @backstage/plugin-catalog-backend@1.2.0-next.1 + - @backstage/catalog-model@1.0.3-next.0 + +## 0.1.6-next.0 + +### Patch Changes + +- eb2544b21b: Inline config interfaces +- Updated dependencies + - @backstage/backend-tasks@0.3.2-next.0 + - @backstage/backend-common@0.13.6-next.0 + - @backstage/integration@1.2.1-next.0 + - @backstage/plugin-catalog-backend@1.2.0-next.0 + ## 0.1.5 ### Patch Changes diff --git a/plugins/catalog-backend-module-aws/package.json b/plugins/catalog-backend-module-aws/package.json index 8f340f3f8a..53ed33c47d 100644 --- a/plugins/catalog-backend-module-aws/package.json +++ b/plugins/catalog-backend-module-aws/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-aws", "description": "A Backstage catalog backend module that helps integrate towards AWS", - "version": "0.1.5", + "version": "0.1.6", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -33,13 +33,13 @@ "start": "backstage-cli package start" }, "dependencies": { - "@backstage/backend-common": "^0.13.3", - "@backstage/backend-tasks": "^0.3.1", - "@backstage/catalog-model": "^1.0.2", + "@backstage/backend-common": "^0.14.0", + "@backstage/backend-tasks": "^0.3.2", + "@backstage/catalog-model": "^1.0.3", "@backstage/config": "^1.0.1", "@backstage/errors": "^1.0.0", - "@backstage/integration": "^1.2.0", - "@backstage/plugin-catalog-backend": "^1.1.2", + "@backstage/integration": "^1.2.1", + "@backstage/plugin-catalog-backend": "^1.2.0", "@backstage/types": "^1.0.0", "aws-sdk": "^2.840.0", "lodash": "^4.17.21", @@ -48,7 +48,7 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/cli": "^0.17.1", + "@backstage/cli": "^0.17.2", "@types/lodash": "^4.14.151", "aws-sdk-mock": "^5.2.1", "yaml": "^1.9.2" diff --git a/plugins/catalog-backend-module-azure/CHANGELOG.md b/plugins/catalog-backend-module-azure/CHANGELOG.md index c01db4760c..fcc8126ad0 100644 --- a/plugins/catalog-backend-module-azure/CHANGELOG.md +++ b/plugins/catalog-backend-module-azure/CHANGELOG.md @@ -1,5 +1,171 @@ # @backstage/plugin-catalog-backend-module-azure +## 0.1.4 + +### Patch Changes + +- b8884fd579: Add a new provider `AzureDevOpsEntityProvider` as replacement for `AzureDevOpsDiscoveryProcessor`. + + In order to migrate from the `AzureDevOpsDiscoveryProcessor` you need to apply + the following changes: + + **Before:** + + ```yaml + # app-config.yaml + + catalog: + locations: + - type: azure-discovery + target: https://dev.azure.com/myorg/myproject/_git/service-*?path=/catalog-info.yaml + ``` + + ```ts + /* packages/backend/src/plugins/catalog.ts */ + + import { AzureDevOpsDiscoveryProcessor } from '@backstage/plugin-catalog-backend-module-azure'; + + const builder = await CatalogBuilder.create(env); + /** ... other processors ... */ + builder.addProcessor(new AzureDevOpsDiscoveryProcessor(env.reader)); + ``` + + **After:** + + ```yaml + # app-config.yaml + + catalog: + providers: + azureDevOps: + anyProviderId: + host: selfhostedazure.yourcompany.com # This is only really needed for on-premise user, defaults to dev.azure.com + organization: myorg # For on-premise this would be your Collection + project: myproject + repository: service-* + path: /catalog-info.yaml + ``` + + ```ts + /* packages/backend/src/plugins/catalog.ts */ + + import { AzureDevOpsEntityProvider } from '@backstage/plugin-catalog-backend-module-azure'; + + const builder = await CatalogBuilder.create(env); + /** ... other processors and/or providers ... */ + builder.addEntityProvider( + AzureDevOpsEntityProvider.fromConfig(env.config, { + logger: env.logger, + schedule: env.scheduler.createScheduledTaskRunner({ + frequency: { minutes: 30 }, + timeout: { minutes: 3 }, + }), + }), + ); + ``` + + Visit [https://backstage.io/docs/integrations/azure/discovery](https://backstage.io/docs/integrations/azure/discovery) for more details and options on configuration. + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-backend@1.2.0 + - @backstage/backend-tasks@0.3.2 + - @backstage/backend-common@0.14.0 + - @backstage/integration@1.2.1 + - @backstage/catalog-model@1.0.3 + +## 0.1.4-next.2 + +### Patch Changes + +- b8884fd579: Add a new provider `AzureDevOpsEntityProvider` as replacement for `AzureDevOpsDiscoveryProcessor`. + + In order to migrate from the `AzureDevOpsDiscoveryProcessor` you need to apply + the following changes: + + **Before:** + + ```yaml + # app-config.yaml + + catalog: + locations: + - type: azure-discovery + target: https://dev.azure.com/myorg/myproject/_git/service-*?path=/catalog-info.yaml + ``` + + ```ts + /* packages/backend/src/plugins/catalog.ts */ + + import { AzureDevOpsDiscoveryProcessor } from '@backstage/plugin-catalog-backend-module-azure'; + + const builder = await CatalogBuilder.create(env); + /** ... other processors ... */ + builder.addProcessor(new AzureDevOpsDiscoveryProcessor(env.reader)); + ``` + + **After:** + + ```yaml + # app-config.yaml + + catalog: + providers: + azureDevOps: + anyProviderId: + host: selfhostedazure.yourcompany.com # This is only really needed for on-premise user, defaults to dev.azure.com + organization: myorg # For on-premise this would be your Collection + project: myproject + repository: service-* + path: /catalog-info.yaml + ``` + + ```ts + /* packages/backend/src/plugins/catalog.ts */ + + import { AzureDevOpsEntityProvider } from '@backstage/plugin-catalog-backend-module-azure'; + + const builder = await CatalogBuilder.create(env); + /** ... other processors and/or providers ... */ + builder.addEntityProvider( + AzureDevOpsEntityProvider.fromConfig(env.config, { + logger: env.logger, + schedule: env.scheduler.createScheduledTaskRunner({ + frequency: { minutes: 30 }, + timeout: { minutes: 3 }, + }), + }), + ); + ``` + + Visit [https://backstage.io/docs/integrations/azure/discovery](https://backstage.io/docs/integrations/azure/discovery) for more details and options on configuration. + +- Updated dependencies + - @backstage/backend-common@0.14.0-next.2 + - @backstage/integration@1.2.1-next.2 + - @backstage/backend-tasks@0.3.2-next.2 + - @backstage/plugin-catalog-backend@1.2.0-next.2 + +## 0.1.4-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/backend-common@0.13.6-next.1 + - @backstage/integration@1.2.1-next.1 + - @backstage/plugin-catalog-backend@1.2.0-next.1 + - @backstage/catalog-model@1.0.3-next.0 + +## 0.1.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + - @backstage/integration@1.2.1-next.0 + - @backstage/plugin-catalog-backend@1.2.0-next.0 + ## 0.1.3 ### Patch Changes diff --git a/plugins/catalog-backend-module-azure/api-report.md b/plugins/catalog-backend-module-azure/api-report.md index 139db6aab5..22f5015c9b 100644 --- a/plugins/catalog-backend-module-azure/api-report.md +++ b/plugins/catalog-backend-module-azure/api-report.md @@ -6,9 +6,12 @@ import { CatalogProcessor } from '@backstage/plugin-catalog-backend'; import { CatalogProcessorEmit } from '@backstage/plugin-catalog-backend'; import { Config } from '@backstage/config'; +import { EntityProvider } from '@backstage/plugin-catalog-backend'; +import { EntityProviderConnection } from '@backstage/plugin-catalog-backend'; import { LocationSpec } from '@backstage/plugin-catalog-backend'; import { Logger } from 'winston'; import { ScmIntegrationRegistry } from '@backstage/integration'; +import { TaskRunner } from '@backstage/backend-tasks'; // @public export class AzureDevOpsDiscoveryProcessor implements CatalogProcessor { @@ -32,4 +35,22 @@ export class AzureDevOpsDiscoveryProcessor implements CatalogProcessor { emit: CatalogProcessorEmit, ): Promise; } + +// @public +export class AzureDevOpsEntityProvider implements EntityProvider { + // (undocumented) + connect(connection: EntityProviderConnection): Promise; + // (undocumented) + static fromConfig( + configRoot: Config, + options: { + logger: Logger; + schedule: TaskRunner; + }, + ): AzureDevOpsEntityProvider[]; + // (undocumented) + getProviderName(): string; + // (undocumented) + refresh(logger: Logger): Promise; +} ``` diff --git a/plugins/catalog-backend-module-azure/config.d.ts b/plugins/catalog-backend-module-azure/config.d.ts new file mode 100644 index 0000000000..6e423c9415 --- /dev/null +++ b/plugins/catalog-backend-module-azure/config.d.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. + */ + +interface AzureDevOpsConfig { + /** + * (Optional) The DevOps host; leave empty for `dev.azure.com`, otherwise set to your self-hosted instance host. + * @visibility backend + */ + host: string; + /** + * (Required) Your organization slug. + * @visibility backend + */ + organization: string; + /** + * (Required) Your project slug. + * @visibility backend + */ + project: string; + /** + * (Optional) The repository name. Wildcards are supported as show on the examples above. + * If not set, all repositories will be searched. + * @visibility backend + */ + repository?: string; + /** + * (Optional) Where to find catalog-info.yaml files. Wildcards are supported. + * If not set, defaults to /catalog-info.yaml. + * @visibility backend + */ + path?: string; +} + +export interface Config { + catalog?: { + /** + * List of provider-specific options and attributes + */ + providers?: { + /** + * AzureDevopsEntityProvider configuration + */ + azureDevOps?: Record; + }; + }; +} diff --git a/plugins/catalog-backend-module-azure/package.json b/plugins/catalog-backend-module-azure/package.json index 9d5d56ac73..5b0966f960 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.3", + "version": "0.1.4", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -33,24 +33,28 @@ "start": "backstage-cli package start" }, "dependencies": { - "@backstage/backend-common": "^0.13.3", - "@backstage/catalog-model": "^1.0.2", + "@backstage/backend-common": "^0.14.0", + "@backstage/catalog-model": "^1.0.3", + "@backstage/backend-tasks": "^0.3.2", "@backstage/config": "^1.0.1", "@backstage/errors": "^1.0.0", - "@backstage/integration": "^1.2.0", - "@backstage/plugin-catalog-backend": "^1.1.2", + "@backstage/integration": "^1.2.1", + "@backstage/plugin-catalog-backend": "^1.2.0", "@backstage/types": "^1.0.0", "lodash": "^4.17.21", - "msw": "^0.35.0", + "msw": "^0.42.0", + "uuid": "^8.0.0", "node-fetch": "^2.6.7", "winston": "^3.2.1" }, "devDependencies": { - "@backstage/backend-test-utils": "^0.1.24", - "@backstage/cli": "^0.17.1", + "@backstage/backend-test-utils": "^0.1.25", + "@backstage/cli": "^0.17.2", "@types/lodash": "^4.14.151" }, "files": [ - "dist" - ] + "dist", + "config.d.ts" + ], + "configSchema": "config.d.ts" } diff --git a/plugins/catalog-backend-module-azure/src/index.ts b/plugins/catalog-backend-module-azure/src/index.ts index f6c61e52a5..a71c40ee6c 100644 --- a/plugins/catalog-backend-module-azure/src/index.ts +++ b/plugins/catalog-backend-module-azure/src/index.ts @@ -20,4 +20,5 @@ * @packageDocumentation */ -export { AzureDevOpsDiscoveryProcessor } from './AzureDevOpsDiscoveryProcessor'; +export { AzureDevOpsDiscoveryProcessor } from './processors'; +export { AzureDevOpsEntityProvider } from './providers'; diff --git a/plugins/catalog-backend-module-azure/src/AzureDevOpsDiscoveryProcessor.test.ts b/plugins/catalog-backend-module-azure/src/processors/AzureDevOpsDiscoveryProcessor.test.ts similarity index 99% rename from plugins/catalog-backend-module-azure/src/AzureDevOpsDiscoveryProcessor.test.ts rename to plugins/catalog-backend-module-azure/src/processors/AzureDevOpsDiscoveryProcessor.test.ts index e6eed8d976..9000b177fa 100644 --- a/plugins/catalog-backend-module-azure/src/AzureDevOpsDiscoveryProcessor.test.ts +++ b/plugins/catalog-backend-module-azure/src/processors/AzureDevOpsDiscoveryProcessor.test.ts @@ -21,9 +21,9 @@ import { AzureDevOpsDiscoveryProcessor, parseUrl, } from './AzureDevOpsDiscoveryProcessor'; -import { codeSearch } from './lib'; +import { codeSearch } from '../lib'; -jest.mock('./lib'); +jest.mock('../lib'); const mockCodeSearch = codeSearch as jest.MockedFunction; describe('AzureDevOpsDiscoveryProcessor', () => { diff --git a/plugins/catalog-backend-module-azure/src/AzureDevOpsDiscoveryProcessor.ts b/plugins/catalog-backend-module-azure/src/processors/AzureDevOpsDiscoveryProcessor.ts similarity index 99% rename from plugins/catalog-backend-module-azure/src/AzureDevOpsDiscoveryProcessor.ts rename to plugins/catalog-backend-module-azure/src/processors/AzureDevOpsDiscoveryProcessor.ts index bdc7255f44..ad63fef810 100644 --- a/plugins/catalog-backend-module-azure/src/AzureDevOpsDiscoveryProcessor.ts +++ b/plugins/catalog-backend-module-azure/src/processors/AzureDevOpsDiscoveryProcessor.ts @@ -26,7 +26,7 @@ import { processingResult, } from '@backstage/plugin-catalog-backend'; import { Logger } from 'winston'; -import { codeSearch } from './lib'; +import { codeSearch } from '../lib'; /** * Extracts repositories out of an Azure DevOps org. diff --git a/plugins/catalog-backend-module-azure/src/processors/index.ts b/plugins/catalog-backend-module-azure/src/processors/index.ts new file mode 100644 index 0000000000..9c76336804 --- /dev/null +++ b/plugins/catalog-backend-module-azure/src/processors/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { AzureDevOpsDiscoveryProcessor } from './AzureDevOpsDiscoveryProcessor'; diff --git a/plugins/catalog-backend-module-azure/src/providers/AzureDevOpsEntityProvider.test.ts b/plugins/catalog-backend-module-azure/src/providers/AzureDevOpsEntityProvider.test.ts new file mode 100644 index 0000000000..08379807d3 --- /dev/null +++ b/plugins/catalog-backend-module-azure/src/providers/AzureDevOpsEntityProvider.test.ts @@ -0,0 +1,195 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { getVoidLogger } from '@backstage/backend-common'; +import { TaskInvocationDefinition, TaskRunner } from '@backstage/backend-tasks'; +import { ConfigReader } from '@backstage/config'; +import { EntityProviderConnection } from '@backstage/plugin-catalog-backend'; +import { CodeSearchResultItem } from '../lib'; +import { AzureDevOpsEntityProvider } from './AzureDevOpsEntityProvider'; +import { codeSearch } from '../lib'; + +jest.mock('../lib'); +const mockCodeSearch = codeSearch as jest.MockedFunction; + +class PersistingTaskRunner implements TaskRunner { + private tasks: TaskInvocationDefinition[] = []; + + getTasks() { + return this.tasks; + } + + run(task: TaskInvocationDefinition): Promise { + this.tasks.push(task); + return Promise.resolve(undefined); + } +} + +const logger = getVoidLogger(); + +describe('AzureDevOpsEntityProvider', () => { + afterEach(() => { + mockCodeSearch.mockClear(); + }); + + const expectMutation = async ( + providerId: string, + providerConfig: object, + codeSearchResults: CodeSearchResultItem[], + expectedBaseUrl: string, + names: Record, + integrationConfig?: object, + ) => { + const config = new ConfigReader({ + integrations: { + azure: integrationConfig ? [integrationConfig] : [], + }, + catalog: { + providers: { + azureDevOps: { + [providerId]: providerConfig, + }, + }, + }, + }); + + mockCodeSearch.mockResolvedValueOnce(codeSearchResults); + + const schedule = new PersistingTaskRunner(); + const entityProviderConnection: EntityProviderConnection = { + applyMutation: jest.fn(), + }; + + const provider = AzureDevOpsEntityProvider.fromConfig(config, { + logger, + schedule, + })[0]; + expect(provider.getProviderName()).toEqual( + `AzureDevOpsEntityProvider:${providerId}`, + ); + + await provider.connect(entityProviderConnection); + + const taskDef = schedule.getTasks()[0]; + expect(taskDef.id).toEqual( + `AzureDevOpsEntityProvider:${providerId}:refresh`, + ); + await (taskDef.fn as () => Promise)(); + + const expectedEntities = codeSearchResults.map(item => { + const url = encodeURI( + `${expectedBaseUrl}/_git/${item.repository.name}?path=${item.path}`, + ); + return { + entity: { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Location', + metadata: { + annotations: { + 'backstage.io/managed-by-location': `url:${url}`, + 'backstage.io/managed-by-origin-location': `url:${url}`, + }, + name: names[`${item.repository.name}?path=${item.path}`], + }, + spec: { + presence: 'required', + target: `${url}`, + type: 'url', + }, + }, + locationKey: `AzureDevOpsEntityProvider:${providerId}`, + }; + }); + + expect(entityProviderConnection.applyMutation).toBeCalledWith({ + type: 'full', + entities: expectedEntities, + }); + }; + + // eslint-disable-next-line jest/expect-expect + it('no mutation when repos are empty', async () => { + return expectMutation( + 'allRepos', + { + organization: 'myorganization', + project: 'myproject', + }, + [], + 'https://dev.azure.com/myorganization/myproject', + {}, + ); + }); + + // eslint-disable-next-line jest/expect-expect + it('single mutation when repos have 1 file found', async () => { + return expectMutation( + 'allReposSingleFile', + { + organization: 'myorganization', + project: 'myproject', + }, + [ + { + fileName: 'catalog-info.yaml', + path: '/catalog-info.yaml', + repository: { + name: 'myrepo', + }, + }, + ], + 'https://dev.azure.com/myorganization/myproject', + { + 'myrepo?path=/catalog-info.yaml': + 'generated-87865246726bb12a8c4fb4f914443f1fbb91648c', + }, + ); + }); + + // eslint-disable-next-line jest/expect-expect + it('single mutation when multiple repos have multiple files', async () => { + return expectMutation( + 'allReposMultipleFiles', + { + organization: 'myorganization', + project: 'myproject', + }, + [ + { + fileName: 'catalog-info.yaml', + path: '/catalog-info.yaml', + repository: { + name: 'myrepo', + }, + }, + { + fileName: 'catalog-info.yaml', + path: '/catalog-info.yaml', + repository: { + name: 'myotherrepo', + }, + }, + ], + 'https://dev.azure.com/myorganization/myproject', + { + 'myrepo?path=/catalog-info.yaml': + 'generated-87865246726bb12a8c4fb4f914443f1fbb91648c', + 'myotherrepo?path=/catalog-info.yaml': + 'generated-2deccac384c34d0dca37be0ebb4b1c8cf6913fe1', + }, + ); + }); +}); diff --git a/plugins/catalog-backend-module-azure/src/providers/AzureDevOpsEntityProvider.ts b/plugins/catalog-backend-module-azure/src/providers/AzureDevOpsEntityProvider.ts new file mode 100644 index 0000000000..aca30f367a --- /dev/null +++ b/plugins/catalog-backend-module-azure/src/providers/AzureDevOpsEntityProvider.ts @@ -0,0 +1,167 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { TaskRunner } from '@backstage/backend-tasks'; +import { Config } from '@backstage/config'; +import { AzureIntegration, ScmIntegrations } from '@backstage/integration'; +import { + EntityProvider, + EntityProviderConnection, + LocationSpec, + locationSpecToLocationEntity, +} from '@backstage/plugin-catalog-backend'; +import { readAzureDevOpsConfigs } from './config'; +import { Logger } from 'winston'; +import { AzureDevOpsConfig } from './types'; +import * as uuid from 'uuid'; +import { codeSearch, CodeSearchResultItem } from '../lib'; + +/** + * Provider which discovers catalog files within an Azure DevOps repositories. + * + * Use `AzureDevOpsEntityProvider.fromConfig(...)` to create instances. + * + * @public + */ +export class AzureDevOpsEntityProvider implements EntityProvider { + private readonly logger: Logger; + private readonly scheduleFn: () => Promise; + private connection?: EntityProviderConnection; + + static fromConfig( + configRoot: Config, + options: { + logger: Logger; + schedule: TaskRunner; + }, + ): AzureDevOpsEntityProvider[] { + const providerConfigs = readAzureDevOpsConfigs(configRoot); + + return providerConfigs.map(providerConfig => { + const integration = ScmIntegrations.fromConfig(configRoot).azure.byHost( + providerConfig.host, + ); + + if (!integration) { + throw new Error( + `There is no Azure integration for host ${providerConfig.host}. Please add a configuration entry for it under integrations.azure`, + ); + } + + return new AzureDevOpsEntityProvider( + providerConfig, + integration, + options.logger, + options.schedule, + ); + }); + } + + private constructor( + private readonly config: AzureDevOpsConfig, + private readonly integration: AzureIntegration, + logger: Logger, + schedule: TaskRunner, + ) { + this.logger = logger.child({ + target: this.getProviderName(), + }); + + this.scheduleFn = this.createScheduleFn(schedule); + } + + private createScheduleFn(schedule: TaskRunner): () => Promise { + return async () => { + const taskId = `${this.getProviderName()}:refresh`; + return schedule.run({ + id: taskId, + fn: async () => { + const logger = this.logger.child({ + class: AzureDevOpsEntityProvider.prototype.constructor.name, + taskId, + taskInstanceId: uuid.v4(), + }); + + try { + await this.refresh(logger); + } catch (error) { + logger.error(error); + } + }, + }); + }; + } + + /** {@inheritdoc @backstage/plugin-catalog-backend#EntityProvider.getProviderName} */ + getProviderName(): string { + return `AzureDevOpsEntityProvider:${this.config.id}`; + } + + /** {@inheritdoc @backstage/plugin-catalog-backend#EntityProvider.connect} */ + async connect(connection: EntityProviderConnection): Promise { + this.connection = connection; + await this.scheduleFn(); + } + + async refresh(logger: Logger) { + if (!this.connection) { + throw new Error('Not initialized'); + } + + logger.info('Discovering Azure DevOps catalog files'); + + const files = await codeSearch( + this.integration.config, + this.config.organization, + this.config.project, + this.config.repository, + this.config.path, + ); + + logger.info(`Discovered ${files.length} catalog files`); + + const locations = files.map(key => this.createLocationSpec(key)); + + await this.connection.applyMutation({ + type: 'full', + entities: locations.map(location => { + return { + locationKey: this.getProviderName(), + entity: locationSpecToLocationEntity({ location }), + }; + }), + }); + + logger.info( + `Committed ${locations.length} locations for AzureDevOps catalog files`, + ); + } + + private createLocationSpec(file: CodeSearchResultItem): LocationSpec { + return { + type: 'url', + target: this.createObjectUrl(file), + presence: 'required', + }; + } + + private createObjectUrl(file: CodeSearchResultItem): string { + const baseUrl = `https://${this.config.host}/${this.config.organization}/${this.config.project}`; + return encodeURI( + `${baseUrl}/_git/${file.repository.name}?path=${file.path}`, + ); + } +} diff --git a/plugins/catalog-backend-module-azure/src/providers/config.test.ts b/plugins/catalog-backend-module-azure/src/providers/config.test.ts new file mode 100644 index 0000000000..236c9a48d4 --- /dev/null +++ b/plugins/catalog-backend-module-azure/src/providers/config.test.ts @@ -0,0 +1,67 @@ +/* + * 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 { ConfigReader } from '@backstage/config'; +import { readAzureDevOpsConfigs } from './config'; + +describe('readAzureDevOpsConfigs', () => { + it('reads all provider configs and set default values', () => { + const provider1 = { + host: 'azure.mycompany.com', + organization: 'mycompany', + project: 'myproject', + }; + const provider2 = { + organization: 'mycompany', + project: 'myproject', + }; + const provider3 = { + organization: 'mycompany', + project: 'myproject', + repository: 'service-*', + }; + const config = { + catalog: { + providers: { + azureDevOps: { provider1, provider2, provider3 }, + }, + }, + }; + + const actual = readAzureDevOpsConfigs(new ConfigReader(config)); + + expect(actual).toHaveLength(3); + expect(actual[0]).toEqual({ + ...provider1, + path: '/catalog-info.yaml', + repository: '*', + id: 'provider1', + }); + expect(actual[1]).toEqual({ + ...provider2, + host: 'dev.azure.com', + path: '/catalog-info.yaml', + repository: '*', + id: 'provider2', + }); + expect(actual[2]).toEqual({ + ...provider3, + host: 'dev.azure.com', + path: '/catalog-info.yaml', + id: 'provider3', + }); + }); +}); diff --git a/plugins/catalog-backend-module-azure/src/providers/config.ts b/plugins/catalog-backend-module-azure/src/providers/config.ts new file mode 100644 index 0000000000..1d143fc882 --- /dev/null +++ b/plugins/catalog-backend-module-azure/src/providers/config.ts @@ -0,0 +1,53 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Config } from '@backstage/config'; +import { AzureDevOpsConfig } from './types'; + +export function readAzureDevOpsConfigs(config: Config): AzureDevOpsConfig[] { + const configs: AzureDevOpsConfig[] = []; + + const providerConfigs = config.getOptionalConfig( + 'catalog.providers.azureDevOps', + ); + + if (!providerConfigs) { + return configs; + } + + for (const id of providerConfigs.keys()) { + configs.push(readAzureDevOpsConfig(id, providerConfigs.getConfig(id))); + } + + return configs; +} + +function readAzureDevOpsConfig(id: string, config: Config): AzureDevOpsConfig { + const organization = config.getString('organization'); + const project = config.getString('project'); + const host = config.getOptionalString('host') || 'dev.azure.com'; + const repository = config.getOptionalString('repository') || '*'; + const path = config.getOptionalString('path') || '/catalog-info.yaml'; + + return { + id, + host, + organization, + project, + repository, + path, + }; +} diff --git a/plugins/catalog-backend-module-azure/src/providers/index.ts b/plugins/catalog-backend-module-azure/src/providers/index.ts new file mode 100644 index 0000000000..450fc0af27 --- /dev/null +++ b/plugins/catalog-backend-module-azure/src/providers/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { AzureDevOpsEntityProvider } from './AzureDevOpsEntityProvider'; diff --git a/plugins/catalog-backend-module-azure/src/providers/types.ts b/plugins/catalog-backend-module-azure/src/providers/types.ts new file mode 100644 index 0000000000..15ea00ff13 --- /dev/null +++ b/plugins/catalog-backend-module-azure/src/providers/types.ts @@ -0,0 +1,24 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export type AzureDevOpsConfig = { + id: string; + host: string; + organization: string; + project: string; + repository: string; + path: string; +}; diff --git a/plugins/catalog-backend-module-bitbucket-cloud/.eslintrc.js b/plugins/catalog-backend-module-bitbucket-cloud/.eslintrc.js new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/plugins/catalog-backend-module-bitbucket-cloud/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md b/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md new file mode 100644 index 0000000000..4f83b02e1f --- /dev/null +++ b/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md @@ -0,0 +1,278 @@ +# @backstage/plugin-catalog-backend-module-bitbucket-cloud + +## 0.1.0 + +### Minor Changes + +- dfc4efcbf0: Add new plugin `catalog-backend-module-bitbucket-cloud` with `BitbucketCloudEntityProvider`. + + This entity provider is an alternative/replacement to the `BitbucketDiscoveryProcessor` **_(for Bitbucket Cloud only!)_**. + It replaces use cases using `search=true` and should be powerful enough as a complete replacement. + + If any feature for Bitbucket Cloud is missing and preventing you from switching, please raise an issue. + + **Before:** + + ```typescript + // packages/backend/src/plugins/catalog.ts + + builder.addProcessor( + BitbucketDiscoveryProcessor.fromConfig(env.config, { logger: env.logger }), + ); + ``` + + ```yaml + # app-config.yaml + + catalog: + locations: + - type: bitbucket-discovery + target: 'https://bitbucket.org/workspaces/workspace-name/projects/apis-*/repos/service-*?search=true&catalogPath=/catalog-info.yaml' + ``` + + **After:** + + ```typescript + // packages/backend/src/plugins/catalog.ts + builder.addEntityProvider( + BitbucketCloudEntityProvider.fromConfig(env.config, { + logger: env.logger, + schedule: env.scheduler.createScheduledTaskRunner({ + frequency: { minutes: 30 }, + timeout: { minutes: 3 }, + }), + }), + ); + ``` + + ```yaml + # app-config.yaml + + catalog: + providers: + bitbucketCloud: + yourProviderId: # identifies your ingested dataset + catalogPath: /catalog-info.yaml # default value + filters: # optional + projectKey: '^apis-.* + ``` + +## 0.1.0-next.0 + +### Minor Changes + +- dfc4efcbf0: Add new plugin `catalog-backend-module-bitbucket-cloud` with `BitbucketCloudEntityProvider`. + + This entity provider is an alternative/replacement to the `BitbucketDiscoveryProcessor` **_(for Bitbucket Cloud only!)_**. + It replaces use cases using `search=true` and should be powerful enough as a complete replacement. + + If any feature for Bitbucket Cloud is missing and preventing you from switching, please raise an issue. + + **Before:** + + ```typescript + // packages/backend/src/plugins/catalog.ts + + builder.addProcessor( + BitbucketDiscoveryProcessor.fromConfig(env.config, { logger: env.logger }), + ); + ``` + + ```yaml + # app-config.yaml + + catalog: + locations: + - type: bitbucket-discovery + target: 'https://bitbucket.org/workspaces/workspace-name/projects/apis-*/repos/service-*?search=true&catalogPath=/catalog-info.yaml' + ``` + + **After:** + + ```typescript + // packages/backend/src/plugins/catalog.ts + builder.addEntityProvider( + BitbucketCloudEntityProvider.fromConfig(env.config, { + logger: env.logger, + schedule: env.scheduler.createScheduledTaskRunner({ + frequency: { minutes: 30 }, + timeout: { minutes: 3 }, + }), + }), + ); + ``` + + ```yaml + # app-config.yaml + + catalog: + providers: + bitbucketCloud: + yourProviderId: # identifies your ingested dataset + catalogPath: /catalog-info.yaml # default value + filters: # optional + projectKey: '^apis-.* # optional; RegExp + repoSlug: '^service-.* # optional; RegExp + workspace: workspace-name + ``` + +### Patch Changes + +- Updated dependencies + - @backstage/backend-tasks@0.3.2-next.1 + - @backstage/integration@1.2.1-next.1 + - @backstage/plugin-catalog-backend@1.2.0-next.1 + - @backstage/plugin-bitbucket-cloud-common@0.1.0-next.0 + +# optional; RegExp + + repoSlug: '^service-.* + +## 0.1.0-next.0 + +### Minor Changes + +- dfc4efcbf0: Add new plugin `catalog-backend-module-bitbucket-cloud` with `BitbucketCloudEntityProvider`. + + This entity provider is an alternative/replacement to the `BitbucketDiscoveryProcessor` **_(for Bitbucket Cloud only!)_**. + It replaces use cases using `search=true` and should be powerful enough as a complete replacement. + + If any feature for Bitbucket Cloud is missing and preventing you from switching, please raise an issue. + + **Before:** + + ```typescript + // packages/backend/src/plugins/catalog.ts + + builder.addProcessor( + BitbucketDiscoveryProcessor.fromConfig(env.config, { logger: env.logger }), + ); + ``` + + ```yaml + # app-config.yaml + + catalog: + locations: + - type: bitbucket-discovery + target: 'https://bitbucket.org/workspaces/workspace-name/projects/apis-*/repos/service-*?search=true&catalogPath=/catalog-info.yaml' + ``` + + **After:** + + ```typescript + // packages/backend/src/plugins/catalog.ts + builder.addEntityProvider( + BitbucketCloudEntityProvider.fromConfig(env.config, { + logger: env.logger, + schedule: env.scheduler.createScheduledTaskRunner({ + frequency: { minutes: 30 }, + timeout: { minutes: 3 }, + }), + }), + ); + ``` + + ```yaml + # app-config.yaml + + catalog: + providers: + bitbucketCloud: + yourProviderId: # identifies your ingested dataset + catalogPath: /catalog-info.yaml # default value + filters: # optional + projectKey: '^apis-.* # optional; RegExp + repoSlug: '^service-.* # optional; RegExp + workspace: workspace-name + ``` + +### Patch Changes + +- Updated dependencies + - @backstage/backend-tasks@0.3.2-next.1 + - @backstage/integration@1.2.1-next.1 + - @backstage/plugin-catalog-backend@1.2.0-next.1 + - @backstage/plugin-bitbucket-cloud-common@0.1.0-next.0 + +# optional; RegExp + + workspace: workspace-name + +```` + +### Patch Changes + +- 9122060776: Updated dependency `msw` to `^0.42.0`. +- Updated dependencies +- @backstage/plugin-catalog-backend@1.2.0 +- @backstage/backend-tasks@0.3.2 +- @backstage/integration@1.2.1 +- @backstage/plugin-bitbucket-cloud-common@0.1.0 + +## 0.1.0-next.0 + +### Minor Changes + +- dfc4efcbf0: Add new plugin `catalog-backend-module-bitbucket-cloud` with `BitbucketCloudEntityProvider`. + +This entity provider is an alternative/replacement to the `BitbucketDiscoveryProcessor` **_(for Bitbucket Cloud only!)_**. +It replaces use cases using `search=true` and should be powerful enough as a complete replacement. + +If any feature for Bitbucket Cloud is missing and preventing you from switching, please raise an issue. + +**Before:** + +```typescript +// packages/backend/src/plugins/catalog.ts + +builder.addProcessor( + BitbucketDiscoveryProcessor.fromConfig(env.config, { logger: env.logger }), +); +```` + +```yaml +# app-config.yaml + +catalog: + locations: + - type: bitbucket-discovery + target: 'https://bitbucket.org/workspaces/workspace-name/projects/apis-*/repos/service-*?search=true&catalogPath=/catalog-info.yaml' +``` + +**After:** + +```typescript +// packages/backend/src/plugins/catalog.ts +builder.addEntityProvider( + BitbucketCloudEntityProvider.fromConfig(env.config, { + logger: env.logger, + schedule: env.scheduler.createScheduledTaskRunner({ + frequency: { minutes: 30 }, + timeout: { minutes: 3 }, + }), + }), +); +``` + +```yaml +# app-config.yaml + +catalog: + providers: + bitbucketCloud: + yourProviderId: # identifies your ingested dataset + catalogPath: /catalog-info.yaml # default value + filters: # optional + projectKey: '^apis-.* # optional; RegExp + repoSlug: '^service-.* # optional; RegExp + workspace: workspace-name +``` + +### Patch Changes + +- Updated dependencies + - @backstage/backend-tasks@0.3.2-next.1 + - @backstage/integration@1.2.1-next.1 + - @backstage/plugin-catalog-backend@1.2.0-next.1 + - @backstage/plugin-bitbucket-cloud-common@0.1.0-next.0 diff --git a/plugins/catalog-backend-module-bitbucket-cloud/README.md b/plugins/catalog-backend-module-bitbucket-cloud/README.md new file mode 100644 index 0000000000..11f40776ca --- /dev/null +++ b/plugins/catalog-backend-module-bitbucket-cloud/README.md @@ -0,0 +1,9 @@ +# Catalog Backend Module for Bitbucket Cloud + +This is an extension module to the catalog-backend plugin, +providing extensions targeted at Bitbucket Cloud offerings. + +## Getting started + +See [Backstage documentation](https://backstage.io/docs/integrations/bitbucketCloud/discovery) +for details on how to install and configure the plugin. diff --git a/plugins/catalog-backend-module-bitbucket-cloud/api-report.md b/plugins/catalog-backend-module-bitbucket-cloud/api-report.md new file mode 100644 index 0000000000..b886cc224f --- /dev/null +++ b/plugins/catalog-backend-module-bitbucket-cloud/api-report.md @@ -0,0 +1,31 @@ +## API Report File for "@backstage/plugin-catalog-backend-module-bitbucket-cloud" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import { Config } from '@backstage/config'; +import { EntityProvider } from '@backstage/plugin-catalog-backend'; +import { EntityProviderConnection } from '@backstage/plugin-catalog-backend'; +import { Logger } from 'winston'; +import { TaskRunner } from '@backstage/backend-tasks'; + +// @public +export class BitbucketCloudEntityProvider implements EntityProvider { + // (undocumented) + connect(connection: EntityProviderConnection): Promise; + // (undocumented) + static fromConfig( + config: Config, + options: { + logger: Logger; + schedule: TaskRunner; + }, + ): BitbucketCloudEntityProvider[]; + // (undocumented) + getProviderName(): string; + // (undocumented) + getTaskId(): string; + // (undocumented) + refresh(logger: Logger): Promise; +} +``` diff --git a/plugins/catalog-backend-module-bitbucket-cloud/config.d.ts b/plugins/catalog-backend-module-bitbucket-cloud/config.d.ts new file mode 100644 index 0000000000..29289bb70e --- /dev/null +++ b/plugins/catalog-backend-module-bitbucket-cloud/config.d.ts @@ -0,0 +1,90 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export interface Config { + catalog?: { + /** + * List of provider-specific options and attributes + */ + providers?: { + /** + * BitbucketCloudEntityProvider configuration + * + * Uses "default" as default id for the single config variant. + */ + bitbucketCloud?: + | { + /** + * (Optional) Path to the catalog file. Default to "/catalog-info.yaml". + * @visibility frontend + */ + catalogPath?: string; + /** + * (Required) Your workspace. + * @visibility frontend + */ + workspace: string; + /** + * (Optional) Filters applied to discovered catalog files in repositories. + * @visibility frontend + */ + filters?: { + /** + * (Optional) Filter for the repository slug. + * @visibility frontend + */ + repoSlug?: RegExp; + /** + * (Optional) Filter for the project key. + * @visibility frontend + */ + projectKey?: RegExp; + }; + } + | Record< + string, + { + /** + * (Optional) Path to the catalog file. Default to "/catalog-info.yaml". + * @visibility frontend + */ + catalogPath?: string; + /** + * (Required) Your workspace. + * @visibility frontend + */ + workspace: string; + /** + * (Optional) Filters applied to discovered catalog files in repositories. + * @visibility frontend + */ + filters?: { + /** + * (Optional) Filter for the repository slug. + * @visibility frontend + */ + repoSlug?: RegExp; + /** + * (Optional) Filter for the project key. + * @visibility frontend + */ + projectKey?: RegExp; + }; + } + >; + }; + }; +} diff --git a/plugins/catalog-backend-module-bitbucket-cloud/package.json b/plugins/catalog-backend-module-bitbucket-cloud/package.json new file mode 100644 index 0000000000..aea92c3b56 --- /dev/null +++ b/plugins/catalog-backend-module-bitbucket-cloud/package.json @@ -0,0 +1,55 @@ +{ + "name": "@backstage/plugin-catalog-backend-module-bitbucket-cloud", + "description": "A Backstage catalog backend module that helps integrate towards Bitbucket Cloud", + "version": "0.1.0", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "private": false, + "publishConfig": { + "access": "public", + "main": "dist/index.cjs.js", + "types": "dist/index.d.ts" + }, + "backstage": { + "role": "backend-plugin-module" + }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/catalog-backend-module-bitbucket-cloud" + }, + "keywords": [ + "backstage" + ], + "scripts": { + "build": "backstage-cli package build", + "lint": "backstage-cli package lint", + "test": "backstage-cli package test", + "prepack": "backstage-cli package prepack", + "postpack": "backstage-cli package postpack", + "clean": "backstage-cli package clean", + "start": "backstage-cli package start" + }, + "dependencies": { + "@backstage/backend-tasks": "^0.3.2", + "@backstage/config": "^1.0.1", + "@backstage/integration": "^1.2.1", + "@backstage/plugin-bitbucket-cloud-common": "^0.1.0", + "@backstage/plugin-catalog-backend": "^1.2.0", + "uuid": "^8.0.0", + "winston": "^3.2.1" + }, + "devDependencies": { + "@backstage/backend-common": "^0.14.0", + "@backstage/backend-test-utils": "^0.1.25", + "@backstage/cli": "^0.17.2", + "msw": "^0.42.0" + }, + "files": [ + "dist", + "config.d.ts" + ], + "configSchema": "config.d.ts" +} diff --git a/plugins/catalog-backend-module-bitbucket-cloud/src/BitbucketCloudEntityProvider.test.ts b/plugins/catalog-backend-module-bitbucket-cloud/src/BitbucketCloudEntityProvider.test.ts new file mode 100644 index 0000000000..efb1328643 --- /dev/null +++ b/plugins/catalog-backend-module-bitbucket-cloud/src/BitbucketCloudEntityProvider.test.ts @@ -0,0 +1,281 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { getVoidLogger } from '@backstage/backend-common'; +import { TaskInvocationDefinition, TaskRunner } from '@backstage/backend-tasks'; +import { ConfigReader } from '@backstage/config'; +import { EntityProviderConnection } from '@backstage/plugin-catalog-backend'; +import { setupRequestMockHandlers } from '@backstage/backend-test-utils'; +import { BitbucketCloudEntityProvider } from './BitbucketCloudEntityProvider'; +import { rest } from 'msw'; +import { setupServer } from 'msw/node'; + +class PersistingTaskRunner implements TaskRunner { + private tasks: TaskInvocationDefinition[] = []; + + getTasks() { + return this.tasks; + } + + run(task: TaskInvocationDefinition): Promise { + this.tasks.push(task); + return Promise.resolve(undefined); + } +} + +const logger = getVoidLogger(); + +const server = setupServer(); + +describe('BitbucketCloudEntityProvider', () => { + setupRequestMockHandlers(server); + afterEach(() => jest.resetAllMocks()); + + it('no provider config', () => { + const schedule = new PersistingTaskRunner(); + const config = new ConfigReader({}); + const providers = BitbucketCloudEntityProvider.fromConfig(config, { + logger, + schedule, + }); + + expect(providers).toHaveLength(0); + }); + + it('single simple provider config', () => { + const schedule = new PersistingTaskRunner(); + const config = new ConfigReader({ + catalog: { + providers: { + bitbucketCloud: { + workspace: 'test-ws', + }, + }, + }, + }); + const providers = BitbucketCloudEntityProvider.fromConfig(config, { + logger, + schedule, + }); + + expect(providers).toHaveLength(1); + expect(providers[0].getProviderName()).toEqual( + 'bitbucketCloud-provider:default', + ); + }); + + it('multiple provider configs', () => { + const schedule = new PersistingTaskRunner(); + const config = new ConfigReader({ + catalog: { + providers: { + bitbucketCloud: { + myProvider: { + workspace: 'test-ws1', + }, + anotherProvider: { + workspace: 'test-ws2', + }, + }, + }, + }, + }); + const providers = BitbucketCloudEntityProvider.fromConfig(config, { + logger, + schedule, + }); + + expect(providers).toHaveLength(2); + expect(providers[0].getProviderName()).toEqual( + 'bitbucketCloud-provider:myProvider', + ); + expect(providers[1].getProviderName()).toEqual( + 'bitbucketCloud-provider:anotherProvider', + ); + }); + + it('apply full update on scheduled execution', async () => { + const config = new ConfigReader({ + catalog: { + providers: { + bitbucketCloud: { + myProvider: { + workspace: 'test-ws', + catalogPath: 'custom/path/catalog-custom.yaml', + filters: { + projectKey: 'test-.*', + repoSlug: 'test-.*', + }, + }, + }, + }, + }, + }); + const schedule = new PersistingTaskRunner(); + const entityProviderConnection: EntityProviderConnection = { + applyMutation: jest.fn(), + }; + const provider = BitbucketCloudEntityProvider.fromConfig(config, { + logger, + schedule, + })[0]; + expect(provider.getProviderName()).toEqual( + 'bitbucketCloud-provider:myProvider', + ); + + server.use( + rest.get( + `https://api.bitbucket.org/2.0/workspaces/test-ws/search/code`, + (_req, res, ctx) => { + const response = { + values: [ + { + // skipped as empty + path_matches: [], + file: { + type: 'commit_file', + path: 'path/to/ignored/file', + }, + }, + { + path_matches: [ + { + match: true, + text: 'catalog-custom.yaml', + }, + ], + file: { + type: 'commit_file', + path: 'custom/path/catalog-custom.yaml', + commit: { + repository: { + // skipped as no match with filter + slug: 'repo', + project: { + key: 'test-project', + }, + mainbranch: { + name: 'main', + }, + links: { + html: { + href: 'https://bitbucket.org/test-ws/repo', + }, + }, + }, + }, + }, + }, + { + path_matches: [ + { + match: true, + text: 'catalog-custom.yaml', + }, + ], + file: { + type: 'commit_file', + path: 'custom/path/catalog-custom.yaml', + commit: { + repository: { + slug: 'test-repo1', + project: { + // skipped as no match with filter + key: 'project', + }, + mainbranch: { + name: 'main', + }, + links: { + html: { + href: 'https://bitbucket.org/test-ws/test-repo1', + }, + }, + }, + }, + }, + }, + { + path_matches: [ + { + match: true, + text: 'catalog-custom.yaml', + }, + ], + file: { + type: 'commit_file', + path: 'custom/path/catalog-custom.yaml', + commit: { + repository: { + slug: 'test-repo2', + project: { + key: 'test-project', + }, + mainbranch: { + name: 'main', + }, + links: { + html: { + href: 'https://bitbucket.org/test-ws/test-repo2', + }, + }, + }, + }, + }, + }, + ], + }; + return res(ctx.json(response)); + }, + ), + ); + + await provider.connect(entityProviderConnection); + + const taskDef = schedule.getTasks()[0]; + expect(taskDef.id).toEqual('bitbucketCloud-provider:myProvider:refresh'); + await (taskDef.fn as () => Promise)(); + + const url = `https://bitbucket.org/test-ws/test-repo2/src/main/custom/path/catalog-custom.yaml`; + const expectedEntities = [ + { + entity: { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Location', + metadata: { + annotations: { + 'backstage.io/managed-by-location': `url:${url}`, + 'backstage.io/managed-by-origin-location': `url:${url}`, + }, + name: 'generated-7c2e6263b6cc2d14e69fd4d029afba601ad6dc3b', + }, + spec: { + presence: 'required', + target: `${url}`, + type: 'url', + }, + }, + locationKey: 'bitbucketCloud-provider:myProvider', + }, + ]; + + expect(entityProviderConnection.applyMutation).toBeCalledTimes(1); + expect(entityProviderConnection.applyMutation).toBeCalledWith({ + type: 'full', + entities: expectedEntities, + }); + }); +}); diff --git a/plugins/catalog-backend-module-bitbucket-cloud/src/BitbucketCloudEntityProvider.ts b/plugins/catalog-backend-module-bitbucket-cloud/src/BitbucketCloudEntityProvider.ts new file mode 100644 index 0000000000..74d642ed9b --- /dev/null +++ b/plugins/catalog-backend-module-bitbucket-cloud/src/BitbucketCloudEntityProvider.ts @@ -0,0 +1,240 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { TaskRunner } from '@backstage/backend-tasks'; +import { Config } from '@backstage/config'; +import { + BitbucketCloudIntegration, + ScmIntegrations, +} from '@backstage/integration'; +import { + BitbucketCloudClient, + Models, +} from '@backstage/plugin-bitbucket-cloud-common'; +import { + EntityProvider, + EntityProviderConnection, + LocationSpec, + locationSpecToLocationEntity, +} from '@backstage/plugin-catalog-backend'; +import { + BitbucketCloudEntityProviderConfig, + readProviderConfigs, +} from './BitbucketCloudEntityProviderConfig'; +import * as uuid from 'uuid'; +import { Logger } from 'winston'; + +const DEFAULT_BRANCH = 'master'; + +/** + * Discovers catalog files located in [Bitbucket Cloud](https://bitbucket.org). + * The provider will search your Bitbucket Cloud account and register catalog files matching the configured path + * as Location entity and via following processing steps add all contained catalog entities. + * This can be useful as an alternative to static locations or manually adding things to the catalog. + * + * @public + */ +export class BitbucketCloudEntityProvider implements EntityProvider { + private readonly client: BitbucketCloudClient; + private readonly config: BitbucketCloudEntityProviderConfig; + private readonly logger: Logger; + private readonly scheduleFn: () => Promise; + private connection?: EntityProviderConnection; + + static fromConfig( + config: Config, + options: { + logger: Logger; + schedule: TaskRunner; + }, + ): BitbucketCloudEntityProvider[] { + const integrations = ScmIntegrations.fromConfig(config); + const integration = integrations.bitbucketCloud.byHost('bitbucket.org'); + if (!integration) { + // this should never happen as we add a default integration, + // but as a general safeguard, e.g. if this approach gets changed + throw new Error('No integration for bitbucket.org available'); + } + + return readProviderConfigs(config).map( + providerConfig => + new BitbucketCloudEntityProvider( + providerConfig, + integration, + options.logger, + options.schedule, + ), + ); + } + + private constructor( + config: BitbucketCloudEntityProviderConfig, + integration: BitbucketCloudIntegration, + logger: Logger, + schedule: TaskRunner, + ) { + this.client = BitbucketCloudClient.fromConfig(integration.config); + this.config = config; + this.logger = logger.child({ + target: this.getProviderName(), + }); + this.scheduleFn = this.createScheduleFn(schedule); + } + + private createScheduleFn(schedule: TaskRunner): () => Promise { + return async () => { + const taskId = this.getTaskId(); + return schedule.run({ + id: taskId, + fn: async () => { + const logger = this.logger.child({ + class: BitbucketCloudEntityProvider.prototype.constructor.name, + taskId, + taskInstanceId: uuid.v4(), + }); + + try { + await this.refresh(logger); + } catch (error) { + logger.error(error); + } + }, + }); + }; + } + + /** {@inheritdoc @backstage/plugin-catalog-backend#EntityProvider.getProviderName} */ + getProviderName(): string { + return `bitbucketCloud-provider:${this.config.id}`; + } + + /** {@inheritdoc @backstage/plugin-catalog-backend#EntityProvider.getTaskId} */ + getTaskId(): string { + return `${this.getProviderName()}:refresh`; + } + + /** {@inheritdoc @backstage/plugin-catalog-backend#EntityProvider.connect} */ + async connect(connection: EntityProviderConnection): Promise { + this.connection = connection; + await this.scheduleFn(); + } + + async refresh(logger: Logger) { + if (!this.connection) { + throw new Error('Not initialized'); + } + + logger.info('Discovering catalog files in Bitbucket Cloud repositories'); + + const targets = await this.findCatalogFiles(); + const entities = targets + .map(BitbucketCloudEntityProvider.toLocationSpec) + .map(location => locationSpecToLocationEntity({ location })) + .map(entity => { + return { + locationKey: this.getProviderName(), + entity: entity, + }; + }); + + await this.connection.applyMutation({ + type: 'full', + entities: entities, + }); + + logger.info( + `Committed ${entities.length} Locations for catalog files in Bitbucket Cloud repositories`, + ); + } + + private async findCatalogFiles(): Promise { + const workspace = this.config.workspace; + const catalogPath = this.config.catalogPath; + + const catalogFilename = catalogPath.substring( + catalogPath.lastIndexOf('/') + 1, + ); + + // load all fields relevant for creating refs later, but not more + const fields = [ + // exclude code/content match details + '-values.content_matches', + // include/add relevant repository details + '+values.file.commit.repository.mainbranch.name', + '+values.file.commit.repository.project.key', + '+values.file.commit.repository.slug', + // remove irrelevant links + '-values.*.links', + '-values.*.*.links', + '-values.*.*.*.links', + // ...except the one we need + '+values.file.commit.repository.links.html.href', + ].join(','); + const query = `"${catalogFilename}" path:${catalogPath}`; + const searchResults = this.client + .searchCode(workspace, query, { fields }) + .iterateResults(); + + const result: string[] = []; + + for await (const searchResult of searchResults) { + // not a file match, but a code match + if (searchResult.path_matches!.length === 0) { + continue; + } + + const repository = searchResult.file!.commit!.repository!; + if (this.matchesFilters(repository)) { + result.push( + BitbucketCloudEntityProvider.toUrl( + repository, + searchResult.file!.path!, + ), + ); + } + } + + return result; + } + + private matchesFilters(repository: Models.Repository): boolean { + const filters = this.config.filters; + return ( + !filters || + ((!filters.projectKey || + filters.projectKey.test(repository.project!.key!)) && + (!filters.repoSlug || filters.repoSlug.test(repository.slug!))) + ); + } + + private static toUrl( + repository: Models.Repository, + filePath: string, + ): string { + const repoUrl = repository.links!.html!.href; + const branch = repository.mainbranch?.name ?? DEFAULT_BRANCH; + + return `${repoUrl}/src/${branch}/${filePath}`; + } + + private static toLocationSpec(target: string): LocationSpec { + return { + type: 'url', + target: target, + presence: 'required', + }; + } +} diff --git a/plugins/catalog-backend-module-bitbucket-cloud/src/BitbucketCloudEntityProviderConfig.test.ts b/plugins/catalog-backend-module-bitbucket-cloud/src/BitbucketCloudEntityProviderConfig.test.ts new file mode 100644 index 0000000000..a42bd2cde7 --- /dev/null +++ b/plugins/catalog-backend-module-bitbucket-cloud/src/BitbucketCloudEntityProviderConfig.test.ts @@ -0,0 +1,115 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ConfigReader } from '@backstage/config'; +import { readProviderConfigs } from './BitbucketCloudEntityProviderConfig'; + +describe('readProviderConfigs', () => { + afterEach(() => jest.resetAllMocks()); + + it('no provider config', () => { + const config = new ConfigReader({}); + const providerConfigs = readProviderConfigs(config); + + expect(providerConfigs).toHaveLength(0); + }); + + it('single simple provider config', () => { + const config = new ConfigReader({ + catalog: { + providers: { + bitbucketCloud: { + workspace: 'test-ws', + }, + }, + }, + }); + const providerConfigs = readProviderConfigs(config); + + expect(providerConfigs).toHaveLength(1); + expect(providerConfigs[0].id).toEqual('default'); + expect(providerConfigs[0].workspace).toEqual('test-ws'); + }); + + it('multiple provider configs', () => { + const config = new ConfigReader({ + catalog: { + providers: { + bitbucketCloud: { + providerWorkspaceOnly: { + workspace: 'test-ws1', + }, + providerCustomCatalogPath: { + workspace: 'test-ws2', + catalogPath: 'custom/path/catalog-info.yaml', + }, + providerWithProjectKeyFilter: { + workspace: 'test-ws3', + filters: { + projectKey: 'projectKey.*filter', + }, + }, + providerWithRepoSlugFilter: { + workspace: 'test-ws4', + filters: { + repoSlug: 'repoSlug.*filter', + }, + }, + }, + }, + }, + }); + const providerConfigs = readProviderConfigs(config); + + expect(providerConfigs).toHaveLength(4); + expect(providerConfigs[0]).toEqual({ + id: 'providerWorkspaceOnly', + workspace: 'test-ws1', + catalogPath: '/catalog-info.yaml', + filters: { + projectKey: undefined, + repoSlug: undefined, + }, + }); + expect(providerConfigs[1]).toEqual({ + id: 'providerCustomCatalogPath', + workspace: 'test-ws2', + catalogPath: 'custom/path/catalog-info.yaml', + filters: { + projectKey: undefined, + repoSlug: undefined, + }, + }); + expect(providerConfigs[2]).toEqual({ + id: 'providerWithProjectKeyFilter', + workspace: 'test-ws3', + catalogPath: '/catalog-info.yaml', + filters: { + projectKey: /^projectKey.*filter$/, + repoSlug: undefined, + }, + }); + expect(providerConfigs[3]).toEqual({ + id: 'providerWithRepoSlugFilter', + workspace: 'test-ws4', + catalogPath: '/catalog-info.yaml', + filters: { + projectKey: undefined, + repoSlug: /^repoSlug.*filter$/, + }, + }); + }); +}); diff --git a/plugins/catalog-backend-module-bitbucket-cloud/src/BitbucketCloudEntityProviderConfig.ts b/plugins/catalog-backend-module-bitbucket-cloud/src/BitbucketCloudEntityProviderConfig.ts new file mode 100644 index 0000000000..95e995f8c3 --- /dev/null +++ b/plugins/catalog-backend-module-bitbucket-cloud/src/BitbucketCloudEntityProviderConfig.ts @@ -0,0 +1,93 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Config } from '@backstage/config'; + +const DEFAULT_CATALOG_PATH = '/catalog-info.yaml'; +const DEFAULT_PROVIDER_ID = 'default'; + +export type BitbucketCloudEntityProviderConfig = { + id: string; + catalogPath: string; + workspace: string; + filters?: { + projectKey?: RegExp; + repoSlug?: RegExp; + }; +}; + +export function readProviderConfigs( + config: Config, +): BitbucketCloudEntityProviderConfig[] { + const providersConfig = config.getOptionalConfig( + 'catalog.providers.bitbucketCloud', + ); + if (!providersConfig) { + return []; + } + + if (providersConfig.has('workspace')) { + // simple/single config variant + return [readProviderConfig(DEFAULT_PROVIDER_ID, providersConfig)]; + } + + return providersConfig.keys().map(id => { + const providerConfig = providersConfig.getConfig(id); + + return readProviderConfig(id, providerConfig); + }); +} + +function readProviderConfig( + id: string, + config: Config, +): BitbucketCloudEntityProviderConfig { + const workspace = config.getString('workspace'); + const catalogPath = + config.getOptionalString('catalogPath') ?? DEFAULT_CATALOG_PATH; + const projectKeyPattern = config.getOptionalString('filters.projectKey'); + const repoSlugPattern = config.getOptionalString('filters.repoSlug'); + + return { + id, + catalogPath, + workspace, + filters: { + projectKey: projectKeyPattern + ? compileRegExp(projectKeyPattern) + : undefined, + repoSlug: repoSlugPattern ? compileRegExp(repoSlugPattern) : undefined, + }, + }; +} + +/** + * Compiles a RegExp while enforcing the pattern to contain + * the start-of-line and end-of-line anchors. + * + * @param pattern + */ +function compileRegExp(pattern: string): RegExp { + let fullLinePattern = pattern; + if (!fullLinePattern.startsWith('^')) { + fullLinePattern = `^${fullLinePattern}`; + } + if (!fullLinePattern.endsWith('$')) { + fullLinePattern = `${fullLinePattern}$`; + } + + return new RegExp(fullLinePattern); +} diff --git a/plugins/catalog-backend-module-bitbucket-cloud/src/index.ts b/plugins/catalog-backend-module-bitbucket-cloud/src/index.ts new file mode 100644 index 0000000000..1c15ad4e8f --- /dev/null +++ b/plugins/catalog-backend-module-bitbucket-cloud/src/index.ts @@ -0,0 +1,23 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * A Backstage catalog backend module that helps integrate towards Bitbucket Cloud + * + * @packageDocumentation + */ + +export { BitbucketCloudEntityProvider } from './BitbucketCloudEntityProvider'; diff --git a/plugins/catalog-backend-module-bitbucket-cloud/src/setupTests.ts b/plugins/catalog-backend-module-bitbucket-cloud/src/setupTests.ts new file mode 100644 index 0000000000..813cdeaae3 --- /dev/null +++ b/plugins/catalog-backend-module-bitbucket-cloud/src/setupTests.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export {}; diff --git a/plugins/catalog-backend-module-bitbucket/CHANGELOG.md b/plugins/catalog-backend-module-bitbucket/CHANGELOG.md index 868d27b750..8c3b2c6ad5 100644 --- a/plugins/catalog-backend-module-bitbucket/CHANGELOG.md +++ b/plugins/catalog-backend-module-bitbucket/CHANGELOG.md @@ -1,5 +1,55 @@ # @backstage/plugin-catalog-backend-module-bitbucket +## 0.2.0 + +### Minor Changes + +- 1c01c0fd14: Integrate `@backstage/plugin-bitbucket-cloud-common` as replacement for the `BitbucketCloudClient`. + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-backend@1.2.0 + - @backstage/backend-common@0.14.0 + - @backstage/integration@1.2.1 + - @backstage/plugin-bitbucket-cloud-common@0.1.0 + - @backstage/catalog-model@1.0.3 + +## 0.2.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0-next.2 + - @backstage/integration@1.2.1-next.2 + - @backstage/plugin-catalog-backend@1.2.0-next.2 + +## 0.2.0-next.1 + +### Minor Changes + +- 1c01c0fd14: Integrate `@backstage/plugin-bitbucket-cloud-common` as replacement for the `BitbucketCloudClient`. + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/backend-common@0.13.6-next.1 + - @backstage/integration@1.2.1-next.1 + - @backstage/plugin-catalog-backend@1.2.0-next.1 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-bitbucket-cloud-common@0.1.0-next.0 + +## 0.1.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + - @backstage/integration@1.2.1-next.0 + - @backstage/plugin-catalog-backend@1.2.0-next.0 + ## 0.1.3 ### Patch Changes diff --git a/plugins/catalog-backend-module-bitbucket/package.json b/plugins/catalog-backend-module-bitbucket/package.json index e433cdda15..8b9c3b4e91 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.1.3", + "version": "0.2.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -33,21 +33,22 @@ "start": "backstage-cli package start" }, "dependencies": { - "@backstage/backend-common": "^0.13.3", - "@backstage/catalog-model": "^1.0.2", + "@backstage/backend-common": "^0.14.0", + "@backstage/catalog-model": "^1.0.3", "@backstage/config": "^1.0.1", "@backstage/errors": "^1.0.0", - "@backstage/integration": "^1.2.0", - "@backstage/plugin-catalog-backend": "^1.1.2", + "@backstage/integration": "^1.2.1", + "@backstage/plugin-bitbucket-cloud-common": "^0.1.0", + "@backstage/plugin-catalog-backend": "^1.2.0", "@backstage/types": "^1.0.0", "lodash": "^4.17.21", - "msw": "^0.35.0", + "msw": "^0.42.0", "node-fetch": "^2.6.7", "winston": "^3.2.1" }, "devDependencies": { - "@backstage/backend-test-utils": "^0.1.24", - "@backstage/cli": "^0.17.1", + "@backstage/backend-test-utils": "^0.1.25", + "@backstage/cli": "^0.17.2", "@types/lodash": "^4.14.151" }, "files": [ diff --git a/plugins/catalog-backend-module-bitbucket/src/BitbucketDiscoveryProcessor.test.ts b/plugins/catalog-backend-module-bitbucket/src/BitbucketDiscoveryProcessor.test.ts index 5f441b787c..ad893e7c87 100644 --- a/plugins/catalog-backend-module-bitbucket/src/BitbucketDiscoveryProcessor.test.ts +++ b/plugins/catalog-backend-module-bitbucket/src/BitbucketDiscoveryProcessor.test.ts @@ -16,6 +16,7 @@ import { getVoidLogger } from '@backstage/backend-common'; import { ConfigReader } from '@backstage/config'; +import { Models } from '@backstage/plugin-bitbucket-cloud-common'; import { LocationSpec, processingResult, @@ -23,7 +24,7 @@ import { import { RequestHandler, rest } from 'msw'; import { setupServer } from 'msw/node'; import { BitbucketDiscoveryProcessor } from './BitbucketDiscoveryProcessor'; -import { BitbucketRepository20, PagedResponse, PagedResponse20 } from './lib'; +import { PagedResponse } from './lib'; const server = setupServer(); @@ -84,14 +85,14 @@ function setupStubs( function setupBitbucketCloudStubs( workspace: string, - repositories: Pick[], + repositories: Pick[], ) { const stubCallerFn = jest.fn(); - function pagedResponse(values: any): PagedResponse20 { + function pagedResponse(values: any): Models.PaginatedRepositories { return { values: values, page: 1, - } as PagedResponse20; + } as Models.PaginatedRepositories; } server.use( @@ -121,15 +122,15 @@ function setupBitbucketCloudStubs( function setupBitbucketCloudSearchStubs( workspace: string, - repositories: Pick[], + repositories: Pick[], catalogPath: string, ) { const stubCallerFn = jest.fn(); - function pagedResponse(values: any): PagedResponse20 { + function pagedResponse(values: any): Models.PaginatedRepositories { return { values: values, page: 1, - } as PagedResponse20; + } as Models.PaginatedRepositories; } server.use( @@ -555,8 +556,14 @@ describe('BitbucketDiscoveryProcessor', () => { it('output all repositories by default', async () => { setupBitbucketCloudStubs('myworkspace', [ - { project: { key: 'prj-one' }, slug: 'repository-one' }, - { project: { key: 'prj-two' }, slug: 'repository-two' }, + { + project: { type: 'project', key: 'prj-one' }, + slug: 'repository-one', + }, + { + project: { type: 'project', key: 'prj-two' }, + slug: 'repository-two', + }, ]); const location: LocationSpec = { type: 'bitbucket-discovery', @@ -590,8 +597,14 @@ describe('BitbucketDiscoveryProcessor', () => { it('uses provided catalog path', async () => { setupBitbucketCloudStubs('myworkspace', [ - { project: { key: 'prj-one' }, slug: 'repository-one' }, - { project: { key: 'prj-two' }, slug: 'repository-two' }, + { + project: { type: 'project', key: 'prj-one' }, + slug: 'repository-one', + }, + { + project: { type: 'project', key: 'prj-two' }, + slug: 'repository-two', + }, ]); const location: LocationSpec = { type: 'bitbucket-discovery', @@ -626,8 +639,14 @@ describe('BitbucketDiscoveryProcessor', () => { it('output all repositories', async () => { setupBitbucketCloudStubs('myworkspace', [ - { project: { key: 'prj-one' }, slug: 'repository-one' }, - { project: { key: 'prj-two' }, slug: 'repository-two' }, + { + project: { type: 'project', key: 'prj-one' }, + slug: 'repository-one', + }, + { + project: { type: 'project', key: 'prj-two' }, + slug: 'repository-two', + }, ]); const location: LocationSpec = { type: 'bitbucket-discovery', @@ -662,8 +681,14 @@ describe('BitbucketDiscoveryProcessor', () => { it('output repositories with wildcards', async () => { setupBitbucketCloudStubs('myworkspace', [ - { project: { key: 'prj-one' }, slug: 'repository-one' }, - { project: { key: 'prj-two' }, slug: 'repository-two' }, + { + project: { type: 'project', key: 'prj-one' }, + slug: 'repository-one', + }, + { + project: { type: 'project', key: 'prj-two' }, + slug: 'repository-two', + }, ]); const location: LocationSpec = { type: 'bitbucket-discovery', @@ -688,9 +713,18 @@ describe('BitbucketDiscoveryProcessor', () => { it('filter unrelated repositories', async () => { setupBitbucketCloudStubs('myworkspace', [ - { project: { key: 'prj-one' }, slug: 'repository-one' }, - { project: { key: 'prj-one' }, slug: 'repository-two' }, - { project: { key: 'prj-one' }, slug: 'repository-three' }, + { + project: { type: 'project', key: 'prj-one' }, + slug: 'repository-one', + }, + { + project: { type: 'project', key: 'prj-one' }, + slug: 'repository-two', + }, + { + project: { type: 'project', key: 'prj-one' }, + slug: 'repository-three', + }, ]); const location: LocationSpec = { type: 'bitbucket-discovery', @@ -715,7 +749,10 @@ describe('BitbucketDiscoveryProcessor', () => { it('submits query', async () => { const mockCall = setupBitbucketCloudStubs('myworkspace', [ - { project: { key: 'prj-one' }, slug: 'repository-one' }, + { + project: { type: 'project', key: 'prj-one' }, + slug: 'repository-one', + }, ]); const location: LocationSpec = { type: 'bitbucket-discovery', @@ -750,7 +787,10 @@ describe('BitbucketDiscoveryProcessor', () => { ${'https://bitbucket.org/workspaces/myworkspace/projects/prj-one/repos/repository-*/'} `("target '$target' adds default path to catalog", async ({ target }) => { setupBitbucketCloudStubs('myworkspace', [ - { project: { key: 'prj-one' }, slug: 'repository-one' }, + { + project: { type: 'project', key: 'prj-one' }, + slug: 'repository-one', + }, ]); const location: LocationSpec = { @@ -778,7 +818,10 @@ describe('BitbucketDiscoveryProcessor', () => { ${'https://bitbucket.org/test'} `("target '$target' is rejected", async ({ target }) => { setupBitbucketCloudStubs('myworkspace', [ - { project: { key: 'prj-one' }, slug: 'repository-one' }, + { + project: { type: 'project', key: 'prj-one' }, + slug: 'repository-one', + }, ]); const location: LocationSpec = { @@ -813,8 +856,14 @@ describe('BitbucketDiscoveryProcessor', () => { setupBitbucketCloudSearchStubs( 'myworkspace', [ - { project: { key: 'prj-one' }, slug: 'repository-one' }, - { project: { key: 'prj-two' }, slug: 'repository-two' }, + { + project: { type: 'project', key: 'prj-one' }, + slug: 'repository-one', + }, + { + project: { type: 'project', key: 'prj-two' }, + slug: 'repository-two', + }, ], 'catalog-info.yaml', ); @@ -852,8 +901,14 @@ describe('BitbucketDiscoveryProcessor', () => { setupBitbucketCloudSearchStubs( 'myworkspace', [ - { project: { key: 'prj-one' }, slug: 'repository-one' }, - { project: { key: 'prj-two' }, slug: 'repository-two' }, + { + project: { type: 'project', key: 'prj-one' }, + slug: 'repository-one', + }, + { + project: { type: 'project', key: 'prj-two' }, + slug: 'repository-two', + }, ], 'my/nested/path/catalog.yaml', ); @@ -892,8 +947,14 @@ describe('BitbucketDiscoveryProcessor', () => { setupBitbucketCloudSearchStubs( 'myworkspace', [ - { project: { key: 'prj-one' }, slug: 'repository-one' }, - { project: { key: 'prj-two' }, slug: 'repository-two' }, + { + project: { type: 'project', key: 'prj-one' }, + slug: 'repository-one', + }, + { + project: { type: 'project', key: 'prj-two' }, + slug: 'repository-two', + }, ], 'catalog.yaml', ); @@ -932,8 +993,14 @@ describe('BitbucketDiscoveryProcessor', () => { setupBitbucketCloudSearchStubs( 'myworkspace', [ - { project: { key: 'prj-one' }, slug: 'repository-one' }, - { project: { key: 'prj-two' }, slug: 'repository-two' }, + { + project: { type: 'project', key: 'prj-one' }, + slug: 'repository-one', + }, + { + project: { type: 'project', key: 'prj-two' }, + slug: 'repository-two', + }, ], 'catalog.yaml', ); @@ -962,9 +1029,18 @@ describe('BitbucketDiscoveryProcessor', () => { setupBitbucketCloudSearchStubs( 'myworkspace', [ - { project: { key: 'prj-one' }, slug: 'repository-one' }, - { project: { key: 'prj-one' }, slug: 'repository-two' }, - { project: { key: 'prj-one' }, slug: 'repository-three' }, + { + project: { type: 'project', key: 'prj-one' }, + slug: 'repository-one', + }, + { + project: { type: 'project', key: 'prj-one' }, + slug: 'repository-two', + }, + { + project: { type: 'project', key: 'prj-one' }, + slug: 'repository-three', + }, ], 'catalog.yaml', ); @@ -997,7 +1073,12 @@ describe('BitbucketDiscoveryProcessor', () => { `("target '$target' adds default path to catalog", async ({ target }) => { setupBitbucketCloudSearchStubs( 'myworkspace', - [{ project: { key: 'prj-one' }, slug: 'repository-one' }], + [ + { + project: { type: 'project', key: 'prj-one' }, + slug: 'repository-one', + }, + ], 'catalog-info.yaml', ); @@ -1027,7 +1108,12 @@ describe('BitbucketDiscoveryProcessor', () => { `("target '$target' is rejected", async ({ target }) => { setupBitbucketCloudSearchStubs( 'myworkspace', - [{ project: { key: 'prj-one' }, slug: 'repository-one' }], + [ + { + project: { type: 'project', key: 'prj-one' }, + slug: 'repository-one', + }, + ], 'catalog-info.yaml', ); diff --git a/plugins/catalog-backend-module-bitbucket/src/BitbucketDiscoveryProcessor.ts b/plugins/catalog-backend-module-bitbucket/src/BitbucketDiscoveryProcessor.ts index a025eda962..14bf90ee62 100644 --- a/plugins/catalog-backend-module-bitbucket/src/BitbucketDiscoveryProcessor.ts +++ b/plugins/catalog-backend-module-bitbucket/src/BitbucketDiscoveryProcessor.ts @@ -20,6 +20,10 @@ import { ScmIntegrationRegistry, ScmIntegrations, } from '@backstage/integration'; +import { + BitbucketCloudClient, + Models, +} from '@backstage/plugin-bitbucket-cloud-common'; import { CatalogProcessor, CatalogProcessorEmit, @@ -27,14 +31,11 @@ import { } from '@backstage/plugin-catalog-backend'; import { Logger } from 'winston'; import { - BitbucketCloudClient, BitbucketRepository, - BitbucketRepository20, BitbucketRepositoryParser, BitbucketServerClient, defaultRepositoryParser, paginated, - paginated20, } from './lib'; const DEFAULT_BRANCH = 'master'; @@ -119,9 +120,7 @@ export class BitbucketDiscoveryProcessor implements CatalogProcessor { options: ProcessOptions, ): Promise { const { location, integration, emit } = options; - const client = new BitbucketCloudClient({ - config: integration.config, - }); + const client = BitbucketCloudClient.fromConfig(integration.config); const { searchEnabled } = parseBitbucketCloudUrl(location.target); @@ -226,28 +225,40 @@ export async function searchBitbucketCloudLocations( catalogPath.lastIndexOf('/') + 1, ); - const searchResults = paginated20(options => - client.searchCode( - workspacePath, - `"${catalogFilename}" path:${catalogPath}`, - options, - ), - ); + // load all fields relevant for creating refs later, but not more + const fields = [ + // exclude code/content match details + '-values.content_matches', + // include/add relevant repository details + '+values.file.commit.repository.mainbranch.name', + '+values.file.commit.repository.project.key', + '+values.file.commit.repository.slug', + // remove irrelevant links + '-values.*.links', + '-values.*.*.links', + '-values.*.*.*.links', + // ...except the one we need + '+values.file.commit.repository.links.html.href', + ].join(','); + const query = `"${catalogFilename}" path:${catalogPath}`; + const searchResults = client + .searchCode(workspacePath, query, { fields }) + .iterateResults(); for await (const searchResult of searchResults) { // not a file match, but a code match - if (searchResult.path_matches.length === 0) { + if (searchResult.path_matches!.length === 0) { continue; } - const repository = searchResult.file.commit.repository; + const repository = searchResult.file!.commit!.repository!; if (!matchesPostFilters(repository, projectSearchPath, repoSearchPath)) { continue; } - const repoUrl = repository.links.html.href; + const repoUrl = repository.links!.html!.href; const branch = repository.mainbranch?.name ?? DEFAULT_BRANCH; - const filePath = searchResult.file.path; + const filePath = searchResult.file!.path; const location = `${repoUrl}/src/${branch}/${filePath}`; result.matches.push(location); @@ -268,7 +279,7 @@ export async function readBitbucketCloudLocations( return readBitbucketCloud(client, target).then(result => { const matches = result.matches.map(repository => { const branch = repository.mainbranch?.name ?? DEFAULT_BRANCH; - return `${repository.links.html.href}/src/${branch}${catalogPath}`; + return `${repository.links!.html!.href}/src/${branch}${catalogPath}`; }); return { @@ -281,7 +292,7 @@ export async function readBitbucketCloudLocations( export async function readBitbucketCloud( client: BitbucketCloudClient, target: string, -): Promise> { +): Promise> { const { workspacePath, queryParam: q, @@ -289,13 +300,10 @@ export async function readBitbucketCloud( repoSearchPath, } = parseBitbucketCloudUrl(target); - const repositories = paginated20( - options => client.listRepositoriesByWorkspace(workspacePath, options), - { - q, - }, - ); - const result: Result = { + const repositories = client + .listRepositoriesByWorkspace(workspacePath, { q }) + .iterateResults(); + const result: Result = { scanned: 0, matches: [], }; @@ -310,13 +318,13 @@ export async function readBitbucketCloud( } function matchesPostFilters( - repository: BitbucketRepository20, + repository: Models.Repository, projectSearchPath: RegExp | undefined, repoSearchPath: RegExp | undefined, ): boolean { return ( - (!projectSearchPath || projectSearchPath.test(repository.project.key)) && - (!repoSearchPath || repoSearchPath.test(repository.slug)) + (!projectSearchPath || projectSearchPath.test(repository.project!.key!)) && + (!repoSearchPath || repoSearchPath.test(repository.slug!)) ); } diff --git a/plugins/catalog-backend-module-bitbucket/src/lib/BitbucketCloudClient.ts b/plugins/catalog-backend-module-bitbucket/src/lib/BitbucketCloudClient.ts deleted file mode 100644 index 80826ce154..0000000000 --- a/plugins/catalog-backend-module-bitbucket/src/lib/BitbucketCloudClient.ts +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright 2021 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { - BitbucketIntegrationConfig, - getBitbucketRequestOptions, -} from '@backstage/integration'; -import fetch from 'node-fetch'; -import { BitbucketRepository20 } from './types'; - -export class BitbucketCloudClient { - private readonly config: BitbucketIntegrationConfig; - - constructor(options: { config: BitbucketIntegrationConfig }) { - this.config = options.config; - } - - async searchCode( - workspace: string, - query: string, - options?: ListOptions20, - ): Promise> { - // load all fields relevant for creating refs later, but not more - const fields = [ - // exclude code/content match details - '-values.content_matches', - // include/add relevant repository details - '+values.file.commit.repository.mainbranch.name', - '+values.file.commit.repository.project.key', - '+values.file.commit.repository.slug', - // remove irrelevant links - '-values.*.links', - '-values.*.*.links', - '-values.*.*.*.links', - // ...except the one we need - '+values.file.commit.repository.links.html.href', - ].join(','); - - return this.pagedRequest( - `${this.config.apiBaseUrl}/workspaces/${encodeURIComponent( - workspace, - )}/search/code`, - { - ...options, - fields: fields, - search_query: query, - }, - ); - } - - async listRepositoriesByWorkspace( - workspace: string, - options?: ListOptions20, - ): Promise> { - return this.pagedRequest( - `${this.config.apiBaseUrl}/repositories/${encodeURIComponent(workspace)}`, - options, - ); - } - - private async pagedRequest( - endpoint: string, - options?: ListOptions20, - ): Promise> { - const request = new URL(endpoint); - for (const key in options) { - if (options[key]) { - request.searchParams.append(key, options[key]!.toString()); - } - } - - const response = await fetch( - request.toString(), - getBitbucketRequestOptions(this.config), - ); - if (!response.ok) { - throw new Error( - `Unexpected response when fetching ${request.toString()}. Expected 200 but got ${ - response.status - } - ${response.statusText}`, - ); - } - return response.json() as Promise>; - } -} - -export type CodeSearchResultItem = { - type: string; - content_match_count: number; - path_matches: Array<{ - text: string; - match?: boolean; - }>; - file: { - path: string; - type: string; - commit: { - repository: BitbucketRepository20; - }; - }; -}; - -export type ListOptions20 = { - [key: string]: string | number | undefined; - page?: number | undefined; - pagelen?: number | undefined; -}; - -export type PagedResponse20 = { - page: number; - pagelen: number; - size: number; - values: T[]; - next: string; -}; - -export async function* paginated20( - request: (options: ListOptions20) => Promise>, - options?: ListOptions20, -) { - const opts = { page: 1, pagelen: 100, ...options }; - let res; - do { - res = await request(opts); - opts.page = opts.page + 1; - for (const item of res.values) { - yield item; - } - } while (res.next); -} diff --git a/plugins/catalog-backend-module-bitbucket/src/lib/index.ts b/plugins/catalog-backend-module-bitbucket/src/lib/index.ts index 8faf3db08f..c93bd7afd9 100644 --- a/plugins/catalog-backend-module-bitbucket/src/lib/index.ts +++ b/plugins/catalog-backend-module-bitbucket/src/lib/index.ts @@ -16,8 +16,6 @@ export { defaultRepositoryParser } from './BitbucketRepositoryParser'; export type { BitbucketRepositoryParser } from './BitbucketRepositoryParser'; -export { BitbucketCloudClient, paginated20 } from './BitbucketCloudClient'; export { BitbucketServerClient, paginated } from './BitbucketServerClient'; -export type { PagedResponse20 } from './BitbucketCloudClient'; export type { PagedResponse } from './BitbucketServerClient'; -export type { BitbucketRepository, BitbucketRepository20 } from './types'; +export type { BitbucketRepository } from './types'; diff --git a/plugins/catalog-backend-module-bitbucket/src/lib/types.ts b/plugins/catalog-backend-module-bitbucket/src/lib/types.ts index db8cf69688..b1bb416363 100644 --- a/plugins/catalog-backend-module-bitbucket/src/lib/types.ts +++ b/plugins/catalog-backend-module-bitbucket/src/lib/types.ts @@ -29,33 +29,3 @@ export type BitbucketRepository = BitbucketRepositoryBase & { }[] >; }; - -export type BitbucketRepository20 = BitbucketRepositoryBase & { - links: Record< - | 'self' - | 'source' - | 'html' - | 'avatar' - | 'pullrequests' - | 'commits' - | 'forks' - | 'watchers' - | 'downloads' - | 'hooks', - { - href: string; - name?: string; - } - > & - Record< - 'clone', - { - href: string; - name?: string; - }[] - >; - mainbranch?: { - type: string; - name: string; - }; -}; diff --git a/plugins/catalog-backend-module-gerrit/CHANGELOG.md b/plugins/catalog-backend-module-gerrit/CHANGELOG.md index 971e71820a..8654556d28 100644 --- a/plugins/catalog-backend-module-gerrit/CHANGELOG.md +++ b/plugins/catalog-backend-module-gerrit/CHANGELOG.md @@ -1,5 +1,51 @@ # @backstage/plugin-catalog-backend-module-gerrit +## 0.1.1 + +### Patch Changes + +- eb2544b21b: Inline config interfaces +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-backend@1.2.0 + - @backstage/backend-tasks@0.3.2 + - @backstage/backend-common@0.14.0 + - @backstage/integration@1.2.1 + - @backstage/catalog-model@1.0.3 + +## 0.1.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0-next.2 + - @backstage/integration@1.2.1-next.2 + - @backstage/backend-tasks@0.3.2-next.2 + - @backstage/plugin-catalog-backend@1.2.0-next.2 + +## 0.1.1-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/backend-tasks@0.3.2-next.1 + - @backstage/backend-common@0.13.6-next.1 + - @backstage/integration@1.2.1-next.1 + - @backstage/plugin-catalog-backend@1.2.0-next.1 + - @backstage/catalog-model@1.0.3-next.0 + +## 0.1.1-next.0 + +### Patch Changes + +- eb2544b21b: Inline config interfaces +- Updated dependencies + - @backstage/backend-tasks@0.3.2-next.0 + - @backstage/backend-common@0.13.6-next.0 + - @backstage/integration@1.2.1-next.0 + - @backstage/plugin-catalog-backend@1.2.0-next.0 + ## 0.1.0 ### Minor Changes diff --git a/plugins/catalog-backend-module-gerrit/package.json b/plugins/catalog-backend-module-gerrit/package.json index d2bb7ab8e6..55c33d2578 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.0", + "version": "0.1.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -28,22 +28,22 @@ "postpack": "backstage-cli package postpack" }, "dependencies": { - "@backstage/backend-common": "^0.13.3", - "@backstage/backend-tasks": "^0.3.1", - "@backstage/catalog-model": "^1.0.2", + "@backstage/backend-common": "^0.14.0", + "@backstage/backend-tasks": "^0.3.2", + "@backstage/catalog-model": "^1.0.3", "@backstage/config": "^1.0.1", "@backstage/errors": "^1.0.0", - "@backstage/integration": "^1.2.0", - "@backstage/plugin-catalog-backend": "^1.1.2", + "@backstage/integration": "^1.2.1", + "@backstage/plugin-catalog-backend": "^1.2.0", "fs-extra": "10.1.0", - "msw": "^0.35.0", + "msw": "^0.42.0", "node-fetch": "^2.6.7", "uuid": "^8.0.0", "winston": "^3.2.1" }, "devDependencies": { - "@backstage/backend-test-utils": "^0.1.24", - "@backstage/cli": "^0.17.1", + "@backstage/backend-test-utils": "^0.1.25", + "@backstage/cli": "^0.17.2", "@types/fs-extra": "^9.0.1" }, "files": [ diff --git a/plugins/catalog-backend-module-github/CHANGELOG.md b/plugins/catalog-backend-module-github/CHANGELOG.md index f3b676f218..553504c46f 100644 --- a/plugins/catalog-backend-module-github/CHANGELOG.md +++ b/plugins/catalog-backend-module-github/CHANGELOG.md @@ -1,5 +1,51 @@ # @backstage/plugin-catalog-backend-module-github +## 0.1.4 + +### Patch Changes + +- 8335a6f6f3: Adds an edit URL to the GitHub Teams Group entities. +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-backend@1.2.0 + - @backstage/backend-tasks@0.3.2 + - @backstage/backend-common@0.14.0 + - @backstage/integration@1.2.1 + - @backstage/catalog-model@1.0.3 + +## 0.1.4-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0-next.2 + - @backstage/integration@1.2.1-next.2 + - @backstage/backend-tasks@0.3.2-next.2 + - @backstage/plugin-catalog-backend@1.2.0-next.2 + +## 0.1.4-next.1 + +### Patch Changes + +- 8335a6f6f3: Adds an edit URL to the GitHub Teams Group entities. +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/backend-tasks@0.3.2-next.1 + - @backstage/backend-common@0.13.6-next.1 + - @backstage/integration@1.2.1-next.1 + - @backstage/plugin-catalog-backend@1.2.0-next.1 + - @backstage/catalog-model@1.0.3-next.0 + +## 0.1.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-tasks@0.3.2-next.0 + - @backstage/backend-common@0.13.6-next.0 + - @backstage/integration@1.2.1-next.0 + - @backstage/plugin-catalog-backend@1.2.0-next.0 + ## 0.1.3 ### Patch Changes diff --git a/plugins/catalog-backend-module-github/package.json b/plugins/catalog-backend-module-github/package.json index b0e29952de..680c812c4c 100644 --- a/plugins/catalog-backend-module-github/package.json +++ b/plugins/catalog-backend-module-github/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-github", "description": "A Backstage catalog backend module that helps integrate towards GitHub", - "version": "0.1.3", + "version": "0.1.4", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -33,24 +33,24 @@ "start": "backstage-cli package start" }, "dependencies": { - "@backstage/backend-common": "^0.13.3", - "@backstage/backend-tasks": "^0.3.1", - "@backstage/catalog-model": "^1.0.2", + "@backstage/backend-common": "^0.14.0", + "@backstage/backend-tasks": "^0.3.2", + "@backstage/catalog-model": "^1.0.3", "@backstage/config": "^1.0.1", "@backstage/errors": "^1.0.0", - "@backstage/integration": "^1.2.0", - "@backstage/plugin-catalog-backend": "^1.1.2", + "@backstage/integration": "^1.2.1", + "@backstage/plugin-catalog-backend": "^1.2.0", "@backstage/types": "^1.0.0", "@octokit/graphql": "^4.5.8", "lodash": "^4.17.21", - "msw": "^0.35.0", + "msw": "^0.42.0", "node-fetch": "^2.6.7", "uuid": "^8.0.0", "winston": "^3.2.1" }, "devDependencies": { - "@backstage/backend-test-utils": "^0.1.24", - "@backstage/cli": "^0.17.1", + "@backstage/backend-test-utils": "^0.1.25", + "@backstage/cli": "^0.17.2", "@types/lodash": "^4.14.151" }, "files": [ diff --git a/plugins/catalog-backend-module-github/src/lib/github.test.ts b/plugins/catalog-backend-module-github/src/lib/github.test.ts index fb4caa42c0..40dec0a297 100644 --- a/plugins/catalog-backend-module-github/src/lib/github.test.ts +++ b/plugins/catalog-backend-module-github/src/lib/github.test.ts @@ -86,6 +86,7 @@ describe('github', () => { name: 'Team', description: 'The one and only team', avatarUrl: 'http://example.com/team.jpeg', + editTeamUrl: 'http://example.com/orgs/blah/teams/team/edit', parentTeam: { slug: 'parent', combinedSlug: '', @@ -109,6 +110,11 @@ describe('github', () => { metadata: expect.objectContaining({ name: 'team', description: 'The one and only team', + annotations: { + 'github.com/team-slug': 'blah/team', + 'backstage.io/edit-url': + 'http://example.com/orgs/blah/teams/team/edit', + }, }), spec: { type: 'team', diff --git a/plugins/catalog-backend-module-github/src/lib/github.ts b/plugins/catalog-backend-module-github/src/lib/github.ts index b3dae26091..888df380f8 100644 --- a/plugins/catalog-backend-module-github/src/lib/github.ts +++ b/plugins/catalog-backend-module-github/src/lib/github.ts @@ -52,6 +52,7 @@ export type Team = { name?: string; description?: string; avatarUrl?: string; + editTeamUrl?: string; parentTeam?: Team; members: Connection; }; @@ -165,6 +166,7 @@ export async function getOrganizationTeams( name description avatarUrl + editTeamUrl parentTeam { slug } members(first: 100, membership: IMMEDIATE) { pageInfo { hasNextPage } @@ -179,14 +181,20 @@ export async function getOrganizationTeams( const groupMemberUsers = new Map(); const mapper = async (team: Team) => { + const annotations: { [annotationName: string]: string } = { + 'github.com/team-slug': team.combinedSlug, + }; + + if (team.editTeamUrl) { + annotations['backstage.io/edit-url'] = team.editTeamUrl; + } + const entity: GroupEntity = { apiVersion: 'backstage.io/v1alpha1', kind: 'Group', metadata: { name: team.slug, - annotations: { - 'github.com/team-slug': team.combinedSlug, - }, + annotations, }, spec: { type: 'team', diff --git a/plugins/catalog-backend-module-gitlab/CHANGELOG.md b/plugins/catalog-backend-module-gitlab/CHANGELOG.md index bd5e58522f..f785a935e6 100644 --- a/plugins/catalog-backend-module-gitlab/CHANGELOG.md +++ b/plugins/catalog-backend-module-gitlab/CHANGELOG.md @@ -1,5 +1,130 @@ # @backstage/plugin-catalog-backend-module-gitlab +## 0.1.4 + +### Patch Changes + +- eea8126171: Add a new provider `GitlabDiscoveryEntityProvider` as replacement for `GitlabDiscoveryProcessor` + + In order to migrate from the `GitlabDiscoveryProcessor` you need to apply + the following changes: + + **Before:** + + ```yaml + # app-config.yaml + + catalog: + locations: + - type: gitlab-discovery + target: https://company.gitlab.com/prefix/*/catalog-info.yaml + ``` + + ```ts + /* packages/backend/src/plugins/catalog.ts */ + + import { GitlabDiscoveryProcessor } from '@backstage/plugin-catalog-backend-module-gitlab'; + + const builder = await CatalogBuilder.create(env); + /** ... other processors ... */ + builder.addProcessor( + GitLabDiscoveryProcessor.fromConfig(env.config, { logger: env.logger }), + ); + ``` + + **After:** + + ```yaml + # app-config.yaml + + catalog: + providers: + gitlab: + yourProviderId: # identifies your dataset / provider independent of config changes + host: gitlab-host # Identifies one of the hosts set up in the integrations + branch: main # Optional. Uses `master` as default + group: example-group # Group and subgroup (if needed) to look for repositories + entityFilename: catalog-info.yaml # Optional. Defaults to `catalog-info.yaml` + ``` + + ```ts + /* packages/backend/src/plugins/catalog.ts */ + + import { GitlabDiscoveryEntityProvider } from '@backstage/plugin-catalog-backend-module-gitlab'; + + const builder = await CatalogBuilder.create(env); + /** ... other processors and/or providers ... */ + builder.addEntityProvider( + ...GitlabDiscoveryEntityProvider.fromConfig(env.config, { + logger: env.logger, + schedule: env.scheduler.createScheduledTaskRunner({ + frequency: { minutes: 30 }, + timeout: { minutes: 3 }, + }), + }), + ); + ``` + +- bad907d794: The `last_activity_after` timestamp is now being omitted when querying the GitLab API for the first time. +- 3ac4522537: do not create location object if file with component definition do not exists in project, that decrease count of request to gitlab with 404 status code. Now we can create processor with new flag to enable this logic: + + ```ts + const processor = GitLabDiscoveryProcessor.fromConfig(config, { + logger, + skipReposWithoutExactFileMatch: true, + }); + ``` + + **WARNING:** This new functionality does not support globs in the repo file path + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-backend@1.2.0 + - @backstage/backend-tasks@0.3.2 + - @backstage/backend-common@0.14.0 + - @backstage/integration@1.2.1 + - @backstage/catalog-model@1.0.3 + +## 0.1.4-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0-next.2 + - @backstage/integration@1.2.1-next.2 + - @backstage/plugin-catalog-backend@1.2.0-next.2 + +## 0.1.4-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/backend-common@0.13.6-next.1 + - @backstage/integration@1.2.1-next.1 + - @backstage/plugin-catalog-backend@1.2.0-next.1 + - @backstage/catalog-model@1.0.3-next.0 + +## 0.1.4-next.0 + +### Patch Changes + +- 3ac4522537: do not create location object if file with component definition do not exists in project, that decrease count of request to gitlab with 404 status code. Now we can create processor with new flag to enable this logic: + + ```ts + const processor = GitLabDiscoveryProcessor.fromConfig(config, { + logger, + skipReposWithoutExactFileMatch: true, + }); + ``` + + **WARNING:** This new functionality does not support globs in the repo file path + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + - @backstage/integration@1.2.1-next.0 + - @backstage/plugin-catalog-backend@1.2.0-next.0 + ## 0.1.3 ### Patch Changes diff --git a/plugins/catalog-backend-module-gitlab/api-report.md b/plugins/catalog-backend-module-gitlab/api-report.md index ec10f80939..1009da8209 100644 --- a/plugins/catalog-backend-module-gitlab/api-report.md +++ b/plugins/catalog-backend-module-gitlab/api-report.md @@ -6,8 +6,29 @@ import { CatalogProcessor } from '@backstage/plugin-catalog-backend'; import { CatalogProcessorEmit } from '@backstage/plugin-catalog-backend'; import { Config } from '@backstage/config'; +import { EntityProvider } from '@backstage/plugin-catalog-backend'; +import { EntityProviderConnection } from '@backstage/plugin-catalog-backend'; import { LocationSpec } from '@backstage/plugin-catalog-backend'; import { Logger } from 'winston'; +import { TaskRunner } from '@backstage/backend-tasks'; + +// @public +export class GitlabDiscoveryEntityProvider implements EntityProvider { + // (undocumented) + connect(connection: EntityProviderConnection): Promise; + // (undocumented) + static fromConfig( + config: Config, + options: { + logger: Logger; + schedule: TaskRunner; + }, + ): GitlabDiscoveryEntityProvider[]; + // (undocumented) + getProviderName(): string; + // (undocumented) + refresh(logger: Logger): Promise; +} // @public export class GitLabDiscoveryProcessor implements CatalogProcessor { @@ -16,6 +37,7 @@ export class GitLabDiscoveryProcessor implements CatalogProcessor { config: Config, options: { logger: Logger; + skipReposWithoutExactFileMatch?: boolean; }, ): GitLabDiscoveryProcessor; // (undocumented) diff --git a/plugins/catalog-backend-module-gitlab/config.d.ts b/plugins/catalog-backend-module-gitlab/config.d.ts new file mode 100644 index 0000000000..5b0d42c81d --- /dev/null +++ b/plugins/catalog-backend-module-gitlab/config.d.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. + */ + +export interface Config { + catalog?: { + /** + * List of provider-specific options and attributes + */ + providers?: { + /** + * GitlabDiscoveryEntityProvider configuration + */ + gitlab?: Record< + string, + { + /** + * (Required) Gitlab's host name. + * @visibility backend + */ + host: string; + /** + * (Required) Gitlab's group[/subgroup] where the discovery is done. + * @visibility backend + */ + group: string; + /** + * (Optional) Default branch to read the catalog-info.yaml file. + * If not set, 'master' will be used. + * @visibility backend + */ + branch?: string; + /** + * (Optional) The name used for the catalog file. + * If not set, 'catalog-info.yaml' will be used. + * @visibility backend + */ + entityFilename?: string; + } + >; + }; + }; +} diff --git a/plugins/catalog-backend-module-gitlab/package.json b/plugins/catalog-backend-module-gitlab/package.json index ff285efe69..4780d86530 100644 --- a/plugins/catalog-backend-module-gitlab/package.json +++ b/plugins/catalog-backend-module-gitlab/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-gitlab", "description": "A Backstage catalog backend module that helps integrate towards GitLab", - "version": "0.1.3", + "version": "0.1.4", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -33,22 +33,25 @@ "start": "backstage-cli package start" }, "dependencies": { - "@backstage/backend-common": "^0.13.3", - "@backstage/catalog-model": "^1.0.2", + "@backstage/backend-common": "^0.14.0", + "@backstage/catalog-model": "^1.0.3", + "@backstage/backend-tasks": "^0.3.2", "@backstage/config": "^1.0.1", "@backstage/errors": "^1.0.0", - "@backstage/integration": "^1.2.0", - "@backstage/plugin-catalog-backend": "^1.1.2", + "@backstage/integration": "^1.2.1", + "@backstage/plugin-catalog-backend": "^1.2.0", "@backstage/types": "^1.0.0", "lodash": "^4.17.21", - "msw": "^0.35.0", + "msw": "^0.42.0", "node-fetch": "^2.6.7", - "winston": "^3.2.1" + "winston": "^3.2.1", + "uuid": "^8.0.0" }, "devDependencies": { - "@backstage/backend-test-utils": "^0.1.24", - "@backstage/cli": "^0.17.1", - "@types/lodash": "^4.14.151" + "@backstage/backend-test-utils": "^0.1.25", + "@backstage/cli": "^0.17.2", + "@types/lodash": "^4.14.151", + "@types/uuid": "^8.0.0" }, "files": [ "dist" diff --git a/plugins/catalog-backend-module-gitlab/src/GitLabDiscoveryProcessor.test.ts b/plugins/catalog-backend-module-gitlab/src/GitLabDiscoveryProcessor.test.ts index dc4ffb7251..341167f279 100644 --- a/plugins/catalog-backend-module-gitlab/src/GitLabDiscoveryProcessor.test.ts +++ b/plugins/catalog-backend-module-gitlab/src/GitLabDiscoveryProcessor.test.ts @@ -17,45 +17,63 @@ import { getVoidLogger } from '@backstage/backend-common'; import { ConfigReader } from '@backstage/config'; import { LocationSpec } from '@backstage/plugin-catalog-backend'; -import { rest } from 'msw'; +import { rest, RestRequest } from 'msw'; import { setupServer } from 'msw/node'; import { GitLabDiscoveryProcessor, parseUrl } from './GitLabDiscoveryProcessor'; import { GitLabProject } from './lib'; const server = setupServer(); -const PROJECTS_URL = 'https://gitlab.fake/api/v4/projects'; -const GROUP_PROJECTS_URL = - 'https://gitlab.fake/api/v4/groups/group%2Fsubgroup/projects'; +const DOMAIN = 'gitlab.fake'; +const SERVER_URL = `https://${DOMAIN}`; +const API_URL = `${SERVER_URL}/api/v4`; +const PROJECTS_URL = `${API_URL}/projects`; +const GROUP_PROJECTS_URL = `${API_URL}/groups/group%2Fsubgroup/projects`; +const EXISTING_PROJECT_PATH = 'exist'; const PROJECT_LOCATION: LocationSpec = { type: 'gitlab-discovery', - target: 'https://gitlab.fake/blob/*/catalog-info.yaml', + target: `${SERVER_URL}/blob/*/catalog-info.yaml`, }; const PROJECT_LOCATION_MASTER_BRANCH: LocationSpec = { type: 'gitlab-discovery', - target: 'https://gitlab.fake/blob/master/catalog-info.yaml', + target: `${SERVER_URL}/blob/master/catalog-info.yaml`, }; const GROUP_LOCATION: LocationSpec = { type: 'gitlab-discovery', - target: 'https://gitlab.fake/group/subgroup/blob/*/catalog-info.yaml', + target: `${SERVER_URL}/group/subgroup/blob/*/catalog-info.yaml`, }; +const GROUP_LOCATION_CUSTOM_BRANCH: LocationSpec = { + type: 'gitlab-discovery', + target: `${SERVER_URL}/group/subgroup/blob/test/catalog-info.yaml`, +}; +const SERVER_TIME = '2001-01-01T12:34:56.000Z'; + function setupFakeServer( url: string, - callback: (request: { page: number; include_subgroups: boolean }) => { + listProjectsCallback: (request: { + page: number; + include_subgroups: boolean; + }) => { data: GitLabProject[]; nextPage?: number; }, + assertion?: (r: RestRequest) => any, ) { server.use( rest.get(url, (req, res, ctx) => { + // Send the request to the assertion to give the test an opportunity to inspect the parameters. + if (assertion !== undefined) { + assertion(req); + } + if (req.headers.get('private-token') !== 'test-token') { return res(ctx.status(401), ctx.json({})); } const page = req.url.searchParams.get('page'); const include_subgroups = req.url.searchParams.get('include_subgroups'); - const response = callback({ + const response = listProjectsCallback({ page: parseInt(page!, 10), include_subgroups: include_subgroups === 'true', }); @@ -75,6 +93,25 @@ function setupFakeServer( ctx.json(filteredData), ); }), + rest.head( + `${API_URL}/projects/:project_path/repository/files/:file_path`, + (req, res, ctx) => { + if (req.headers.get('private-token') !== 'test-token') { + return res(ctx.status(401), ctx.json({})); + } + const ref = req.url.searchParams.get('ref'); + + if (ref === 'main' || ref === 'master') { + return res(ctx.status(200)); + } + + if (EXISTING_PROJECT_PATH === req.params.project_path) { + return res(ctx.status(200)); + } + + return res(ctx.status(404)); + }, + ), ); } @@ -86,8 +123,8 @@ function getConfig(): any { integrations: { gitlab: [ { - host: 'gitlab.fake', - apiBaseUrl: 'https://gitlab.fake/api/v4', + host: DOMAIN, + apiBaseUrl: API_URL, token: 'test-token', }, ], @@ -95,11 +132,18 @@ function getConfig(): any { }; } -function getProcessor(config?: any): GitLabDiscoveryProcessor { +function getProcessor({ + config, + options, +}: { + config?: any; + options?: Partial[1]>; +} = {}): GitLabDiscoveryProcessor { return GitLabDiscoveryProcessor.fromConfig( new ConfigReader(config || getConfig()), { logger: getVoidLogger(), + ...options, }, ); } @@ -108,7 +152,7 @@ describe('GitlabDiscoveryProcessor', () => { beforeAll(() => { server.listen(); jest.useFakeTimers('modern'); - jest.setSystemTime(new Date('2001-01-01T12:34:56Z')); + jest.setSystemTime(new Date(SERVER_TIME)); }); afterEach(() => server.resetHandlers()); afterAll(() => { @@ -163,6 +207,15 @@ describe('GitlabDiscoveryProcessor', () => { default_branch: 'main', last_activity_at: '2021-08-05T11:03:05.774Z', web_url: 'https://gitlab.fake/1', + path_with_namespace: '1', + }, + { + id: 2, + archived: false, + default_branch: 'main', + last_activity_at: '2021-08-05T11:03:05.774Z', + web_url: 'https://gitlab.fake/g/2', + path_with_namespace: 'g/2', }, ], nextPage: 2, @@ -170,26 +223,29 @@ describe('GitlabDiscoveryProcessor', () => { case 2: return { data: [ - { - id: 2, - archived: false, - default_branch: 'master', - last_activity_at: '2021-08-05T11:03:05.774Z', - web_url: 'https://gitlab.fake/2', - }, { id: 3, - archived: true, // ARCHIVED + archived: false, default_branch: 'master', last_activity_at: '2021-08-05T11:03:05.774Z', web_url: 'https://gitlab.fake/3', + path_with_namespace: '3', }, { id: 4, + archived: true, // ARCHIVED + default_branch: 'master', + last_activity_at: '2021-08-05T11:03:05.774Z', + web_url: 'https://gitlab.fake/4', + path_with_namespace: '4', + }, + { + id: 5, archived: false, default_branch: undefined, // MISSING DEFAULT BRANCH last_activity_at: '2021-08-05T11:03:05.774Z', - web_url: 'https://gitlab.fake/4', + web_url: 'https://gitlab.fake/g/5', + path_with_namespace: 'g/5', }, ], }; @@ -215,7 +271,15 @@ describe('GitlabDiscoveryProcessor', () => { type: 'location', location: { type: 'url', - target: 'https://gitlab.fake/2/-/blob/master/catalog-info.yaml', + target: 'https://gitlab.fake/g/2/-/blob/main/catalog-info.yaml', + presence: 'optional', + }, + }, + { + type: 'location', + location: { + type: 'url', + target: 'https://gitlab.fake/3/-/blob/master/catalog-info.yaml', presence: 'optional', }, }, @@ -235,6 +299,7 @@ describe('GitlabDiscoveryProcessor', () => { default_branch: 'main', last_activity_at: '2021-08-05T11:03:05.774Z', web_url: 'https://gitlab.fake/1', + path_with_namespace: '1', }, ], }; @@ -275,6 +340,7 @@ describe('GitlabDiscoveryProcessor', () => { default_branch: 'main', last_activity_at: '2021-08-05T11:03:05.774Z', web_url: 'https://gitlab.fake/1', + path_with_namespace: '1', }, ], }; @@ -291,9 +357,14 @@ describe('GitlabDiscoveryProcessor', () => { expect(result).toHaveLength(1); }); - it('uses the previous scan timestamp to filter', async () => { - const processor = getProcessor(); - setupFakeServer(PROJECTS_URL, request => { + it('can filter based on file existing', async () => { + const processor = getProcessor({ + options: { skipReposWithoutExactFileMatch: true }, + }); + setupFakeServer(GROUP_PROJECTS_URL, request => { + if (!request.include_subgroups) { + throw new Error('include_subgroups should be set'); + } switch (request.page) { case 1: return { @@ -302,15 +373,17 @@ describe('GitlabDiscoveryProcessor', () => { id: 1, archived: false, default_branch: 'main', - last_activity_at: '2000-01-01T00:00:00Z', + last_activity_at: '2021-08-05T11:03:05.774Z', web_url: 'https://gitlab.fake/1', + path_with_namespace: '1', }, { - id: 2, + id: 1, archived: false, default_branch: 'main', - last_activity_at: '2002-01-01T00:00:00Z', - web_url: 'https://gitlab.fake/2', + last_activity_at: '2021-08-05T11:03:05.774Z', + web_url: `https://gitlab.fake/${EXISTING_PROJECT_PATH}`, + path_with_namespace: EXISTING_PROJECT_PATH, }, ], }; @@ -319,6 +392,53 @@ describe('GitlabDiscoveryProcessor', () => { } }); + const result: any[] = []; + await processor.readLocation(GROUP_LOCATION_CUSTOM_BRANCH, false, e => { + result.push(e); + }); + // If everything was set up correctly, we should have received the fake repo specified above + expect(result).toHaveLength(1); + }); + + it('uses the previous scan timestamp to filter', async () => { + const payload = { + data: [ + { + id: 1, + archived: false, + default_branch: 'main', + last_activity_at: '2000-01-01T00:00:00Z', + web_url: 'https://gitlab.fake/1', + }, + { + id: 2, + archived: false, + default_branch: 'main', + last_activity_at: '2002-01-01T00:00:00Z', + web_url: 'https://gitlab.fake/2', + }, + ], + }; + const processor = getProcessor(); + + setupFakeServer( + PROJECTS_URL, + request => { + switch (request.page) { + case 1: + return payload; + default: + throw new Error('Invalid request'); + } + }, + request => { + // We assert that the last activity timestamp is not being sent to the GitLab API as we expect the cache to be empty. + expect( + request.url.searchParams.get('last_activity_after'), + ).toBeNull(); + }, + ); + const result: any[] = []; // First scan should find all repos, since no last activity was cached @@ -329,6 +449,18 @@ describe('GitlabDiscoveryProcessor', () => { // Second scan should have used the mocked Date to set the last scanned time to 2001 // This should result in only the second repo being scanned, since that has a timestamp of 2002 + setupFakeServer( + PROJECTS_URL, + _ => { + return payload; + }, + request => { + // We assert that the last activity timestamp is being sent to the GitLab API since we expect it to be in the cache. + expect(request.url.searchParams.get('last_activity_after')).toMatch( + SERVER_TIME, + ); + }, + ); const result2: any[] = []; await processor.readLocation(PROJECT_LOCATION, false, e => { result2.push(e); @@ -349,7 +481,7 @@ describe('GitlabDiscoveryProcessor', () => { const config = getConfig(); config.integrations.gitlab[0].token = 'invalid'; await expect( - getProcessor(config).readLocation(PROJECT_LOCATION, false, _ => {}), + getProcessor({ config }).readLocation(PROJECT_LOCATION, false, _ => {}), ).rejects.toThrow(/Unauthorized/); }); @@ -357,7 +489,7 @@ describe('GitlabDiscoveryProcessor', () => { const config = getConfig(); delete config.integrations; await expect( - getProcessor(config).readLocation(PROJECT_LOCATION, false, _ => {}), + getProcessor({ config }).readLocation(PROJECT_LOCATION, false, _ => {}), ).rejects.toThrow(/no GitLab integration/); }); diff --git a/plugins/catalog-backend-module-gitlab/src/GitLabDiscoveryProcessor.ts b/plugins/catalog-backend-module-gitlab/src/GitLabDiscoveryProcessor.ts index 01de1f12a0..b03c665ebc 100644 --- a/plugins/catalog-backend-module-gitlab/src/GitLabDiscoveryProcessor.ts +++ b/plugins/catalog-backend-module-gitlab/src/GitLabDiscoveryProcessor.ts @@ -41,8 +41,12 @@ export class GitLabDiscoveryProcessor implements CatalogProcessor { private readonly integrations: ScmIntegrationRegistry; private readonly logger: Logger; private readonly cache: CacheClient; + private readonly skipReposWithoutExactFileMatch: boolean; - static fromConfig(config: Config, options: { logger: Logger }) { + static fromConfig( + config: Config, + options: { logger: Logger; skipReposWithoutExactFileMatch?: boolean }, + ): GitLabDiscoveryProcessor { const integrations = ScmIntegrations.fromConfig(config); const pluginCache = CacheManager.fromConfig(config).forPlugin('gitlab-discovery'); @@ -58,10 +62,13 @@ export class GitLabDiscoveryProcessor implements CatalogProcessor { integrations: ScmIntegrationRegistry; pluginCache: PluginCacheManager; logger: Logger; + skipReposWithoutExactFileMatch?: boolean; }) { this.integrations = options.integrations; this.cache = options.pluginCache.getClient(); this.logger = options.logger; + this.skipReposWithoutExactFileMatch = + options.skipReposWithoutExactFileMatch || false; } getProcessorName(): string { @@ -77,6 +84,7 @@ export class GitLabDiscoveryProcessor implements CatalogProcessor { return false; } + const startTime = new Date(); const { group, host, branch, catalogPath } = parseUrl(location.target); const integration = this.integrations.gitlab.byUrl(`https://${host}`); @@ -90,14 +98,18 @@ export class GitLabDiscoveryProcessor implements CatalogProcessor { config: integration.config, logger: this.logger, }); - const startTimestamp = Date.now(); this.logger.debug(`Reading GitLab projects from ${location.target}`); - const projects = paginated(options => client.listProjects(options), { + const lastActivity = (await this.cache.get(this.getCacheKey())) as string; + const opts = { group, - last_activity_after: await this.updateLastActivity(), page: 1, - }); + // We check for the existence of lastActivity and only set it if it's present to ensure + // that the options doesn't include the key so that the API doesn't receive an empty query parameter. + ...(lastActivity && { last_activity_after: lastActivity }), + }; + + const projects = paginated(options => client.listProjects(options), opts); const res: Result = { scanned: 0, @@ -114,6 +126,20 @@ export class GitLabDiscoveryProcessor implements CatalogProcessor { continue; } + if (this.skipReposWithoutExactFileMatch) { + const project_branch = branch === '*' ? project.default_branch : branch; + + const projectHasFile: boolean = await client.hasFile( + project.path_with_namespace, + project_branch, + catalogPath, + ); + + if (!projectHasFile) { + continue; + } + } + res.matches.push(project); } @@ -135,7 +161,10 @@ export class GitLabDiscoveryProcessor implements CatalogProcessor { ); } - const duration = ((Date.now() - startTimestamp) / 1000).toFixed(1); + // Save an ISO formatted string in the cache as that's what GitLab expects in the API request. + await this.cache.set(this.getCacheKey(), startTime.toISOString()); + + const duration = ((Date.now() - startTime.getTime()) / 1000).toFixed(1); this.logger.debug( `Read ${res.scanned} GitLab repositories in ${duration} seconds`, ); @@ -143,11 +172,8 @@ export class GitLabDiscoveryProcessor implements CatalogProcessor { return true; } - private async updateLastActivity(): Promise { - const cacheKey = `processors/${this.getProcessorName()}/last-activity`; - const lastActivity = await this.cache.get(cacheKey); - await this.cache.set(cacheKey, new Date().toISOString()); - return lastActivity as string | undefined; + private getCacheKey(): string { + return `processors/${this.getProcessorName()}/last-activity`; } } diff --git a/plugins/catalog-backend-module-gitlab/src/index.ts b/plugins/catalog-backend-module-gitlab/src/index.ts index e2a08509fb..3182210c81 100644 --- a/plugins/catalog-backend-module-gitlab/src/index.ts +++ b/plugins/catalog-backend-module-gitlab/src/index.ts @@ -21,3 +21,4 @@ */ export { GitLabDiscoveryProcessor } from './GitLabDiscoveryProcessor'; +export { GitlabDiscoveryEntityProvider } from './providers'; diff --git a/plugins/catalog-backend-module-gitlab/src/lib/client.test.ts b/plugins/catalog-backend-module-gitlab/src/lib/client.test.ts index e50ad34734..132dd0bc34 100644 --- a/plugins/catalog-backend-module-gitlab/src/lib/client.test.ts +++ b/plugins/catalog-backend-module-gitlab/src/lib/client.test.ts @@ -103,6 +103,21 @@ function setupFakeInstanceProjectsEndpoint( ); } +function setupFakeHasFileEndpoint(srv: SetupServerApi, apiBaseUrl: string) { + srv.use( + rest.head( + `${apiBaseUrl}/projects/group%2Frepo/repository/files/catalog-info.yaml`, + (req, res, ctx) => { + const branch = req.url.searchParams.get('ref'); + if (branch === 'master') { + return res(ctx.status(200)); + } + return res(ctx.status(404, 'Not Found')); + }, + ), + ); +} + describe('GitLabClient', () => { describe('isSelfManaged', () => { it('returns true if self managed instance', () => { @@ -266,3 +281,33 @@ describe('paginated', () => { expect(allItems).toHaveLength(4); }); }); + +describe('hasFile', () => { + let client: GitLabClient; + + beforeEach(() => { + setupFakeHasFileEndpoint(server, MOCK_CONFIG.apiBaseUrl); + client = new GitLabClient({ + config: MOCK_CONFIG, + logger: getVoidLogger(), + }); + }); + + it('should not find catalog file', async () => { + const hasFile = await client.hasFile( + 'group/repo', + 'master', + 'catalog-info.yaml', + ); + expect(hasFile).toBe(true); + }); + + it('should find catalog file', async () => { + const hasFile = await client.hasFile( + 'group/repo', + 'unknown', + 'catalog-info.yaml', + ); + expect(hasFile).toBe(false); + }); +}); diff --git a/plugins/catalog-backend-module-gitlab/src/lib/client.ts b/plugins/catalog-backend-module-gitlab/src/lib/client.ts index 4d42f5e068..adf0a78e8f 100644 --- a/plugins/catalog-backend-module-gitlab/src/lib/client.ts +++ b/plugins/catalog-backend-module-gitlab/src/lib/client.ts @@ -63,6 +63,43 @@ export class GitLabClient { return this.pagedRequest(`/projects`, options); } + /** + * General existence check. + * + * @param projectPath - The path to the project + * @param branch - The branch used to search + * @param filePath - The path to the file + */ + async hasFile( + projectPath: string, + branch: string, + filePath: string, + ): Promise { + const endpoint: string = `/projects/${encodeURIComponent( + projectPath, + )}/repository/files/${encodeURIComponent(filePath)}`; + const request = new URL(`${this.config.apiBaseUrl}${endpoint}`); + request.searchParams.append('ref', branch); + + const response = await fetch(request.toString(), { + headers: getGitLabRequestOptions(this.config).headers, + method: 'HEAD', + }); + + if (!response.ok) { + if (response.status >= 500) { + this.logger.debug( + `Unexpected response when fetching ${request.toString()}. Expected 200 but got ${ + response.status + } - ${response.statusText}`, + ); + } + return false; + } + + return true; + } + /** * Performs a request against a given paginated GitLab endpoint. * diff --git a/plugins/catalog-backend-module-gitlab/src/lib/index.ts b/plugins/catalog-backend-module-gitlab/src/lib/index.ts index 1df3d2cb84..53fad07994 100644 --- a/plugins/catalog-backend-module-gitlab/src/lib/index.ts +++ b/plugins/catalog-backend-module-gitlab/src/lib/index.ts @@ -15,4 +15,9 @@ */ export { GitLabClient, paginated } from './client'; -export type { GitLabProject } from './types'; +export type { + GitLabProject, + GitlabProviderConfig, + GitlabGroupDescription, +} from './types'; +export { readGitlabConfigs } from '../providers/config'; diff --git a/plugins/catalog-backend-module-gitlab/src/lib/types.ts b/plugins/catalog-backend-module-gitlab/src/lib/types.ts index d41ed73aac..69de9d28c7 100644 --- a/plugins/catalog-backend-module-gitlab/src/lib/types.ts +++ b/plugins/catalog-backend-module-gitlab/src/lib/types.ts @@ -14,10 +14,25 @@ * limitations under the License. */ +export type GitlabGroupDescription = { + id: number; + web_url: string; + projects: GitLabProject[]; +}; + export type GitLabProject = { id: number; default_branch?: string; archived: boolean; last_activity_at: string; web_url: string; + path_with_namespace?: string; +}; + +export type GitlabProviderConfig = { + host: string; + group: string; + id: string; + branch: string; + catalogFile: string; }; diff --git a/plugins/catalog-backend-module-gitlab/src/providers/GitlabDiscoveryEntityProvider.test.ts b/plugins/catalog-backend-module-gitlab/src/providers/GitlabDiscoveryEntityProvider.test.ts new file mode 100644 index 0000000000..dc70772d3d --- /dev/null +++ b/plugins/catalog-backend-module-gitlab/src/providers/GitlabDiscoveryEntityProvider.test.ts @@ -0,0 +1,230 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { getVoidLogger } from '@backstage/backend-common'; +import { TaskInvocationDefinition, TaskRunner } from '@backstage/backend-tasks'; +import { ConfigReader } from '@backstage/config'; +import { EntityProviderConnection } from '@backstage/plugin-catalog-backend'; +import { setupRequestMockHandlers } from '@backstage/backend-test-utils'; +import { GitlabDiscoveryEntityProvider } from './GitlabDiscoveryEntityProvider'; +import { rest } from 'msw'; +import { setupServer } from 'msw/node'; + +class PersistingTaskRunner implements TaskRunner { + private tasks: TaskInvocationDefinition[] = []; + + getTasks() { + return this.tasks; + } + + run(task: TaskInvocationDefinition): Promise { + this.tasks.push(task); + return Promise.resolve(undefined); + } +} + +const logger = getVoidLogger(); + +const server = setupServer(); + +describe('GitlabDiscoveryEntityProvider', () => { + setupRequestMockHandlers(server); + afterEach(() => jest.resetAllMocks()); + + it('no provider config', () => { + const schedule = new PersistingTaskRunner(); + const config = new ConfigReader({}); + const providers = GitlabDiscoveryEntityProvider.fromConfig(config, { + logger, + schedule, + }); + + expect(providers).toHaveLength(0); + }); + + it('single simple discovery config', () => { + const schedule = new PersistingTaskRunner(); + const config = new ConfigReader({ + integrations: { + gitlab: [ + { + host: 'test-gitlab', + apiBaseUrl: 'https://api.gitlab.example/api/v4', + token: '1234', + }, + ], + }, + catalog: { + providers: { + gitlab: { + 'test-id': { + host: 'test-gitlab', + group: 'test-group', + }, + }, + }, + }, + }); + const providers = GitlabDiscoveryEntityProvider.fromConfig(config, { + logger, + schedule, + }); + + expect(providers).toHaveLength(1); + expect(providers[0].getProviderName()).toEqual( + 'GitlabDiscoveryEntityProvider:test-id', + ); + }); + + it('multiple discovery configs', () => { + const schedule = new PersistingTaskRunner(); + const config = new ConfigReader({ + integrations: { + gitlab: [ + { + host: 'test-gitlab', + apiBaseUrl: 'https://api.gitlab.example/api/v4', + token: '1234', + }, + ], + }, + catalog: { + providers: { + gitlab: { + 'test-id': { + host: 'test-gitlab', + group: 'test-group', + }, + 'second-test': { + host: 'test-gitlab', + group: 'second-group', + }, + }, + }, + }, + }); + const providers = GitlabDiscoveryEntityProvider.fromConfig(config, { + logger, + schedule, + }); + + expect(providers).toHaveLength(2); + expect(providers[0].getProviderName()).toEqual( + 'GitlabDiscoveryEntityProvider:test-id', + ); + expect(providers[1].getProviderName()).toEqual( + 'GitlabDiscoveryEntityProvider:second-test', + ); + }); + + it('apply full update on scheduled execution', async () => { + const config = new ConfigReader({ + integrations: { + gitlab: [ + { + host: 'test-gitlab', + apiBaseUrl: 'https://api.gitlab.example/api/v4', + token: '1234', + }, + ], + }, + catalog: { + providers: { + gitlab: { + 'test-id': { + host: 'test-gitlab', + group: 'test-group', + }, + }, + }, + }, + }); + const schedule = new PersistingTaskRunner(); + const entityProviderConnection: EntityProviderConnection = { + applyMutation: jest.fn(), + }; + const provider = GitlabDiscoveryEntityProvider.fromConfig(config, { + logger, + schedule, + })[0]; + expect(provider.getProviderName()).toEqual( + 'GitlabDiscoveryEntityProvider:test-id', + ); + + server.use( + rest.get( + `https://api.gitlab.example/api/v4/groups/test-group/projects`, + (_req, res, ctx) => { + const response = [ + { + id: 123, + default_branch: 'master', + archived: false, + last_activity_at: new Date().toString(), + web_url: 'https://api.gitlab.example/test-group/test-repo', + path_with_namespace: 'test-group/test-repo', + }, + ]; + return res(ctx.json(response)); + }, + ), + rest.head( + 'https://api.gitlab.example/api/v4/projects/test-group%2Ftest-repo/repository/files/catalog-info.yaml', + (req, res, ctx) => { + if (req.url.searchParams.get('ref') === 'master') { + return res(ctx.status(200)); + } + return res(ctx.status(404, 'Not Found')); + }, + ), + ); + + await provider.connect(entityProviderConnection); + + const taskDef = schedule.getTasks()[0]; + expect(taskDef.id).toEqual('GitlabDiscoveryEntityProvider:test-id:refresh'); + await (taskDef.fn as () => Promise)(); + + const url = `https://api.gitlab.example/test-group/test-repo/-/blob/master/catalog-info.yaml`; + const expectedEntities = [ + { + entity: { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Location', + metadata: { + annotations: { + 'backstage.io/managed-by-location': `url:${url}`, + 'backstage.io/managed-by-origin-location': `url:${url}`, + }, + name: 'generated-cd37bf72a2fe92603f4255d9f49c6c1ead746a48', + }, + spec: { + presence: 'optional', + target: `${url}`, + type: 'url', + }, + }, + locationKey: 'GitlabDiscoveryEntityProvider:test-id', + }, + ]; + + expect(entityProviderConnection.applyMutation).toBeCalledTimes(1); + expect(entityProviderConnection.applyMutation).toBeCalledWith({ + type: 'full', + entities: expectedEntities, + }); + }); +}); diff --git a/plugins/catalog-backend-module-gitlab/src/providers/GitlabDiscoveryEntityProvider.ts b/plugins/catalog-backend-module-gitlab/src/providers/GitlabDiscoveryEntityProvider.ts new file mode 100644 index 0000000000..36c3ee90be --- /dev/null +++ b/plugins/catalog-backend-module-gitlab/src/providers/GitlabDiscoveryEntityProvider.ts @@ -0,0 +1,190 @@ +/* + * 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 { TaskRunner } from '@backstage/backend-tasks'; +import { Config } from '@backstage/config'; +import { GitLabIntegration, ScmIntegrations } from '@backstage/integration'; +import { + EntityProvider, + EntityProviderConnection, +} from '@backstage/plugin-catalog-backend'; +import { LocationSpec } from '@backstage/plugin-catalog-backend'; +import { Logger } from 'winston'; +import { + GitLabClient, + GitLabProject, + GitlabProviderConfig, + paginated, + readGitlabConfigs, +} from '../lib'; +import * as uuid from 'uuid'; +import { locationSpecToLocationEntity } from '@backstage/plugin-catalog-backend'; + +type Result = { + scanned: number; + matches: GitLabProject[]; +}; + +/** + * Discovers entity definition files in the groups of a Gitlab instance. + * @public + */ +export class GitlabDiscoveryEntityProvider implements EntityProvider { + private readonly config: GitlabProviderConfig; + private readonly integration: GitLabIntegration; + private readonly logger: Logger; + private readonly scheduleFn: () => Promise; + private connection?: EntityProviderConnection; + + static fromConfig( + config: Config, + options: { logger: Logger; schedule: TaskRunner }, + ): GitlabDiscoveryEntityProvider[] { + const providerConfigs = readGitlabConfigs(config); + const integrations = ScmIntegrations.fromConfig(config).gitlab; + const providers: GitlabDiscoveryEntityProvider[] = []; + + providerConfigs.forEach(providerConfig => { + const integration = integrations.byHost(providerConfig.host); + if (!integration) { + throw new Error( + `No gitlab integration found that matches host ${providerConfig.host}`, + ); + } + providers.push( + new GitlabDiscoveryEntityProvider({ + ...options, + config: providerConfig, + integration, + }), + ); + }); + return providers; + } + + private constructor(options: { + config: GitlabProviderConfig; + integration: GitLabIntegration; + logger: Logger; + schedule: TaskRunner; + }) { + this.config = options.config; + this.integration = options.integration; + this.logger = options.logger.child({ + target: this.getProviderName(), + }); + this.scheduleFn = this.createScheduleFn(options.schedule); + } + + getProviderName(): string { + return `GitlabDiscoveryEntityProvider:${this.config.id}`; + } + + async connect(connection: EntityProviderConnection): Promise { + this.connection = connection; + await this.scheduleFn(); + } + + private createScheduleFn(schedule: TaskRunner): () => Promise { + return async () => { + const taskId = `${this.getProviderName()}:refresh`; + return schedule.run({ + id: taskId, + fn: async () => { + const logger = this.logger.child({ + class: GitlabDiscoveryEntityProvider.prototype.constructor.name, + taskId, + taskInstanceId: uuid.v4(), + }); + + try { + await this.refresh(logger); + } catch (error) { + logger.error(error); + } + }, + }); + }; + } + + async refresh(logger: Logger): Promise { + if (!this.connection) { + throw new Error( + `Gitlab discovery connection not initialized for ${this.getProviderName()}`, + ); + } + + const client = new GitLabClient({ + config: this.integration.config, + logger: logger, + }); + + const projects = paginated( + options => client.listProjects(options), + { + group: this.config.group, + page: 1, + per_page: 50, + }, + ); + + const res: Result = { + scanned: 0, + matches: [], + }; + + for await (const project of projects) { + res.scanned++; + + if (project.archived) { + continue; + } + + if (this.config.branch === '*' && project.default_branch === undefined) { + continue; + } + + const project_branch = project.default_branch ?? this.config.branch; + + const projectHasFile: boolean = await client.hasFile( + project.path_with_namespace ?? '', + project_branch, + this.config.catalogFile, + ); + if (projectHasFile) { + res.matches.push(project); + } + } + + const locations = res.matches.map(p => this.createLocationSpec(p)); + await this.connection.applyMutation({ + type: 'full', + entities: locations.map(location => ({ + locationKey: this.getProviderName(), + entity: locationSpecToLocationEntity({ location }), + })), + }); + } + + private createLocationSpec(project: GitLabProject): LocationSpec { + const project_branch = project.default_branch ?? this.config.branch; + return { + type: 'url', + target: `${project.web_url}/-/blob/${project_branch}/${this.config.catalogFile}`, + presence: 'optional', + }; + } +} diff --git a/plugins/catalog-backend-module-gitlab/src/providers/config.test.ts b/plugins/catalog-backend-module-gitlab/src/providers/config.test.ts new file mode 100644 index 0000000000..d28930b151 --- /dev/null +++ b/plugins/catalog-backend-module-gitlab/src/providers/config.test.ts @@ -0,0 +1,106 @@ +/* + * 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 { ConfigReader } from '@backstage/config'; +import { readGitlabConfigs } from './config'; + +describe('config', () => { + it('empty gitlab config', () => { + const config = new ConfigReader({ + catalog: { + providers: {}, + }, + }); + + const result = readGitlabConfigs(config); + expect(result).toHaveLength(0); + }); + + it('valid config with default optional params', () => { + const config = new ConfigReader({ + catalog: { + providers: { + gitlab: { + test: { + group: 'group', + host: 'host', + }, + }, + }, + }, + }); + + const result = readGitlabConfigs(config); + expect(result).toHaveLength(1); + result.forEach(r => + expect(r).toStrictEqual({ + id: 'test', + group: 'group', + branch: 'master', + host: 'host', + catalogFile: 'catalog-info.yaml', + }), + ); + }); + + it('valid config with custom optional params', () => { + const config = new ConfigReader({ + catalog: { + providers: { + gitlab: { + test: { + group: 'group', + host: 'host', + branch: 'not-master', + entityFilename: 'custom-file.yaml', + }, + }, + }, + }, + }); + + const result = readGitlabConfigs(config); + expect(result).toHaveLength(1); + result.forEach(r => + expect(r).toStrictEqual({ + id: 'test', + group: 'group', + branch: 'not-master', + host: 'host', + catalogFile: 'custom-file.yaml', + }), + ); + }); + + it('missing params', () => { + const config = new ConfigReader({ + catalog: { + providers: { + gitlab: { + test: { + branch: 'not-master', + entityFilename: 'custom-file.yaml', + }, + }, + }, + }, + }); + + expect(() => readGitlabConfigs(config)).toThrow( + "Missing required config value at 'catalog.providers.gitlab.test.group'", + ); + }); +}); diff --git a/plugins/catalog-backend-module-gitlab/src/providers/config.ts b/plugins/catalog-backend-module-gitlab/src/providers/config.ts new file mode 100644 index 0000000000..f7eb316997 --- /dev/null +++ b/plugins/catalog-backend-module-gitlab/src/providers/config.ts @@ -0,0 +1,64 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Config } from '@backstage/config'; +import { GitlabProviderConfig } from '../lib/types'; + +/** + * Extracts the gitlab config from a config object + * + * @public + * + * @param config - The config object to extract from + */ +function readGitlabConfig(id: string, config: Config): GitlabProviderConfig { + const group = config.getString('group'); + const host = config.getString('host'); + const branch = config.getOptionalString('branch') ?? 'master'; + const catalogFile = + config.getOptionalString('entityFilename') ?? 'catalog-info.yaml'; + + return { + id, + group, + branch, + host, + catalogFile, + }; +} + +/** + * Extracts the gitlab config from a config object array + * + * @public + * + * @param config - The config object to extract from + */ +export function readGitlabConfigs(config: Config): GitlabProviderConfig[] { + const configs: GitlabProviderConfig[] = []; + + const providerConfigs = config.getOptionalConfig('catalog.providers.gitlab'); + + if (!providerConfigs) { + return configs; + } + + for (const id of providerConfigs.keys()) { + configs.push(readGitlabConfig(id, providerConfigs.getConfig(id))); + } + + return configs; +} diff --git a/plugins/catalog-backend-module-gitlab/src/providers/index.ts b/plugins/catalog-backend-module-gitlab/src/providers/index.ts new file mode 100644 index 0000000000..e7cb00a73f --- /dev/null +++ b/plugins/catalog-backend-module-gitlab/src/providers/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { GitlabDiscoveryEntityProvider } from './GitlabDiscoveryEntityProvider'; diff --git a/plugins/catalog-backend-module-ldap/CHANGELOG.md b/plugins/catalog-backend-module-ldap/CHANGELOG.md index 072d858124..072ef991d4 100644 --- a/plugins/catalog-backend-module-ldap/CHANGELOG.md +++ b/plugins/catalog-backend-module-ldap/CHANGELOG.md @@ -1,5 +1,39 @@ # @backstage/plugin-catalog-backend-module-ldap +## 0.5.0 + +### Minor Changes + +- 1f83f0bc84: Added the possibility to pass TLS configuration to ldap connection + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.2.0 + - @backstage/backend-tasks@0.3.2 + - @backstage/catalog-model@1.0.3 + +## 0.5.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-tasks@0.3.2-next.1 + - @backstage/plugin-catalog-backend@1.2.0-next.1 + - @backstage/catalog-model@1.0.3-next.0 + +## 0.5.0-next.0 + +### Minor Changes + +- 1f83f0bc84: Added the possibility to pass TLS configuration to ldap connection + +### Patch Changes + +- Updated dependencies + - @backstage/backend-tasks@0.3.2-next.0 + - @backstage/plugin-catalog-backend@1.2.0-next.0 + ## 0.4.3 ### Patch Changes diff --git a/plugins/catalog-backend-module-ldap/api-report.md b/plugins/catalog-backend-module-ldap/api-report.md index 3c6cd4849b..90e58b3fb2 100644 --- a/plugins/catalog-backend-module-ldap/api-report.md +++ b/plugins/catalog-backend-module-ldap/api-report.md @@ -82,6 +82,7 @@ export class LdapClient { logger: Logger, target: string, bind?: BindConfig, + tls?: TLSConfig, ): Promise; getRootDSE(): Promise; getVendor(): Promise; @@ -154,6 +155,7 @@ export class LdapOrgReaderProcessor implements CatalogProcessor { // @public export type LdapProviderConfig = { target: string; + tls?: TLSConfig; bind?: BindConfig; users: UserConfig; groups: GroupConfig; @@ -192,6 +194,11 @@ export function readLdapOrg( groups: GroupEntity[]; }>; +// @public +export type TLSConfig = { + rejectUnauthorized?: boolean; +}; + // @public export type UserConfig = { dn: string; diff --git a/plugins/catalog-backend-module-ldap/config.d.ts b/plugins/catalog-backend-module-ldap/config.d.ts index 8ae3145811..eb9564f20d 100644 --- a/plugins/catalog-backend-module-ldap/config.d.ts +++ b/plugins/catalog-backend-module-ldap/config.d.ts @@ -50,6 +50,14 @@ export interface Config { secret: string; }; + /** + * TLS settings + */ + tls?: { + // Node TLS rejectUnauthorized + rejectUnauthorized?: boolean; + }; + /** * The settings that govern the reading and interpretation of users. */ @@ -273,6 +281,14 @@ export interface Config { secret: string; }; + /** + * TLS settings + */ + tls?: { + // Node TLS rejectUnauthorized + rejectUnauthorized?: boolean; + }; + /** * The settings that govern the reading and interpretation of users. */ diff --git a/plugins/catalog-backend-module-ldap/package.json b/plugins/catalog-backend-module-ldap/package.json index 7bde21862d..288fccf7a5 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.4.3", + "version": "0.5.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -33,11 +33,11 @@ "start": "backstage-cli package start" }, "dependencies": { - "@backstage/backend-tasks": "^0.3.1", - "@backstage/catalog-model": "^1.0.2", + "@backstage/backend-tasks": "^0.3.2", + "@backstage/catalog-model": "^1.0.3", "@backstage/config": "^1.0.1", "@backstage/errors": "^1.0.0", - "@backstage/plugin-catalog-backend": "^1.1.2", + "@backstage/plugin-catalog-backend": "^1.2.0", "@backstage/types": "^1.0.0", "@types/ldapjs": "^2.2.0", "ldapjs": "^2.2.0", @@ -46,7 +46,7 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/cli": "^0.17.1", + "@backstage/cli": "^0.17.2", "@types/lodash": "^4.14.151" }, "files": [ diff --git a/plugins/catalog-backend-module-ldap/src/ldap/client.ts b/plugins/catalog-backend-module-ldap/src/ldap/client.ts index e778b0cfe4..a9283ecb94 100644 --- a/plugins/catalog-backend-module-ldap/src/ldap/client.ts +++ b/plugins/catalog-backend-module-ldap/src/ldap/client.ts @@ -18,7 +18,7 @@ import { ForwardedError } from '@backstage/errors'; import ldap, { Client, SearchEntry, SearchOptions } from 'ldapjs'; import { cloneDeep } from 'lodash'; import { Logger } from 'winston'; -import { BindConfig } from './config'; +import { BindConfig, TLSConfig } from './config'; import { errorString } from './util'; import { ActiveDirectoryVendor, @@ -40,8 +40,12 @@ export class LdapClient { logger: Logger, target: string, bind?: BindConfig, + tls?: TLSConfig, ): Promise { - const client = ldap.createClient({ url: target }); + const client = ldap.createClient({ + url: target, + tlsOptions: tls, + }); // We want to have a catch-all error handler at the top, since the default // behavior of the client is to blow up the entire process when it fails, diff --git a/plugins/catalog-backend-module-ldap/src/ldap/config.test.ts b/plugins/catalog-backend-module-ldap/src/ldap/config.test.ts index 292e9a218e..28c842294f 100644 --- a/plugins/catalog-backend-module-ldap/src/ldap/config.test.ts +++ b/plugins/catalog-backend-module-ldap/src/ldap/config.test.ts @@ -80,6 +80,7 @@ describe('readLdapConfig', () => { { target: 'target', bind: { dn: 'bdn', secret: 's' }, + tls: { rejectUnauthorized: false }, users: { dn: 'udn', options: { @@ -139,6 +140,7 @@ describe('readLdapConfig', () => { { target: 'target', bind: { dn: 'bdn', secret: 's' }, + tls: { rejectUnauthorized: false }, users: { dn: 'udn', options: { diff --git a/plugins/catalog-backend-module-ldap/src/ldap/config.ts b/plugins/catalog-backend-module-ldap/src/ldap/config.ts index 370c9491b3..9ae4b9ed8d 100644 --- a/plugins/catalog-backend-module-ldap/src/ldap/config.ts +++ b/plugins/catalog-backend-module-ldap/src/ldap/config.ts @@ -30,6 +30,8 @@ export type LdapProviderConfig = { // The prefix of the target that this matches on, e.g. // "ldaps://ds.example.net", with no trailing slash. target: string; + // TLS settings + tls?: TLSConfig; // The settings to use for the bind command. If none are specified, the bind // command is not issued. bind?: BindConfig; @@ -39,6 +41,16 @@ export type LdapProviderConfig = { groups: GroupConfig; }; +/** + * TLS settings + * + * @public + */ +export type TLSConfig = { + // Node TLS rejectUnauthorized + rejectUnauthorized?: boolean; +}; + /** * The settings to use for the a command. * @@ -185,6 +197,17 @@ export function readLdapConfig(config: Config): LdapProviderConfig[] { }); } + function readTlsConfig( + c: Config | undefined, + ): LdapProviderConfig['tls'] | undefined { + if (!c) { + return undefined; + } + return { + rejectUnauthorized: c.getOptionalBoolean('rejectUnauthorized'), + }; + } + function readBindConfig( c: Config | undefined, ): LdapProviderConfig['bind'] | undefined { @@ -312,6 +335,7 @@ export function readLdapConfig(config: Config): LdapProviderConfig[] { return providerConfigs.map(c => { const newConfig = { target: trimEnd(c.getString('target'), '/'), + tls: readTlsConfig(c.getOptionalConfig('tls')), bind: readBindConfig(c.getOptionalConfig('bind')), users: readUserConfig(c.getConfig('users')), groups: readGroupConfig(c.getConfig('groups')), diff --git a/plugins/catalog-backend-module-ldap/src/ldap/index.ts b/plugins/catalog-backend-module-ldap/src/ldap/index.ts index c3aace492f..6d7800fbfd 100644 --- a/plugins/catalog-backend-module-ldap/src/ldap/index.ts +++ b/plugins/catalog-backend-module-ldap/src/ldap/index.ts @@ -22,6 +22,7 @@ export type { GroupConfig, UserConfig, BindConfig, + TLSConfig, } from './config'; export type { LdapVendor } from './vendors'; export { diff --git a/plugins/catalog-backend-module-ldap/src/processors/LdapOrgEntityProvider.ts b/plugins/catalog-backend-module-ldap/src/processors/LdapOrgEntityProvider.ts index b31b7ae266..b38ea51cc0 100644 --- a/plugins/catalog-backend-module-ldap/src/processors/LdapOrgEntityProvider.ts +++ b/plugins/catalog-backend-module-ldap/src/processors/LdapOrgEntityProvider.ts @@ -179,6 +179,7 @@ export class LdapOrgEntityProvider implements EntityProvider { this.options.logger, this.options.provider.target, this.options.provider.bind, + this.options.provider.tls, ); const { users, groups } = await readLdapOrg( diff --git a/plugins/catalog-backend-module-ldap/src/processors/LdapOrgReaderProcessor.ts b/plugins/catalog-backend-module-ldap/src/processors/LdapOrgReaderProcessor.ts index 74a7e153b9..fcfdb43eb1 100644 --- a/plugins/catalog-backend-module-ldap/src/processors/LdapOrgReaderProcessor.ts +++ b/plugins/catalog-backend-module-ldap/src/processors/LdapOrgReaderProcessor.ts @@ -103,6 +103,7 @@ export class LdapOrgReaderProcessor implements CatalogProcessor { this.logger, provider.target, provider.bind, + provider.tls, ); const { users, groups } = await readLdapOrg( client, diff --git a/plugins/catalog-backend-module-msgraph/CHANGELOG.md b/plugins/catalog-backend-module-msgraph/CHANGELOG.md index 3a88e1f3e5..d6a0f7c7b7 100644 --- a/plugins/catalog-backend-module-msgraph/CHANGELOG.md +++ b/plugins/catalog-backend-module-msgraph/CHANGELOG.md @@ -1,5 +1,33 @@ # @backstage/plugin-catalog-backend-module-msgraph +## 0.3.3 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-backend@1.2.0 + - @backstage/backend-tasks@0.3.2 + - @backstage/catalog-model@1.0.3 + +## 0.3.3-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/backend-tasks@0.3.2-next.1 + - @backstage/plugin-catalog-backend@1.2.0-next.1 + - @backstage/catalog-model@1.0.3-next.0 + +## 0.3.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-tasks@0.3.2-next.0 + - @backstage/plugin-catalog-backend@1.2.0-next.0 + ## 0.3.2 ### Patch Changes diff --git a/plugins/catalog-backend-module-msgraph/package.json b/plugins/catalog-backend-module-msgraph/package.json index d9f8b9753a..3131dc6daa 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.3.2", + "version": "0.3.3", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -34,10 +34,10 @@ }, "dependencies": { "@azure/msal-node": "^1.1.0", - "@backstage/backend-tasks": "^0.3.1", - "@backstage/catalog-model": "^1.0.2", + "@backstage/backend-tasks": "^0.3.2", + "@backstage/catalog-model": "^1.0.3", "@backstage/config": "^1.0.1", - "@backstage/plugin-catalog-backend": "^1.1.2", + "@backstage/plugin-catalog-backend": "^1.2.0", "@microsoft/microsoft-graph-types": "^2.6.0", "@types/node-fetch": "^2.5.12", "lodash": "^4.17.21", @@ -48,11 +48,11 @@ "qs": "^6.9.4" }, "devDependencies": { - "@backstage/backend-common": "^0.13.3", - "@backstage/backend-test-utils": "^0.1.24", - "@backstage/cli": "^0.17.1", + "@backstage/backend-common": "^0.14.0", + "@backstage/backend-test-utils": "^0.1.25", + "@backstage/cli": "^0.17.2", "@types/lodash": "^4.14.151", - "msw": "^0.35.0" + "msw": "^0.42.0" }, "files": [ "dist", diff --git a/plugins/catalog-backend/CHANGELOG.md b/plugins/catalog-backend/CHANGELOG.md index c4679de20a..f235285617 100644 --- a/plugins/catalog-backend/CHANGELOG.md +++ b/plugins/catalog-backend/CHANGELOG.md @@ -1,5 +1,86 @@ # @backstage/plugin-catalog-backend +## 1.2.0 + +### Minor Changes + +- b594679ae3: Allow array as non-spread arguments at the `CatalogBuilder`. + + ```typescript + builder.addEntityProvider(...getArrayOfProviders()); + ``` + + can be simplified to + + ```typescript + builder.addEntityProvider(getArrayOfProviders()); + ``` + +### Patch Changes + +- 8838b13038: Disallow anything but `'url'` locations from being registered via the location service. +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-search-common@0.3.5 + - @backstage/plugin-permission-node@0.6.2 + - @backstage/plugin-catalog-common@1.0.3 + - @backstage/backend-common@0.14.0 + - @backstage/integration@1.2.1 + - @backstage/catalog-client@1.0.3 + - @backstage/plugin-permission-common@0.6.2 + - @backstage/catalog-model@1.0.3 + - @backstage/plugin-scaffolder-common@1.1.1 + +## 1.2.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-common@0.3.5-next.1 + - @backstage/plugin-catalog-common@1.0.3-next.1 + - @backstage/backend-common@0.14.0-next.2 + - @backstage/integration@1.2.1-next.2 + - @backstage/plugin-permission-node@0.6.2-next.2 + +## 1.2.0-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/backend-common@0.13.6-next.1 + - @backstage/catalog-client@1.0.3-next.0 + - @backstage/integration@1.2.1-next.1 + - @backstage/plugin-permission-common@0.6.2-next.0 + - @backstage/plugin-permission-node@0.6.2-next.1 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-common@1.0.3-next.0 + - @backstage/plugin-search-common@0.3.5-next.0 + - @backstage/plugin-scaffolder-common@1.1.1-next.0 + +## 1.2.0-next.0 + +### Minor Changes + +- b594679ae3: Allow array as non-spread arguments at the `CatalogBuilder`. + + ```typescript + builder.addEntityProvider(...getArrayOfProviders()); + ``` + + can be simplified to + + ```typescript + builder.addEntityProvider(getArrayOfProviders()); + ``` + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + - @backstage/integration@1.2.1-next.0 + - @backstage/plugin-permission-node@0.6.2-next.0 + ## 1.1.2 ### Patch Changes @@ -495,7 +576,7 @@ - 022507c860: A `DefaultCatalogCollatorFactory`, which works with the new stream-based search indexing subsystem, is now available. The `DefaultCatalogCollator` will continue to be available for those unable to upgrade to the stream-based - `@backstage/search-backend-node` (and related packages), however it is now + `@backstage/plugin-search-backend-node` (and related packages), however it is now marked as deprecated and will be removed in a future version. To upgrade this plugin and the search indexing subsystem in one go, check diff --git a/plugins/catalog-backend/package.json b/plugins/catalog-backend/package.json index c9781c1b99..c33dccd8a7 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.1.2", + "version": "1.2.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -34,17 +34,17 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/backend-common": "^0.13.3", - "@backstage/catalog-client": "^1.0.2", - "@backstage/catalog-model": "^1.0.2", + "@backstage/backend-common": "^0.14.0", + "@backstage/catalog-client": "^1.0.3", + "@backstage/catalog-model": "^1.0.3", "@backstage/config": "^1.0.1", "@backstage/errors": "^1.0.0", - "@backstage/integration": "^1.2.0", - "@backstage/plugin-catalog-common": "^1.0.2", - "@backstage/plugin-permission-common": "^0.6.1", - "@backstage/plugin-permission-node": "^0.6.1", - "@backstage/plugin-scaffolder-common": "^1.1.0", - "@backstage/plugin-search-common": "^0.3.4", + "@backstage/integration": "^1.2.1", + "@backstage/plugin-catalog-common": "^1.0.3", + "@backstage/plugin-permission-common": "^0.6.2", + "@backstage/plugin-permission-node": "^0.6.2", + "@backstage/plugin-scaffolder-common": "^1.1.1", + "@backstage/plugin-search-common": "^0.3.5", "@backstage/types": "^1.0.0", "@types/express": "^4.17.6", "codeowners-utils": "^1.0.2", @@ -68,17 +68,17 @@ "zod": "^3.11.6" }, "devDependencies": { - "@backstage/backend-test-utils": "^0.1.24", - "@backstage/cli": "^0.17.1", - "@backstage/plugin-permission-common": "^0.6.1", - "@backstage/plugin-search-backend-node": "0.6.1", + "@backstage/backend-test-utils": "^0.1.25", + "@backstage/cli": "^0.17.2", + "@backstage/plugin-permission-common": "^0.6.2", + "@backstage/plugin-search-backend-node": "0.6.2", "@types/core-js": "^2.5.4", "@types/git-url-parse": "^9.0.0", "@types/lodash": "^4.14.151", "@types/supertest": "^2.0.8", "@types/uuid": "^8.0.0", "better-sqlite3": "^7.5.0", - "msw": "^0.35.0", + "msw": "^0.42.0", "supertest": "^6.1.3", "wait-for-expect": "^3.0.2", "luxon": "^2.0.2" diff --git a/plugins/catalog-backend/src/search/DefaultCatalogCollator.ts b/plugins/catalog-backend/src/search/DefaultCatalogCollator.ts index bb44670443..f895ded85e 100644 --- a/plugins/catalog-backend/src/search/DefaultCatalogCollator.ts +++ b/plugins/catalog-backend/src/search/DefaultCatalogCollator.ts @@ -37,7 +37,7 @@ import { Permission } from '@backstage/plugin-permission-common'; /** * @public - * @deprecated Upgrade to a more recent `@backstage/search-backend-node` and + * @deprecated Upgrade to a more recent `@backstage/plugin-search-backend-node` and * use `DefaultCatalogCollatorFactory` instead. */ export class DefaultCatalogCollator { diff --git a/plugins/catalog-backend/src/service/DefaultLocationService.test.ts b/plugins/catalog-backend/src/service/DefaultLocationService.test.ts index 562bae4867..bd166cbd50 100644 --- a/plugins/catalog-backend/src/service/DefaultLocationService.test.ts +++ b/plugins/catalog-backend/src/service/DefaultLocationService.test.ts @@ -17,6 +17,7 @@ import { DefaultLocationService } from './DefaultLocationService'; import { CatalogProcessingOrchestrator } from '../processing/types'; import { LocationStore } from './types'; +import { InputError } from '@backstage/errors'; describe('DefaultLocationServiceTest', () => { const orchestrator: jest.Mocked = { @@ -251,6 +252,18 @@ describe('DefaultLocationServiceTest', () => { type: 'url', }); }); + + it('should not allow locations of unknown types', async () => { + await expect( + locationService.createLocation( + { + type: 'unknown', + target: 'https://backstage.io/catalog-info.yaml', + }, + false, + ), + ).rejects.toThrow(InputError); + }); }); describe('listLocations', () => { diff --git a/plugins/catalog-backend/src/service/DefaultLocationService.ts b/plugins/catalog-backend/src/service/DefaultLocationService.ts index 7a1b923068..fc6ffb0825 100644 --- a/plugins/catalog-backend/src/service/DefaultLocationService.ts +++ b/plugins/catalog-backend/src/service/DefaultLocationService.ts @@ -27,6 +27,7 @@ import { } from '../processing/types'; import { LocationInput, LocationService, LocationStore } from './types'; import { locationSpecToMetadataName } from '../util/conversion'; +import { InputError } from '@backstage/errors'; export class DefaultLocationService implements LocationService { constructor( @@ -38,6 +39,9 @@ export class DefaultLocationService implements LocationService { input: LocationInput, dryRun: boolean, ): Promise<{ location: Location; entities: Entity[]; exists?: boolean }> { + if (input.type !== 'url') { + throw new InputError(`Registered locations must be of type 'url'`); + } if (dryRun) { return this.dryRunCreateLocation(input); } diff --git a/plugins/catalog-common/CHANGELOG.md b/plugins/catalog-common/CHANGELOG.md index 43699f7f4a..72002894c4 100644 --- a/plugins/catalog-common/CHANGELOG.md +++ b/plugins/catalog-common/CHANGELOG.md @@ -1,5 +1,30 @@ # @backstage/plugin-catalog-common +## 1.0.3 + +### Patch Changes + +- 7d8acfc32e: Replaced all usages of `@backstage/search-common` with `@backstage/plugin-search-common` +- Updated dependencies + - @backstage/plugin-search-common@0.3.5 + - @backstage/plugin-permission-common@0.6.2 + +## 1.0.3-next.1 + +### Patch Changes + +- 7d8acfc32e: Replaced all usages of `@backstage/search-common` with `@backstage/plugin-search-common` +- Updated dependencies + - @backstage/plugin-search-common@0.3.5-next.1 + +## 1.0.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-common@0.6.2-next.0 + - @backstage/search-common@0.3.5-next.0 + ## 1.0.2 ### Patch Changes diff --git a/plugins/catalog-common/api-report.md b/plugins/catalog-common/api-report.md index c15cbae103..ab540d446b 100644 --- a/plugins/catalog-common/api-report.md +++ b/plugins/catalog-common/api-report.md @@ -4,7 +4,7 @@ ```ts import { BasicPermission } from '@backstage/plugin-permission-common'; -import { IndexableDocument } from '@backstage/search-common'; +import { IndexableDocument } from '@backstage/plugin-search-common'; import { ResourcePermission } from '@backstage/plugin-permission-common'; // @alpha diff --git a/plugins/catalog-common/package.json b/plugins/catalog-common/package.json index 41c9c1d3de..27e7764812 100644 --- a/plugins/catalog-common/package.json +++ b/plugins/catalog-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-common", "description": "Common functionalities for the catalog plugin", - "version": "1.0.2", + "version": "1.0.3", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -34,11 +34,11 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/plugin-permission-common": "^0.6.1", - "@backstage/search-common": "^0.3.4" + "@backstage/plugin-permission-common": "^0.6.2", + "@backstage/plugin-search-common": "^0.3.5" }, "devDependencies": { - "@backstage/cli": "^0.17.1" + "@backstage/cli": "^0.17.2" }, "files": [ "dist", diff --git a/plugins/catalog-common/src/search/CatalogEntityDocument.ts b/plugins/catalog-common/src/search/CatalogEntityDocument.ts index 834dde2568..0c53c369cf 100644 --- a/plugins/catalog-common/src/search/CatalogEntityDocument.ts +++ b/plugins/catalog-common/src/search/CatalogEntityDocument.ts @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { IndexableDocument } from '@backstage/search-common'; +import { IndexableDocument } from '@backstage/plugin-search-common'; /** * The Document format for an Entity in the Catalog for search diff --git a/plugins/catalog-graph/CHANGELOG.md b/plugins/catalog-graph/CHANGELOG.md index a6b285f65c..2afbca45c5 100644 --- a/plugins/catalog-graph/CHANGELOG.md +++ b/plugins/catalog-graph/CHANGELOG.md @@ -1,5 +1,35 @@ # @backstage/plugin-catalog-graph +## 0.2.18 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/catalog-client@1.0.3 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## 0.2.18-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/catalog-client@1.0.3-next.0 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## 0.2.18-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + ## 0.2.17 ### Patch Changes diff --git a/plugins/catalog-graph/package.json b/plugins/catalog-graph/package.json index 55458a005b..508453b387 100644 --- a/plugins/catalog-graph/package.json +++ b/plugins/catalog-graph/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-graph", - "version": "0.2.17", + "version": "0.2.18", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -24,11 +24,11 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-client": "^1.0.2", - "@backstage/catalog-model": "^1.0.2", - "@backstage/core-components": "^0.9.4", - "@backstage/core-plugin-api": "^1.0.2", - "@backstage/plugin-catalog-react": "^1.1.0", + "@backstage/catalog-client": "^1.0.3", + "@backstage/catalog-model": "^1.0.3", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", + "@backstage/plugin-catalog-react": "^1.1.1", "@backstage/theme": "^0.2.15", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -45,11 +45,11 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/plugin-catalog": "^1.2.0", - "@backstage/core-app-api": "^1.0.2", - "@backstage/dev-utils": "^1.0.2", - "@backstage/test-utils": "^1.1.0", + "@backstage/cli": "^0.17.2", + "@backstage/core-app-api": "^1.0.3", + "@backstage/dev-utils": "^1.0.3", + "@backstage/plugin-catalog": "^1.3.0", + "@backstage/test-utils": "^1.1.1", "@backstage/types": "^1.0.0", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", diff --git a/plugins/catalog-graphql/CHANGELOG.md b/plugins/catalog-graphql/CHANGELOG.md index eb9e5b0127..b7c6aa5723 100644 --- a/plugins/catalog-graphql/CHANGELOG.md +++ b/plugins/catalog-graphql/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-catalog-graphql +## 0.3.10 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/catalog-model@1.0.3 + +## 0.3.10-next.0 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/catalog-model@1.0.3-next.0 + ## 0.3.9 ### Patch Changes diff --git a/plugins/catalog-graphql/package.json b/plugins/catalog-graphql/package.json index efbd6502cd..3e631906a6 100644 --- a/plugins/catalog-graphql/package.json +++ b/plugins/catalog-graphql/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-graphql", "description": "An experimental Backstage catalog GraphQL module", - "version": "0.3.9", + "version": "0.3.10", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -34,7 +34,7 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-model": "^1.0.2", + "@backstage/catalog-model": "^1.0.3", "@backstage/config": "^1.0.1", "@backstage/types": "^1.0.0", "apollo-server": "^3.0.0", @@ -46,14 +46,14 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/test-utils": "^1.1.0", + "@backstage/cli": "^0.17.2", + "@backstage/test-utils": "^1.1.1", "@graphql-codegen/cli": "^2.3.1", "@graphql-codegen/graphql-modules-preset": "^2.3.2", "@graphql-codegen/typescript": "^2.4.2", "@graphql-codegen/typescript-resolvers": "^2.4.3", "@graphql-tools/schema": "^8.3.1", - "msw": "^0.35.0" + "msw": "^0.42.0" }, "files": [ "dist" diff --git a/plugins/catalog-import/CHANGELOG.md b/plugins/catalog-import/CHANGELOG.md index d0e07747e2..5ee8857f18 100644 --- a/plugins/catalog-import/CHANGELOG.md +++ b/plugins/catalog-import/CHANGELOG.md @@ -1,5 +1,45 @@ # @backstage/plugin-catalog-import +## 0.8.9 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- 05be420971: Updated catalog import page text so they go in the correct hierarchy order +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/integration@1.2.1 + - @backstage/catalog-client@1.0.3 + - @backstage/core-plugin-api@1.0.3 + - @backstage/integration-react@1.1.1 + - @backstage/catalog-model@1.0.3 + +## 0.8.9-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- 05be420971: Updated catalog import page text so they go in the correct hierarchy order +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/catalog-client@1.0.3-next.0 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/integration-react@1.1.1-next.1 + - @backstage/integration@1.2.1-next.1 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## 0.8.9-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + - @backstage/integration@1.2.1-next.0 + - @backstage/integration-react@1.1.1-next.0 + ## 0.8.8 ### Patch Changes diff --git a/plugins/catalog-import/package.json b/plugins/catalog-import/package.json index 6b9defebd8..0c72baaf9f 100644 --- a/plugins/catalog-import/package.json +++ b/plugins/catalog-import/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-import", "description": "A Backstage plugin the helps you import entities into your catalog", - "version": "0.8.8", + "version": "0.8.9", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -34,15 +34,15 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-client": "^1.0.2", - "@backstage/catalog-model": "^1.0.2", + "@backstage/catalog-client": "^1.0.3", + "@backstage/catalog-model": "^1.0.3", "@backstage/config": "^1.0.1", - "@backstage/core-components": "^0.9.4", - "@backstage/core-plugin-api": "^1.0.2", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", "@backstage/errors": "^1.0.0", - "@backstage/integration": "^1.2.0", - "@backstage/integration-react": "^1.1.0", - "@backstage/plugin-catalog-react": "^1.1.0", + "@backstage/integration": "^1.2.1", + "@backstage/integration-react": "^1.1.1", + "@backstage/plugin-catalog-react": "^1.1.1", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -60,17 +60,17 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/core-app-api": "^1.0.2", - "@backstage/dev-utils": "^1.0.2", - "@backstage/test-utils": "^1.1.0", + "@backstage/cli": "^0.17.2", + "@backstage/core-app-api": "^1.0.3", + "@backstage/dev-utils": "^1.0.3", + "@backstage/test-utils": "^1.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/react-hooks": "^8.0.0", "@testing-library/user-event": "^14.0.0", "@types/jest": "^26.0.7", "cross-fetch": "^3.1.5", - "msw": "^0.35.0" + "msw": "^0.42.0" }, "files": [ "dist", diff --git a/plugins/catalog-import/src/components/ImportInfoCard/ImportInfoCard.tsx b/plugins/catalog-import/src/components/ImportInfoCard/ImportInfoCard.tsx index c879cbe998..4779e2d64b 100644 --- a/plugins/catalog-import/src/components/ImportInfoCard/ImportInfoCard.tsx +++ b/plugins/catalog-import/src/components/ImportInfoCard/ImportInfoCard.tsx @@ -46,14 +46,14 @@ export const ImportInfoCard = (props: ImportInfoCardProps) => { const appTitle = configApi.getOptional('app.title') || 'Backstage'; const catalogImportApi = useApi(catalogImportApiRef); - const integrations = configApi.getConfig('integrations'); - const hasGithubIntegration = integrations.has('github'); + const hasGithubIntegration = configApi.has('integrations.github'); const catalogFilename = useCatalogFilename(); return ( { Enter the URL to your source code repository to add it to {appTitle}. - Link to an existing entity file + + Link to an existing entity file + Example: {exampleLocationUrl} @@ -72,7 +74,7 @@ export const ImportInfoCard = (props: ImportInfoCardProps) => { {hasGithubIntegration && ( <> - + Link to a repository{' '} diff --git a/plugins/catalog-react/CHANGELOG.md b/plugins/catalog-react/CHANGELOG.md index acc6123d20..ba7f7deb6d 100644 --- a/plugins/catalog-react/CHANGELOG.md +++ b/plugins/catalog-react/CHANGELOG.md @@ -1,5 +1,52 @@ # @backstage/plugin-catalog-react +## 1.1.1 + +### Patch Changes + +- 1f70704580: Accessibility updates: + + - Wrapped the `EntityLifecyclePicker`, `EntityOwnerPicker`, `EntityTagPicker`, in `label` elements + - Changed group name `Typography` component to `span` (from default `h6`), added `aria-label` to the `List` component, and `role` of `menuitem` to the container of the `MenuItem` component + +- 568f2d1e75: Table component no longer has drag and drop columns by default +- Updated dependencies + - @backstage/plugin-catalog-common@1.0.3 + - @backstage/core-components@0.9.5 + - @backstage/integration@1.2.1 + - @backstage/catalog-client@1.0.3 + - @backstage/core-plugin-api@1.0.3 + - @backstage/plugin-permission-common@0.6.2 + - @backstage/catalog-model@1.0.3 + - @backstage/plugin-permission-react@0.4.2 + +## 1.1.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/catalog-client@1.0.3-next.0 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/integration@1.2.1-next.1 + - @backstage/plugin-permission-common@0.6.2-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-permission-react@0.4.2-next.0 + - @backstage/plugin-catalog-common@1.0.3-next.0 + +## 1.1.1-next.0 + +### Patch Changes + +- 1f70704580: Accessibility updates: + + - Wrapped the `EntityLifecyclePicker`, `EntityOwnerPicker`, `EntityTagPicker`, in `label` elements + - Changed group name `Typography` component to `span` (from default `h6`), added `aria-label` to the `List` component, and `role` of `menuitem` to the container of the `MenuItem` component + +- Updated dependencies + - @backstage/core-components@0.9.5-next.0 + - @backstage/integration@1.2.1-next.0 + ## 1.1.0 ### Minor Changes diff --git a/plugins/catalog-react/package.json b/plugins/catalog-react/package.json index f661f363d0..47e99f23d8 100644 --- a/plugins/catalog-react/package.json +++ b/plugins/catalog-react/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-react", "description": "A frontend library that helps other Backstage plugins interact with the catalog", - "version": "1.1.0", + "version": "1.1.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -34,15 +34,15 @@ "start": "backstage-cli package start" }, "dependencies": { - "@backstage/catalog-client": "^1.0.2", - "@backstage/catalog-model": "^1.0.2", - "@backstage/core-components": "^0.9.4", - "@backstage/core-plugin-api": "^1.0.2", + "@backstage/catalog-client": "^1.0.3", + "@backstage/catalog-model": "^1.0.3", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", "@backstage/errors": "^1.0.0", - "@backstage/integration": "^1.2.0", - "@backstage/plugin-catalog-common": "^1.0.2", - "@backstage/plugin-permission-common": "^0.6.1", - "@backstage/plugin-permission-react": "^0.4.1", + "@backstage/integration": "^1.2.1", + "@backstage/plugin-catalog-common": "^1.0.3", + "@backstage/plugin-permission-common": "^0.6.2", + "@backstage/plugin-permission-react": "^0.4.2", "@backstage/theme": "^0.2.15", "@backstage/types": "^1.0.0", "@backstage/version-bridge": "^1.0.1", @@ -63,11 +63,11 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/core-app-api": "^1.0.2", - "@backstage/plugin-catalog-common": "^1.0.2", - "@backstage/plugin-scaffolder-common": "^1.1.0", - "@backstage/test-utils": "^1.1.0", + "@backstage/cli": "^0.17.2", + "@backstage/core-app-api": "^1.0.3", + "@backstage/plugin-catalog-common": "^1.0.3", + "@backstage/plugin-scaffolder-common": "^1.1.1", + "@backstage/test-utils": "^1.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/react-hooks": "^8.0.0", diff --git a/plugins/catalog-react/src/components/EntityTable/EntityTable.tsx b/plugins/catalog-react/src/components/EntityTable/EntityTable.tsx index 3aa6857120..424d8b940e 100644 --- a/plugins/catalog-react/src/components/EntityTable/EntityTable.tsx +++ b/plugins/catalog-react/src/components/EntityTable/EntityTable.tsx @@ -85,6 +85,7 @@ export const EntityTable = (props: EntityTableProps) => { paging: false, actionsColumnIndex: -1, padding: 'dense', + draggable: false, }} data={entities} /> diff --git a/plugins/catalog/CHANGELOG.md b/plugins/catalog/CHANGELOG.md index 4ca3cb0a29..5f4e9809c0 100644 --- a/plugins/catalog/CHANGELOG.md +++ b/plugins/catalog/CHANGELOG.md @@ -1,5 +1,59 @@ # @backstage/plugin-catalog +## 1.3.0 + +### Minor Changes + +- fe7614ea54: Add an optional icon to the Catalog and TechDocs search results + +### Patch Changes + +- 449dcef98e: Updates the `isKind`, `รฌsComponentType`, and `isNamespace` to allow an array of possible values +- 1f70704580: Accessibility updates: + + - Added screen reader elements to describe default table `Action` buttons + +- 915700f64f: In order to simplify analytics on top of the search experience in Backstage, the provided `<*ResultListItem />` component now captures a `discover` analytics event instead of a `click` event. This event includes the result rank as its `value` and, like a click, the URL/path clicked to as its `to` attribute. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/plugin-search-common@0.3.5 + - @backstage/plugin-search-react@0.2.1 + - @backstage/plugin-catalog-common@1.0.3 + - @backstage/core-components@0.9.5 + - @backstage/catalog-client@1.0.3 + - @backstage/core-plugin-api@1.0.3 + - @backstage/integration-react@1.1.1 + - @backstage/catalog-model@1.0.3 + +## 1.2.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/catalog-client@1.0.3-next.0 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/integration-react@1.1.1-next.1 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + - @backstage/plugin-search-react@0.2.1-next.0 + - @backstage/plugin-catalog-common@1.0.3-next.0 + - @backstage/plugin-search-common@0.3.5-next.0 + +## 1.2.1-next.0 + +### Patch Changes + +- 449dcef98e: Updates the `isKind`, `รฌsComponentType`, and `isNamespace` to allow an array of possible values +- 1f70704580: Accessibility updates: + + - Added screen reader elements to describe default table `Action` buttons + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + - @backstage/integration-react@1.1.1-next.0 + ## 1.2.0 ### Minor Changes diff --git a/plugins/catalog/api-report.md b/plugins/catalog/api-report.md index 3d426a0c8b..0d2d611412 100644 --- a/plugins/catalog/api-report.md +++ b/plugins/catalog/api-report.md @@ -112,6 +112,10 @@ export interface CatalogSearchResultListItemProps { // (undocumented) highlight?: ResultHighlight; // (undocumented) + icon?: ReactNode; + // (undocumented) + rank?: number; + // (undocumented) result: IndexableDocument; } @@ -207,6 +211,9 @@ export interface DependsOnResourcesCardProps { // @public (undocumented) export const EntityAboutCard: (props: AboutCardProps) => JSX.Element; +// @public (undocumented) +export type EntityContextMenuClassKey = 'button'; + // @public (undocumented) export const EntityDependencyOfComponentsCard: ( props: DependencyOfComponentsCardProps, @@ -390,6 +397,7 @@ export function isOrphan(entity: Entity): boolean; export type PluginCatalogComponentsNameToClassKey = { PluginCatalogEntityLinksEmptyState: EntityLinksEmptyStateClassKey; PluginCatalogSystemDiagramCard: SystemDiagramCardClassKey; + PluginCatalogEntityContextMenu: EntityContextMenuClassKey; }; // @public (undocumented) diff --git a/plugins/catalog/package.json b/plugins/catalog/package.json index 5331618829..792df94e18 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.2.0", + "version": "1.3.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -34,16 +34,16 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-client": "^1.0.2", - "@backstage/catalog-model": "^1.0.2", - "@backstage/core-components": "^0.9.4", - "@backstage/core-plugin-api": "^1.0.2", + "@backstage/catalog-client": "^1.0.3", + "@backstage/catalog-model": "^1.0.3", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", "@backstage/errors": "^1.0.0", - "@backstage/integration-react": "^1.1.0", - "@backstage/plugin-catalog-common": "^1.0.2", - "@backstage/plugin-catalog-react": "^1.1.0", - "@backstage/plugin-search-common": "^0.3.4", - "@backstage/plugin-search-react": "^0.2.0", + "@backstage/integration-react": "^1.1.1", + "@backstage/plugin-catalog-common": "^1.0.3", + "@backstage/plugin-catalog-react": "^1.1.1", + "@backstage/plugin-search-common": "^0.3.5", + "@backstage/plugin-search-react": "^0.2.1", "@backstage/theme": "^0.2.15", "@backstage/types": "^1.0.0", "@material-ui/core": "^4.12.2", @@ -61,11 +61,11 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/core-app-api": "^1.0.2", - "@backstage/dev-utils": "^1.0.2", - "@backstage/plugin-permission-react": "^0.4.1", - "@backstage/test-utils": "^1.1.0", + "@backstage/cli": "^0.17.2", + "@backstage/core-app-api": "^1.0.3", + "@backstage/dev-utils": "^1.0.3", + "@backstage/plugin-permission-react": "^0.4.2", + "@backstage/test-utils": "^1.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", diff --git a/plugins/catalog/src/components/CatalogSearchResultListItem/CatalogSearchResultListItem.tsx b/plugins/catalog/src/components/CatalogSearchResultListItem/CatalogSearchResultListItem.tsx index 5ba9d09949..a7307055c2 100644 --- a/plugins/catalog/src/components/CatalogSearchResultListItem/CatalogSearchResultListItem.tsx +++ b/plugins/catalog/src/components/CatalogSearchResultListItem/CatalogSearchResultListItem.tsx @@ -14,16 +14,18 @@ * limitations under the License. */ -import React from 'react'; +import React, { ReactNode } from 'react'; import { Box, Chip, Divider, ListItem, + ListItemIcon, ListItemText, makeStyles, } from '@material-ui/core'; import { Link } from '@backstage/core-components'; +import { useAnalytics } from '@backstage/core-plugin-api'; import { IndexableDocument, ResultHighlight, @@ -47,8 +49,10 @@ const useStyles = makeStyles({ * @public */ export interface CatalogSearchResultListItemProps { + icon?: ReactNode; result: IndexableDocument; highlight?: ResultHighlight; + rank?: number; } /** @public */ @@ -58,41 +62,54 @@ export function CatalogSearchResultListItem( const result = props.result as any; const classes = useStyles(); + const analytics = useAnalytics(); + const handleClick = () => { + analytics.captureEvent('discover', result.title, { + attributes: { to: result.location }, + value: props.rank, + }); + }; + return ( - - - - ) : ( - result.title - ) - } - secondary={ - props.highlight?.fields.text ? ( - - ) : ( - result.text - ) - } - /> - - {result.kind && } - {result.lifecycle && ( - - )} - + + + {props.icon && {props.icon}} +
+ + ) : ( + result.title + ) + } + secondary={ + props.highlight?.fields.text ? ( + + ) : ( + result.text + ) + } + /> + + {result.kind && ( + + )} + {result.lifecycle && ( + + )} + +
diff --git a/plugins/catalog/src/components/EntityContextMenu/EntityContextMenu.tsx b/plugins/catalog/src/components/EntityContextMenu/EntityContextMenu.tsx index 5664186ab1..db50eeab84 100644 --- a/plugins/catalog/src/components/EntityContextMenu/EntityContextMenu.tsx +++ b/plugins/catalog/src/components/EntityContextMenu/EntityContextMenu.tsx @@ -31,13 +31,21 @@ import React, { useState } from 'react'; import { IconComponent } from '@backstage/core-plugin-api'; import { useEntityPermission } from '@backstage/plugin-catalog-react'; import { catalogEntityDeletePermission } from '@backstage/plugin-catalog-common'; +import { BackstageTheme } from '@backstage/theme'; -// TODO(freben): It should probably instead be the case that Header sets the theme text color to white inside itself unconditionally instead -const useStyles = makeStyles({ - button: { - color: 'white', +/** @public */ +export type EntityContextMenuClassKey = 'button'; + +const useStyles = makeStyles( + (theme: BackstageTheme) => { + return { + button: { + color: theme.palette.bursts.fontColor, + }, + }; }, -}); + { name: 'PluginCatalogEntityContextMenu' }, +); // NOTE(freben): Intentionally not exported at this point, since it's part of // the unstable extra context menu items concept below diff --git a/plugins/catalog/src/components/EntityContextMenu/index.ts b/plugins/catalog/src/components/EntityContextMenu/index.ts new file mode 100644 index 0000000000..4a2deb20b4 --- /dev/null +++ b/plugins/catalog/src/components/EntityContextMenu/index.ts @@ -0,0 +1,18 @@ +/* + * 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 { EntityContextMenu } from './EntityContextMenu'; +export type { EntityContextMenuClassKey } from './EntityContextMenu'; diff --git a/plugins/catalog/src/index.ts b/plugins/catalog/src/index.ts index cfe4851d09..f0f94656d5 100644 --- a/plugins/catalog/src/index.ts +++ b/plugins/catalog/src/index.ts @@ -62,6 +62,7 @@ export type { } from './components/EntityLinksCard'; export type { SystemDiagramCardClassKey } from './components/SystemDiagramCard'; export type { DefaultCatalogPageProps } from './components/CatalogPage'; +export type { EntityContextMenuClassKey } from './components/EntityContextMenu'; export type { HasComponentsCardProps } from './components/HasComponentsCard'; export type { HasResourcesCardProps } from './components/HasResourcesCard'; export type { HasSubcomponentsCardProps } from './components/HasSubcomponentsCard'; diff --git a/plugins/catalog/src/overridableComponents.ts b/plugins/catalog/src/overridableComponents.ts index e76aafbb68..5cd82202d5 100644 --- a/plugins/catalog/src/overridableComponents.ts +++ b/plugins/catalog/src/overridableComponents.ts @@ -18,11 +18,13 @@ import { StyleRules } from '@material-ui/core/styles/withStyles'; import { EntityLinksEmptyStateClassKey } from './components/EntityLinksCard'; import { SystemDiagramCardClassKey } from './components/SystemDiagramCard'; +import { EntityContextMenuClassKey } from './components/EntityContextMenu'; /** @public */ export type PluginCatalogComponentsNameToClassKey = { PluginCatalogEntityLinksEmptyState: EntityLinksEmptyStateClassKey; PluginCatalogSystemDiagramCard: SystemDiagramCardClassKey; + PluginCatalogEntityContextMenu: EntityContextMenuClassKey; }; /** @public */ diff --git a/plugins/cicd-statistics-module-gitlab/CHANGELOG.md b/plugins/cicd-statistics-module-gitlab/CHANGELOG.md index 0f8a185cd2..de73c87469 100644 --- a/plugins/cicd-statistics-module-gitlab/CHANGELOG.md +++ b/plugins/cicd-statistics-module-gitlab/CHANGELOG.md @@ -1,5 +1,30 @@ # @backstage/plugin-cicd-statistics-module-gitlab +## 0.1.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + - @backstage/plugin-cicd-statistics@0.1.8 + +## 0.1.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-cicd-statistics@0.1.8-next.1 + +## 0.1.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-cicd-statistics@0.1.8-next.0 + ## 0.1.1 ### Patch Changes diff --git a/plugins/cicd-statistics-module-gitlab/package.json b/plugins/cicd-statistics-module-gitlab/package.json index b6fa60eb31..dd48ad7719 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.1", + "version": "0.1.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -29,16 +29,16 @@ "postpack": "backstage-cli package postpack" }, "dependencies": { - "@backstage/plugin-cicd-statistics": "^0.1.7", + "@backstage/plugin-cicd-statistics": "^0.1.8", "@gitbeaker/browser": "^35.6.0", "@gitbeaker/core": "^35.6.0", "luxon": "^2.0.2", "p-limit": "^4.0.0", - "@backstage/core-plugin-api": "^1.0.2", - "@backstage/catalog-model": "^1.0.2" + "@backstage/core-plugin-api": "^1.0.3", + "@backstage/catalog-model": "^1.0.3" }, "devDependencies": { - "@backstage/cli": "^0.17.1" + "@backstage/cli": "^0.17.2" }, "files": [ "dist" diff --git a/plugins/cicd-statistics/CHANGELOG.md b/plugins/cicd-statistics/CHANGELOG.md index 0f640057b3..498cfa47a3 100644 --- a/plugins/cicd-statistics/CHANGELOG.md +++ b/plugins/cicd-statistics/CHANGELOG.md @@ -1,5 +1,30 @@ # @backstage/plugin-cicd-statistics +## 0.1.8 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## 0.1.8-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## 0.1.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + ## 0.1.7 ### Patch Changes diff --git a/plugins/cicd-statistics/package.json b/plugins/cicd-statistics/package.json index 78599b558d..0859afd945 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.7", + "version": "0.1.8", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -37,9 +37,9 @@ "@types/luxon": "^2.0.5" }, "dependencies": { - "@backstage/catalog-model": "^1.0.2", - "@backstage/core-plugin-api": "^1.0.2", - "@backstage/plugin-catalog-react": "^1.1.0", + "@backstage/catalog-model": "^1.0.3", + "@backstage/core-plugin-api": "^1.0.3", + "@backstage/plugin-catalog-react": "^1.1.1", "@date-io/luxon": "^1.3.13", "@material-ui/core": "^4.9.13", "@material-ui/icons": "^4.11.2", diff --git a/plugins/circleci/CHANGELOG.md b/plugins/circleci/CHANGELOG.md index 130a6650d7..39cfcea568 100644 --- a/plugins/circleci/CHANGELOG.md +++ b/plugins/circleci/CHANGELOG.md @@ -1,5 +1,35 @@ # @backstage/plugin-circleci +## 0.3.6 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## 0.3.6-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## 0.3.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + ## 0.3.5 ### Patch Changes diff --git a/plugins/circleci/package.json b/plugins/circleci/package.json index 627ae10ff6..3e00add228 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.5", + "version": "0.3.6", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -35,10 +35,10 @@ "postpack": "backstage-cli package postpack" }, "dependencies": { - "@backstage/catalog-model": "^1.0.2", - "@backstage/core-components": "^0.9.4", - "@backstage/core-plugin-api": "^1.0.2", - "@backstage/plugin-catalog-react": "^1.1.0", + "@backstage/catalog-model": "^1.0.3", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", + "@backstage/plugin-catalog-react": "^1.1.1", "@backstage/theme": "^0.2.15", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -55,10 +55,10 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/core-app-api": "^1.0.2", - "@backstage/dev-utils": "^1.0.2", - "@backstage/test-utils": "^1.1.0", + "@backstage/cli": "^0.17.2", + "@backstage/core-app-api": "^1.0.3", + "@backstage/dev-utils": "^1.0.3", + "@backstage/test-utils": "^1.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", @@ -66,7 +66,7 @@ "@types/jest": "^26.0.7", "@types/node": "^16.11.26", "cross-fetch": "^3.1.5", - "msw": "^0.35.0" + "msw": "^0.42.0" }, "files": [ "dist" diff --git a/plugins/cloudbuild/CHANGELOG.md b/plugins/cloudbuild/CHANGELOG.md index 742eef4f96..8454cc5cd4 100644 --- a/plugins/cloudbuild/CHANGELOG.md +++ b/plugins/cloudbuild/CHANGELOG.md @@ -1,5 +1,35 @@ # @backstage/plugin-cloudbuild +## 0.3.6 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## 0.3.6-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## 0.3.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + ## 0.3.5 ### Patch Changes diff --git a/plugins/cloudbuild/package.json b/plugins/cloudbuild/package.json index 0c316f0f47..b1f14356e0 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.5", + "version": "0.3.6", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -34,10 +34,10 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-model": "^1.0.2", - "@backstage/core-components": "^0.9.4", - "@backstage/core-plugin-api": "^1.0.2", - "@backstage/plugin-catalog-react": "^1.1.0", + "@backstage/catalog-model": "^1.0.3", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", + "@backstage/plugin-catalog-react": "^1.1.1", "@backstage/theme": "^0.2.15", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -52,17 +52,17 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/core-app-api": "^1.0.2", - "@backstage/dev-utils": "^1.0.2", - "@backstage/test-utils": "^1.1.0", + "@backstage/cli": "^0.17.2", + "@backstage/core-app-api": "^1.0.3", + "@backstage/dev-utils": "^1.0.3", + "@backstage/test-utils": "^1.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", "@types/jest": "^26.0.7", "@types/node": "^16.11.26", "cross-fetch": "^3.1.5", - "msw": "^0.35.0" + "msw": "^0.42.0" }, "files": [ "dist" diff --git a/plugins/code-climate/CHANGELOG.md b/plugins/code-climate/CHANGELOG.md index 83cc86ebfb..7cc1362a4d 100644 --- a/plugins/code-climate/CHANGELOG.md +++ b/plugins/code-climate/CHANGELOG.md @@ -1,5 +1,35 @@ # @backstage/plugin-code-climate +## 0.1.6 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## 0.1.6-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## 0.1.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + ## 0.1.5 ### Patch Changes diff --git a/plugins/code-climate/package.json b/plugins/code-climate/package.json index ef18d12bef..193fc71f3c 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.5", + "version": "0.1.6", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -23,10 +23,10 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-model": "^1.0.2", - "@backstage/core-components": "^0.9.4", - "@backstage/core-plugin-api": "^1.0.2", - "@backstage/plugin-catalog-react": "^1.1.0", + "@backstage/catalog-model": "^1.0.3", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", + "@backstage/plugin-catalog-react": "^1.1.1", "@backstage/theme": "^0.2.15", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -40,9 +40,9 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/dev-utils": "^1.0.2", - "@backstage/test-utils": "^1.1.0", + "@backstage/cli": "^0.17.2", + "@backstage/dev-utils": "^1.0.3", + "@backstage/test-utils": "^1.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", @@ -51,7 +51,7 @@ "@types/luxon": "^2.0.9", "@types/node": "^16.11.26", "cross-fetch": "^3.1.5", - "msw": "^0.35.0" + "msw": "^0.42.0" }, "files": [ "dist" diff --git a/plugins/code-coverage-backend/CHANGELOG.md b/plugins/code-coverage-backend/CHANGELOG.md index ecebc0c82f..ebf82cc984 100644 --- a/plugins/code-coverage-backend/CHANGELOG.md +++ b/plugins/code-coverage-backend/CHANGELOG.md @@ -1,5 +1,43 @@ # @backstage/plugin-code-coverage-backend +## 0.1.31 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/backend-common@0.14.0 + - @backstage/integration@1.2.1 + - @backstage/catalog-client@1.0.3 + - @backstage/catalog-model@1.0.3 + +## 0.1.31-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0-next.2 + - @backstage/integration@1.2.1-next.2 + +## 0.1.31-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/backend-common@0.13.6-next.1 + - @backstage/catalog-client@1.0.3-next.0 + - @backstage/integration@1.2.1-next.1 + - @backstage/catalog-model@1.0.3-next.0 + +## 0.1.31-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + - @backstage/integration@1.2.1-next.0 + ## 0.1.30 ### Patch Changes diff --git a/plugins/code-coverage-backend/package.json b/plugins/code-coverage-backend/package.json index 2c3efa027a..0bc5281648 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.1.30", + "version": "0.1.31", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -23,12 +23,12 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/backend-common": "^0.13.3", - "@backstage/catalog-client": "^1.0.2", - "@backstage/catalog-model": "^1.0.2", + "@backstage/backend-common": "^0.14.0", + "@backstage/catalog-client": "^1.0.3", + "@backstage/catalog-model": "^1.0.3", "@backstage/config": "^1.0.1", "@backstage/errors": "^1.0.0", - "@backstage/integration": "^1.2.0", + "@backstage/integration": "^1.2.1", "@types/express": "^4.17.6", "express": "^4.17.1", "express-promise-router": "^4.1.0", @@ -39,10 +39,10 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", + "@backstage/cli": "^0.17.2", "@types/express-xml-bodyparser": "^0.3.2", "@types/supertest": "^2.0.8", - "msw": "^0.35.0", + "msw": "^0.42.0", "supertest": "^6.1.6", "xml2js": "^0.4.23" }, diff --git a/plugins/code-coverage/CHANGELOG.md b/plugins/code-coverage/CHANGELOG.md index 1e1ea8d1c7..474c324e98 100644 --- a/plugins/code-coverage/CHANGELOG.md +++ b/plugins/code-coverage/CHANGELOG.md @@ -1,5 +1,35 @@ # @backstage/plugin-code-coverage +## 0.1.33 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## 0.1.33-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## 0.1.33-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + ## 0.1.32 ### Patch Changes diff --git a/plugins/code-coverage/package.json b/plugins/code-coverage/package.json index d2163e9a90..f72d73d293 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.1.32", + "version": "0.1.33", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -24,12 +24,12 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-model": "^1.0.2", + "@backstage/catalog-model": "^1.0.3", "@backstage/config": "^1.0.1", - "@backstage/core-components": "^0.9.4", - "@backstage/core-plugin-api": "^1.0.2", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", "@backstage/errors": "^1.0.0", - "@backstage/plugin-catalog-react": "^1.1.0", + "@backstage/plugin-catalog-react": "^1.1.1", "@backstage/theme": "^0.2.15", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -46,10 +46,10 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/core-app-api": "^1.0.2", - "@backstage/dev-utils": "^1.0.2", - "@backstage/test-utils": "^1.1.0", + "@backstage/cli": "^0.17.2", + "@backstage/core-app-api": "^1.0.3", + "@backstage/dev-utils": "^1.0.3", + "@backstage/test-utils": "^1.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", @@ -58,7 +58,7 @@ "@types/node": "^16.11.26", "@types/recharts": "^1.8.15", "cross-fetch": "^3.1.5", - "msw": "^0.35.0" + "msw": "^0.42.0" }, "files": [ "dist" diff --git a/plugins/codescene/CHANGELOG.md b/plugins/codescene/CHANGELOG.md index 9e8705c859..2443fda056 100644 --- a/plugins/codescene/CHANGELOG.md +++ b/plugins/codescene/CHANGELOG.md @@ -1,5 +1,39 @@ # @backstage/plugin-codescene +## 0.1.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- ee2cd642c5: Updated dependency `rc-progress` to `3.3.3`. +- Updated dependencies + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + +## 0.1.1-next.2 + +### Patch Changes + +- ee2cd642c5: Updated dependency `rc-progress` to `3.3.3`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.2 + +## 0.1.1-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + +## 0.1.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.9.5-next.0 + ## 0.1.0 ### Minor Changes diff --git a/plugins/codescene/package.json b/plugins/codescene/package.json index d0acded7e4..47006c25c7 100644 --- a/plugins/codescene/package.json +++ b/plugins/codescene/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-codescene", - "version": "0.1.0", + "version": "0.1.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -23,14 +23,14 @@ }, "dependencies": { "@backstage/config": "^1.0.1", - "@backstage/core-components": "^0.9.4", - "@backstage/core-plugin-api": "^1.0.2", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", "@backstage/errors": "^1.0.0", "@backstage/theme": "^0.2.15", "@material-ui/core": "^4.9.10", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "^4.0.0-alpha.57", - "rc-progress": "3.3.2", + "rc-progress": "3.3.3", "react-router-dom": "6.0.0-beta.0", "react-use": "^17.2.4" }, @@ -38,16 +38,16 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/core-app-api": "^1.0.2", - "@backstage/dev-utils": "^1.0.2", - "@backstage/test-utils": "^1.1.0", + "@backstage/cli": "^0.17.2", + "@backstage/core-app-api": "^1.0.3", + "@backstage/dev-utils": "^1.0.3", + "@backstage/test-utils": "^1.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", "@types/jest": "*", "@types/node": "*", - "msw": "^0.35.0", + "msw": "^0.42.0", "cross-fetch": "^3.1.5" }, "files": [ diff --git a/plugins/config-schema/CHANGELOG.md b/plugins/config-schema/CHANGELOG.md index 026f3a66e0..1128a525fe 100644 --- a/plugins/config-schema/CHANGELOG.md +++ b/plugins/config-schema/CHANGELOG.md @@ -1,5 +1,30 @@ # @backstage/plugin-config-schema +## 0.1.29 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + +## 0.1.29-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + +## 0.1.29-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.9.5-next.0 + ## 0.1.28 ### Patch Changes diff --git a/plugins/config-schema/package.json b/plugins/config-schema/package.json index 3ea1eacb8a..0a6c6f2d88 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.28", + "version": "0.1.29", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -25,8 +25,8 @@ }, "dependencies": { "@backstage/config": "^1.0.1", - "@backstage/core-components": "^0.9.4", - "@backstage/core-plugin-api": "^1.0.2", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", "@backstage/errors": "^1.0.0", "@backstage/theme": "^0.2.15", "@backstage/types": "^1.0.0", @@ -41,17 +41,17 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/core-app-api": "^1.0.2", - "@backstage/dev-utils": "^1.0.2", - "@backstage/test-utils": "^1.1.0", + "@backstage/cli": "^0.17.2", + "@backstage/core-app-api": "^1.0.3", + "@backstage/dev-utils": "^1.0.3", + "@backstage/test-utils": "^1.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", "@types/jest": "^26.0.7", "@types/node": "^16.11.26", "cross-fetch": "^3.1.5", - "msw": "^0.35.0" + "msw": "^0.42.0" }, "files": [ "dist" diff --git a/plugins/cost-insights/CHANGELOG.md b/plugins/cost-insights/CHANGELOG.md index 7cc0c60e62..5b726f558c 100644 --- a/plugins/cost-insights/CHANGELOG.md +++ b/plugins/cost-insights/CHANGELOG.md @@ -1,5 +1,46 @@ # @backstage/plugin-cost-insights +## 0.11.28 + +### Patch Changes + +- dea1f32f44: In the README, a old path of the `sidebar` was updated to the current path. +- eb2544b21b: Add missing `export` in configuration schema. +- 2fc98ac50c: Fix broken app-config in the example in the README +- 2297510941: Fixed css to show large tooltips on cost overview graph +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## 0.11.28-next.2 + +### Patch Changes + +- 2fc98ac50c: Fix broken app-config in the example in the README +- Updated dependencies + - @backstage/core-components@0.9.5-next.2 + +## 0.11.28-next.1 + +### Patch Changes + +- 2297510941: Fixed css to show large tooltips on cost overview graph +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + +## 0.11.28-next.0 + +### Patch Changes + +- eb2544b21b: Add missing `export` in configuration schema. +- Updated dependencies + - @backstage/core-components@0.9.5-next.0 + ## 0.11.27 ### Patch Changes diff --git a/plugins/cost-insights/README.md b/plugins/cost-insights/README.md index c78f4d4b6d..824cf6f9fe 100644 --- a/plugins/cost-insights/README.md +++ b/plugins/cost-insights/README.md @@ -26,6 +26,8 @@ yarn add --cwd packages/app @backstage/plugin-cost-insights 2. Create a CostInsights client. Clients must implement the [CostInsightsApi](https://github.com/backstage/backstage/blob/master/plugins/cost-insights/src/api/CostInsightsApi.ts) interface. Create your own or [use a template](https://github.com/backstage/backstage/blob/master/plugins/cost-insights/src/example/templates/CostInsightsClient.ts) to get started. +Tip: You can also use the `ExampleCostInsightsClient` from `@backstage/plugin-cost-insights` to see how the plugin looks with some mock data. + ```ts // path/to/CostInsightsClient.ts import { CostInsightsApi } from '@backstage/plugin-cost-insights'; @@ -70,37 +72,50 @@ import { CostInsightsPage } from '@backstage/plugin-cost-insights'; To expose the plugin to your users, you can integrate the `cost-insights` route anyway that suits your application, but most commonly it is added to the Sidebar. ```diff -// packages/app/src/sidebar.tsx +// packages/app/src/components/Root/Root.tsx + import MoneyIcon from '@material-ui/icons/MonetizationOn'; ... - export const AppSidebar = () => ( - - - } to="/search"> - - - - {/* Global nav, not org-specific */} - }> - - - - - - -+ - - {/* End global nav */} - - - - } to="/settings"> - - - - ); + export const Root = ({ children }: PropsWithChildren<{}>) => ( + + + + } to="/search"> + + {({ toggleModal }) => } + + + + + + + + {/* End global nav */} + + ++ + + + + + + + } + to="/settings" + > + + + + {children} + +); ``` ## Configuration @@ -155,7 +170,7 @@ costInsights: ### Currencies (Optional) -In the `Cost Overview` panel, users can choose from a dropdown of currencies to see costs in, such as Engineers or USD. Currencies must be defined as keys on the `currencies` field. A user-friendly label and unit are **required**. If not set, the `defaultCurrencies` in `currenc.ts` will be used. +In the `Cost Overview` panel, users can choose from a dropdown of currencies to see costs in, such as Engineers or USD. Currencies must be defined as keys on the `currencies` field. A user-friendly label and unit are **required**. If not set, the `defaultCurrencies` in `currency.ts` will be used. A currency without `kind` is reserved to calculate cost for `engineers`. There should only be one currency without `kind`. @@ -171,16 +186,15 @@ costInsights: name: Some Other Cloud Product icon: data currencies: - metricA: - currencyA: - label: Currency A - unit: Unit A - currencyB: - label: Currency B - kind: CURRENCY_B - unit: Unit B - prefix: B - rate: 3.5 + currencyA: + label: Currency A + unit: Unit A + currencyB: + label: Currency B + kind: CURRENCY_B + unit: Unit B + prefix: B + rate: 3.5 ``` ## Alerts diff --git a/plugins/cost-insights/package.json b/plugins/cost-insights/package.json index f19739436e..9cb8d22db9 100644 --- a/plugins/cost-insights/package.json +++ b/plugins/cost-insights/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-cost-insights", "description": "A Backstage plugin that helps you keep track of your cloud spend", - "version": "0.11.27", + "version": "0.11.28", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -34,10 +34,10 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-model": "^1.0.2", + "@backstage/catalog-model": "^1.0.3", "@backstage/config": "^1.0.1", - "@backstage/core-components": "^0.9.4", - "@backstage/core-plugin-api": "^1.0.2", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", "@backstage/theme": "^0.2.15", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -60,10 +60,10 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/core-app-api": "^1.0.2", - "@backstage/dev-utils": "^1.0.2", - "@backstage/test-utils": "^1.1.0", + "@backstage/cli": "^0.17.2", + "@backstage/core-app-api": "^1.0.3", + "@backstage/dev-utils": "^1.0.3", + "@backstage/test-utils": "^1.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", @@ -75,7 +75,7 @@ "@types/yup": "^0.29.13", "canvas": "^2.6.1", "cross-fetch": "^3.1.5", - "msw": "^0.35.0" + "msw": "^0.42.0" }, "files": [ "dist", diff --git a/plugins/cost-insights/src/components/CostOverviewCard/CostOverviewCard.tsx b/plugins/cost-insights/src/components/CostOverviewCard/CostOverviewCard.tsx index 1a4bb4ba6e..3e77858967 100644 --- a/plugins/cost-insights/src/components/CostOverviewCard/CostOverviewCard.tsx +++ b/plugins/cost-insights/src/components/CostOverviewCard/CostOverviewCard.tsx @@ -106,7 +106,7 @@ export const CostOverviewCard = ({ const showMetricSelect = config.metrics.length && safeTabIndex === 0; return ( - + {dailyCostData.groupedCosts && } diff --git a/plugins/dynatrace/.eslintrc.js b/plugins/dynatrace/.eslintrc.js new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/plugins/dynatrace/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/dynatrace/CHANGELOG.md b/plugins/dynatrace/CHANGELOG.md new file mode 100644 index 0000000000..fddf3667af --- /dev/null +++ b/plugins/dynatrace/CHANGELOG.md @@ -0,0 +1,14 @@ +# @backstage/plugin-dynatrace + +## 0.1.0 + +### Minor Changes + +- 70027d09aa: Adds Dynatrace plugin + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 diff --git a/plugins/dynatrace/README.md b/plugins/dynatrace/README.md new file mode 100644 index 0000000000..150c9768af --- /dev/null +++ b/plugins/dynatrace/README.md @@ -0,0 +1,56 @@ +# Dynatrace + +Welcome to the Dynatrace plugin! + +## Getting started + +This plugin uses the Backstage proxy to communicate with Dynatrace's REST APIs. + +### Setup + +#### Dynatrace API Key + +The Dynatrace plugin will require the following information, to be used in the configuration options detailed below: + +- Dynatrace API URL, e.g. `https://my-dynatrace-instance.dynatrace.com/api/v2` +- Dynatrace API access token (see [documentation](https://www.dynatrace.com/support/help/dynatrace-api/basics/dynatrace-api-authentication)), with the following permissions: + - `entities.read` + - `problems.read` + +#### Plugin Configuration + +This plugin requires a proxy endpoint for Dynatrace configured in `app-config.yaml` like so: + +```yaml +proxy: + '/dynatrace': + target: 'https://example.dynatrace.com/api/v2' + headers: + Authorization: 'Api-Token ${DYNATRACE_ACCESS_TOKEN}' +``` + +It also requires a baseUrl for rendering links to problems in the table like so: + +```yaml +dynatrace: + baseUrl: 'https://example.dynatrace.com' +``` + +#### Catalog Configuration + +To show information from Dynatrace for a catalog entity, add the following annotation to `catalog-info.yaml`: + +```yaml +# catalog-info.yaml +# [...] +metadata: + annotations: + dynatrace.com/dynatrace-entity-id: DYNATRACE_ENTITY_ID +# [...] +``` + +The `DYNATRACE_ENTITY_ID` can be found in Dynatrace by browsing to the entity (a service, synthetic, frontend, workload, etc.). It will be located in the browser address bar in the `id` parameter and has the format `ENTITY_TYPE-ENTITY_ID`, where `ENTITY_TYPE` will be one of `SERVICE`, `SYNTHETIC_TEST`, or other, and `ENTITY_ID` will be a string of characters containing uppercase letters and numbers. + +## Disclaimer + +This plugin is not officially supported by Dynatrace. diff --git a/plugins/dynatrace/api-report.md b/plugins/dynatrace/api-report.md new file mode 100644 index 0000000000..026df62189 --- /dev/null +++ b/plugins/dynatrace/api-report.md @@ -0,0 +1,23 @@ +## API Report File for "@backstage/plugin-dynatrace" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +/// + +import { BackstagePlugin } from '@backstage/core-plugin-api'; +import { Entity } from '@backstage/catalog-model'; + +// @public +export const dynatracePlugin: BackstagePlugin<{}, {}>; + +// @public +export const DynatraceTab: () => JSX.Element; + +// Warning: (tsdoc-param-tag-with-invalid-type) The @param block should not include a JSDoc-style '{type}' +// +// @public +export const isDynatraceAvailable: (entity: Entity) => boolean; + +// (No @packageDocumentation comment for this package) +``` diff --git a/plugins/dynatrace/config.d.ts b/plugins/dynatrace/config.d.ts new file mode 100644 index 0000000000..b06d0c7362 --- /dev/null +++ b/plugins/dynatrace/config.d.ts @@ -0,0 +1,28 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export interface Config { + /** + * dynatrace config + * @visibility frontend + */ + dynatrace?: { + /** + * base url for links + * @visibility frontend + */ + baseUrl: string; + }; +} diff --git a/plugins/dynatrace/dev/index.tsx b/plugins/dynatrace/dev/index.tsx new file mode 100644 index 0000000000..d44f4381ec --- /dev/null +++ b/plugins/dynatrace/dev/index.tsx @@ -0,0 +1,27 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React from 'react'; +import { createDevApp } from '@backstage/dev-utils'; +import { dynatracePlugin, DynatraceTab } from '../src/plugin'; + +createDevApp() + .registerPlugin(dynatracePlugin) + .addPage({ + element: , + title: 'Root Page', + path: '/dynatrace', + }) + .render(); diff --git a/plugins/dynatrace/package.json b/plugins/dynatrace/package.json new file mode 100644 index 0000000000..9ee59f38a3 --- /dev/null +++ b/plugins/dynatrace/package.json @@ -0,0 +1,58 @@ +{ + "name": "@backstage/plugin-dynatrace", + "version": "0.1.0", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "publishConfig": { + "access": "public", + "main": "dist/index.esm.js", + "types": "dist/index.d.ts" + }, + "backstage": { + "role": "frontend-plugin" + }, + "scripts": { + "start": "backstage-cli package start", + "build": "backstage-cli package build", + "lint": "backstage-cli package lint", + "test": "backstage-cli package test", + "clean": "backstage-cli package clean", + "prepack": "backstage-cli package prepack", + "postpack": "backstage-cli package postpack" + }, + "dependencies": { + "@backstage/catalog-model": "^1.0.3", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", + "@backstage/theme": "^0.2.15", + "@material-ui/core": "^4.9.13", + "@material-ui/icons": "^4.9.1", + "@material-ui/lab": "4.0.0-alpha.57", + "cross-fetch": "^3.1.5", + "react-use": "^17.2.4" + }, + "peerDependencies": { + "@backstage/plugin-catalog-react": "^1.1.1-next.1", + "react": "^16.13.1 || ^17.0.0" + }, + "devDependencies": { + "@backstage/cli": "^0.17.2", + "@backstage/core-app-api": "^1.0.3", + "@backstage/dev-utils": "^1.0.3", + "@backstage/test-utils": "^1.1.1", + "@testing-library/jest-dom": "^5.10.1", + "@testing-library/react": "^12.1.3", + "@testing-library/user-event": "^14.0.0", + "@types/jest": "*", + "@types/node": "*", + "cross-fetch": "^3.1.5", + "express": "^4.18.1", + "msw": "^0.42.0" + }, + "files": [ + "dist", + "config.d.ts" + ], + "configSchema": "config.d.ts" +} diff --git a/plugins/dynatrace/src/api/DynatraceApi.ts b/plugins/dynatrace/src/api/DynatraceApi.ts new file mode 100644 index 0000000000..1865b20363 --- /dev/null +++ b/plugins/dynatrace/src/api/DynatraceApi.ts @@ -0,0 +1,50 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { createApiRef } from '@backstage/core-plugin-api'; + +export type DynatraceEntity = { + entityId: { + id: string; + type: string; + }; + name: string; +}; + +export type DynatraceProblem = { + problemId: string; + impactLevel: string; + status: string; + startTime: number; + endTime: number; + title: string; + severityLevel: string; + rootCauseEntity: DynatraceEntity; + affectedEntities: Array; +}; + +export interface DynatraceProblems { + problems: Array; +} + +export const dynatraceApiRef = createApiRef({ + id: 'plugin.dynatrace.service', +}); + +export type DynatraceApi = { + getDynatraceProblems( + dynatraceEntityId: string, + ): Promise; +}; diff --git a/plugins/dynatrace/src/api/DynatraceClient.ts b/plugins/dynatrace/src/api/DynatraceClient.ts new file mode 100644 index 0000000000..27476ddbac --- /dev/null +++ b/plugins/dynatrace/src/api/DynatraceClient.ts @@ -0,0 +1,66 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { DynatraceProblems, DynatraceApi } from './DynatraceApi'; +import { DiscoveryApi, FetchApi } from '@backstage/core-plugin-api'; + +export class DynatraceClient implements DynatraceApi { + discoveryApi: DiscoveryApi; + fetchApi: FetchApi; + + constructor({ + discoveryApi, + fetchApi, + }: { + discoveryApi: DiscoveryApi; + fetchApi: FetchApi; + }) { + this.discoveryApi = discoveryApi; + this.fetchApi = fetchApi; + } + + private async callApi( + path: string, + query: { [key in string]: any }, + ): Promise { + const apiUrl = `${await this.discoveryApi.getBaseUrl('proxy')}/dynatrace`; + const response = await this.fetchApi.fetch( + `${apiUrl}/${path}?${new URLSearchParams(query).toString()}`, + { + headers: { + 'Content-Type': 'application/json', + }, + }, + ); + if (response.status === 200) { + return (await response.json()) as T; + } + throw new Error( + `Dynatrace API call failed: ${response.status}:${response.statusText}`, + ); + } + + async getDynatraceProblems( + dynatraceEntityId: string, + ): Promise { + if (!dynatraceEntityId) { + throw new Error('Dynatrace entity ID is required'); + } + + return this.callApi('problems', { + entitySelector: `entityId(${dynatraceEntityId})`, + }); + } +} diff --git a/plugins/dynatrace/src/api/index.ts b/plugins/dynatrace/src/api/index.ts new file mode 100644 index 0000000000..97e33c9281 --- /dev/null +++ b/plugins/dynatrace/src/api/index.ts @@ -0,0 +1,22 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export type { + DynatraceProblems, + DynatraceProblem, + DynatraceEntity, +} from './DynatraceApi'; +export { dynatraceApiRef } from './DynatraceApi'; +export { DynatraceClient } from './DynatraceClient'; diff --git a/plugins/dynatrace/src/components/DynatraceTab/DynatraceTab.tsx b/plugins/dynatrace/src/components/DynatraceTab/DynatraceTab.tsx new file mode 100644 index 0000000000..c284eaba0f --- /dev/null +++ b/plugins/dynatrace/src/components/DynatraceTab/DynatraceTab.tsx @@ -0,0 +1,56 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React from 'react'; +import { Grid } from '@material-ui/core'; +import { + Page, + Content, + ContentHeader, + SupportButton, + MissingAnnotationEmptyState, +} from '@backstage/core-components'; +import { useEntity } from '@backstage/plugin-catalog-react'; +import { ProblemsList } from '../Problems/ProblemsList'; +import { isDynatraceAvailable } from '../../plugin'; +import { DYNATRACE_ID_ANNOTATION } from '../../constants'; + +export const DynatraceTab = () => { + const { entity } = useEntity(); + + if (!isDynatraceAvailable(entity)) { + return ; + } + + const dynatraceEntityId: string = + entity?.metadata.annotations?.[DYNATRACE_ID_ANNOTATION]!; + + return ( + + + + + Plugin to show information from Dynatrace + + + + + + + + + + ); +}; diff --git a/plugins/dynatrace/src/components/DynatraceTab/index.ts b/plugins/dynatrace/src/components/DynatraceTab/index.ts new file mode 100644 index 0000000000..cd3a1b8fc6 --- /dev/null +++ b/plugins/dynatrace/src/components/DynatraceTab/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export { DynatraceTab } from './DynatraceTab'; diff --git a/plugins/dynatrace/src/components/Problems/ProblemStatus/ProblemStatus.test.tsx b/plugins/dynatrace/src/components/Problems/ProblemStatus/ProblemStatus.test.tsx new file mode 100644 index 0000000000..9ac20ea1a2 --- /dev/null +++ b/plugins/dynatrace/src/components/Problems/ProblemStatus/ProblemStatus.test.tsx @@ -0,0 +1,29 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React from 'react'; +import { ProblemStatus } from './ProblemStatus'; +import { renderInTestApp } from '@backstage/test-utils'; + +describe('ProblemStatus', () => { + it('renders StatusOK for a closed issue', async () => { + const rendered = await renderInTestApp(); + expect(await rendered.findByText('Closed')).toBeInTheDocument(); + }); + it('renders StatusError for an open issue', async () => { + const rendered = await renderInTestApp(); + expect(await rendered.findByText('Open')).toBeInTheDocument(); + }); +}); diff --git a/plugins/dynatrace/src/components/Problems/ProblemStatus/ProblemStatus.tsx b/plugins/dynatrace/src/components/Problems/ProblemStatus/ProblemStatus.tsx new file mode 100644 index 0000000000..2ed861963a --- /dev/null +++ b/plugins/dynatrace/src/components/Problems/ProblemStatus/ProblemStatus.tsx @@ -0,0 +1,39 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React from 'react'; +import { DynatraceProblem } from '../../../api/DynatraceApi'; +import { StatusError, StatusOK } from '@backstage/core-components'; + +export const ProblemStatus = ({ status }: Partial) => { + switch (status?.toLocaleLowerCase()) { + case 'open': + return ( + <> + + Open + + ); + case 'closed': + return ( + <> + + Closed + + ); + default: + return <>; + } +}; diff --git a/plugins/dynatrace/src/components/Problems/ProblemStatus/index.ts b/plugins/dynatrace/src/components/Problems/ProblemStatus/index.ts new file mode 100644 index 0000000000..14124f8d46 --- /dev/null +++ b/plugins/dynatrace/src/components/Problems/ProblemStatus/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export { ProblemStatus } from './ProblemStatus'; diff --git a/plugins/dynatrace/src/components/Problems/ProblemsList/ProblemsList.test.tsx b/plugins/dynatrace/src/components/Problems/ProblemsList/ProblemsList.test.tsx new file mode 100644 index 0000000000..1249f4e886 --- /dev/null +++ b/plugins/dynatrace/src/components/Problems/ProblemsList/ProblemsList.test.tsx @@ -0,0 +1,55 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React from 'react'; +import { ProblemsList } from './ProblemsList'; +import { renderInTestApp, TestApiRegistry } from '@backstage/test-utils'; +import { dynatraceApiRef } from '../../../api'; +import { problems } from '../../../mocks/problems.json'; +import { ApiProvider, ConfigReader } from '@backstage/core-app-api'; +import { configApiRef } from '@backstage/core-plugin-api'; + +const mockDynatraceApi = { + getDynatraceProblems: jest.fn(), +}; +const apis = TestApiRegistry.from( + [dynatraceApiRef, mockDynatraceApi], + [configApiRef, new ConfigReader({ dynatrace: { baseUrl: '__dynatrace__' } })], +); + +describe('ProblemStatus', () => { + it('renders a table with problem data', async () => { + mockDynatraceApi.getDynatraceProblems = jest + .fn() + .mockResolvedValue({ problems }); + const rendered = await renderInTestApp( + + + , + ); + expect(await rendered.findByText('example-service')).toBeInTheDocument(); + }); + it('renders "nothing to report :)" if no problems are found', async () => { + mockDynatraceApi.getDynatraceProblems = jest.fn().mockResolvedValue({}); + const rendered = await renderInTestApp( + + + , + ); + expect( + await rendered.findByText('Nothing to report :)'), + ).toBeInTheDocument(); + }); +}); diff --git a/plugins/dynatrace/src/components/Problems/ProblemsList/ProblemsList.tsx b/plugins/dynatrace/src/components/Problems/ProblemsList/ProblemsList.tsx new file mode 100644 index 0000000000..ac69abf93e --- /dev/null +++ b/plugins/dynatrace/src/components/Problems/ProblemsList/ProblemsList.tsx @@ -0,0 +1,43 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React from 'react'; +import useAsync from 'react-use/lib/useAsync'; +import { Progress } from '@backstage/core-components'; +import Alert from '@material-ui/lab/Alert'; +import { useApi } from '@backstage/core-plugin-api'; +import { ProblemsTable } from '../ProblemsTable'; +import { dynatraceApiRef } from '../../../api'; + +type ProblemsListProps = { + dynatraceEntityId: string; +}; + +export const ProblemsList = (props: ProblemsListProps) => { + const { dynatraceEntityId } = props; + const dynatraceApi = useApi(dynatraceApiRef); + const { value, loading, error } = useAsync(async () => { + return dynatraceApi.getDynatraceProblems(dynatraceEntityId); + }, [dynatraceApi, dynatraceEntityId]); + const problems = value?.problems; + + if (loading) { + return ; + } else if (error) { + return {error.message}; + } + if (!problems) return
Nothing to report :)
; + return ; +}; diff --git a/plugins/dynatrace/src/components/Problems/ProblemsList/index.ts b/plugins/dynatrace/src/components/Problems/ProblemsList/index.ts new file mode 100644 index 0000000000..f70c9c6f20 --- /dev/null +++ b/plugins/dynatrace/src/components/Problems/ProblemsList/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export { ProblemsList } from './ProblemsList'; diff --git a/plugins/dynatrace/src/components/Problems/ProblemsTable/ProblemsTable.test.tsx b/plugins/dynatrace/src/components/Problems/ProblemsTable/ProblemsTable.test.tsx new file mode 100644 index 0000000000..e4adc43656 --- /dev/null +++ b/plugins/dynatrace/src/components/Problems/ProblemsTable/ProblemsTable.test.tsx @@ -0,0 +1,44 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React from 'react'; +import { ProblemsTable } from './ProblemsTable'; +import { renderInTestApp, TestApiRegistry } from '@backstage/test-utils'; +import { problems } from '../../../mocks/problems.json'; +import { ApiProvider, ConfigReader } from '@backstage/core-app-api'; +import { configApiRef } from '@backstage/core-plugin-api'; + +describe('ProblemsTable', () => { + const apis = TestApiRegistry.from([ + configApiRef, + new ConfigReader({ dynatrace: { baseUrl: '__dynatrace__' } }), + ]); + it('renders the table with some problem data', async () => { + const rendered = await renderInTestApp( + + , + , + ); + expect(await rendered.findByText('example-service')).toBeInTheDocument(); + }); + it('renders an empty table when no data is provided', async () => { + const rendered = await renderInTestApp( + + + , + ); + expect(await rendered.findByText('Problems')).toBeInTheDocument(); + }); +}); diff --git a/plugins/dynatrace/src/components/Problems/ProblemsTable/ProblemsTable.tsx b/plugins/dynatrace/src/components/Problems/ProblemsTable/ProblemsTable.tsx new file mode 100644 index 0000000000..8e3d330c37 --- /dev/null +++ b/plugins/dynatrace/src/components/Problems/ProblemsTable/ProblemsTable.tsx @@ -0,0 +1,86 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React from 'react'; +import { Table, TableColumn } from '@backstage/core-components'; +import { DynatraceProblem } from '../../../api/DynatraceApi'; +import { ProblemStatus } from '../ProblemStatus'; +import { configApiRef } from '@backstage/core-plugin-api'; +import { useApi } from '@backstage/core-plugin-api'; +import { Link } from '@material-ui/core'; + +type ProblemsTableProps = { + problems: DynatraceProblem[]; +}; + +export const ProblemsTable = ({ problems }: ProblemsTableProps) => { + const configApi = useApi(configApiRef); + const dynatraceBaseUrl = configApi.getString('dynatrace.baseUrl'); + const columns: TableColumn[] = [ + { + title: 'Title', + field: 'title', + render: (row: Partial) => ( + + {row.title} + + ), + }, + { + title: 'Status', + field: 'status', + render: (row: Partial) => ( + + ), + }, + { title: 'Severity', field: 'severityLevel' }, + { + title: 'Root Cause', + field: 'rootCauseEntity', + render: (row: Partial) => row.rootCauseEntity?.name, + }, + { + title: 'Affected', + field: 'affectedEntities', + render: (row: Partial) => + row.affectedEntities?.map(e => e.name), + }, + { + title: 'Start Time', + field: 'startTime', + render: (row: Partial) => + new Date(row.startTime || 0).toString(), + }, + { + title: 'End Time', + field: 'endTime', + render: (row: Partial) => + row.endTime === -1 ? 'ongoing' : new Date(row.endTime || 0).toString(), + }, + ]; + + return ( +
{ + return { ...p, id: p.problemId }; + })} + /> + ); +}; diff --git a/plugins/dynatrace/src/components/Problems/ProblemsTable/index.ts b/plugins/dynatrace/src/components/Problems/ProblemsTable/index.ts new file mode 100644 index 0000000000..d1cd52e511 --- /dev/null +++ b/plugins/dynatrace/src/components/Problems/ProblemsTable/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export { ProblemsTable } from './ProblemsTable'; diff --git a/plugins/dynatrace/src/constants.ts b/plugins/dynatrace/src/constants.ts new file mode 100644 index 0000000000..21453e0338 --- /dev/null +++ b/plugins/dynatrace/src/constants.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export const DYNATRACE_ID_ANNOTATION = 'dynatrace.com/dynatrace-entity-id'; diff --git a/plugins/dynatrace/src/index.ts b/plugins/dynatrace/src/index.ts new file mode 100644 index 0000000000..c96c16201d --- /dev/null +++ b/plugins/dynatrace/src/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export { dynatracePlugin, DynatraceTab, isDynatraceAvailable } from './plugin'; diff --git a/plugins/dynatrace/src/mocks/problems.json b/plugins/dynatrace/src/mocks/problems.json new file mode 100644 index 0000000000..b1894bef56 --- /dev/null +++ b/plugins/dynatrace/src/mocks/problems.json @@ -0,0 +1,135 @@ +{ + "totalCount": 0, + "pageSize": 0, + "nextPageKey": "AQAAABQBAAAABQ==", + "problems": [ + { + "managementZones": [ + { + "name": "string", + "id": "string" + } + ], + "severityLevel": "AVAILABILITY", + "entityTags": [ + { + "stringRepresentation": "string", + "value": "string", + "key": "string", + "context": "string" + } + ], + "problemId": "string", + "displayId": "string", + "affectedEntities": [ + { + "entityId": { + "id": "string", + "type": "string" + }, + "name": "my-service" + }, + { + "entityId": { + "id": "string", + "type": "string" + }, + "name": "example-service-3" + } + ], + "rootCauseEntity": { + "entityId": { + "id": "string", + "type": "string" + }, + "name": "example-service" + }, + "impactedEntities": [ + { + "entityId": { + "id": "string", + "type": "string" + }, + "name": "HELP ME" + }, + { + "entityId": { + "id": "string", + "type": "string" + }, + "name": "HELP ME 2" + } + ], + "linkedProblemInfo": { + "problemId": "string", + "displayId": "string" + }, + "problemFilters": [ + { + "name": "string", + "id": "string" + } + ], + "evidenceDetails": { + "totalCount": 0, + "details": [ + { + "evidenceType": "AVAILABILITY_EVIDENCE", + "displayName": "string", + "entity": { + "entityId": { + "id": "string", + "type": "string" + }, + "name": "string" + }, + "groupingEntity": { + "entityId": { + "id": "string", + "type": "string" + }, + "name": "string" + }, + "rootCauseRelevant": true, + "startTime": 0 + } + ] + }, + "recentComments": { + "comments": [ + { + "createdAtTimestamp": 0, + "authorName": "string", + "context": "string", + "id": "string", + "content": "string" + } + ], + "totalCount": 0, + "pageSize": 0, + "nextPageKey": "AQAAABQBAAAABQ==" + }, + "impactAnalysis": { + "impacts": [ + { + "impactType": "APPLICATION", + "impactedEntity": { + "entityId": { + "id": "string", + "type": "string" + }, + "name": "string" + }, + "estimatedAffectedUsers": 0 + } + ] + }, + "impactLevel": "APPLICATION", + "status": "OPEN", + "startTime": 0, + "endTime": 0, + "title": "this IS a big problem" + } + ], + "warnings": ["string"] +} diff --git a/plugins/dynatrace/src/plugin.test.ts b/plugins/dynatrace/src/plugin.test.ts new file mode 100644 index 0000000000..94dc2da765 --- /dev/null +++ b/plugins/dynatrace/src/plugin.test.ts @@ -0,0 +1,22 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { dynatracePlugin } from './plugin'; + +describe('dynatrace', () => { + it('should export plugin', () => { + expect(dynatracePlugin).toBeDefined(); + }); +}); diff --git a/plugins/dynatrace/src/plugin.ts b/plugins/dynatrace/src/plugin.ts new file mode 100644 index 0000000000..fc1778b45c --- /dev/null +++ b/plugins/dynatrace/src/plugin.ts @@ -0,0 +1,71 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { dynatraceApiRef, DynatraceClient } from './api'; +import { + createApiFactory, + createPlugin, + discoveryApiRef, + fetchApiRef, + createRoutableExtension, +} from '@backstage/core-plugin-api'; + +import { Entity } from '@backstage/catalog-model'; +import { DYNATRACE_ID_ANNOTATION } from './constants'; + +import { rootRouteRef } from './routes'; + +/** + * Create the Dynatrace plugin. + * @public + */ +export const dynatracePlugin = createPlugin({ + id: 'dynatrace', + apis: [ + createApiFactory({ + api: dynatraceApiRef, + deps: { + discoveryApi: discoveryApiRef, + fetchApi: fetchApiRef, + }, + factory: ({ discoveryApi, fetchApi }) => + new DynatraceClient({ + discoveryApi, + fetchApi, + }), + }), + ], +}); + +/** + * Checks if the entity has a dynatrace id annotation. + * @public + * @param entity {Entity} - The entity to check for the dynatrace id annotation. + */ +export const isDynatraceAvailable = (entity: Entity) => + Boolean(entity.metadata.annotations?.[DYNATRACE_ID_ANNOTATION]); + +/** + * Creates a routable extension for the dynatrace plugin tab. + * @public + */ +export const DynatraceTab = dynatracePlugin.provide( + createRoutableExtension({ + name: 'DynatraceTab', + component: () => + import('./components/DynatraceTab').then(m => m.DynatraceTab), + mountPoint: rootRouteRef, + }), +); diff --git a/plugins/dynatrace/src/routes.ts b/plugins/dynatrace/src/routes.ts new file mode 100644 index 0000000000..73f7b573b7 --- /dev/null +++ b/plugins/dynatrace/src/routes.ts @@ -0,0 +1,20 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { createRouteRef } from '@backstage/core-plugin-api'; + +export const rootRouteRef = createRouteRef({ + id: 'dynatrace', +}); diff --git a/plugins/dynatrace/src/setupTests.ts b/plugins/dynatrace/src/setupTests.ts new file mode 100644 index 0000000000..9bb3e72355 --- /dev/null +++ b/plugins/dynatrace/src/setupTests.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import '@testing-library/jest-dom'; +import 'cross-fetch/polyfill'; diff --git a/plugins/example-todo-list-backend/CHANGELOG.md b/plugins/example-todo-list-backend/CHANGELOG.md index f5bb0542a3..46a980fbe3 100644 --- a/plugins/example-todo-list-backend/CHANGELOG.md +++ b/plugins/example-todo-list-backend/CHANGELOG.md @@ -1,5 +1,29 @@ # @internal/plugin-todo-list-backend +## 1.0.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0 + - @backstage/plugin-auth-node@0.2.2 + +## 1.0.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0-next.2 + - @backstage/plugin-auth-node@0.2.2-next.2 + +## 1.0.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + - @backstage/plugin-auth-node@0.2.2-next.0 + ## 1.0.1 ### Patch Changes diff --git a/plugins/example-todo-list-backend/README.md b/plugins/example-todo-list-backend/README.md index d5c51b2b57..f1dbd427d2 100644 --- a/plugins/example-todo-list-backend/README.md +++ b/plugins/example-todo-list-backend/README.md @@ -1,3 +1,3 @@ # todo-list-backend -This package provides a starting point to demonstrate how plugin authors can use the Backstage permission framework. Refer to the [documentation](https://backstage.io/docs/permission/plugin-authors/01-setup) to get started. +This package provides a starting point to demonstrate how plugin authors can use the Backstage permission framework. Refer to the [documentation](https://backstage.io/docs/permissions/plugin-authors/01-setup) to get started. diff --git a/plugins/example-todo-list-backend/package.json b/plugins/example-todo-list-backend/package.json index 78600526ad..7aaed0532b 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.1", + "version": "1.0.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,10 +20,10 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/backend-common": "^0.13.3", + "@backstage/backend-common": "^0.14.0", "@backstage/config": "^1.0.1", "@backstage/errors": "^1.0.0", - "@backstage/plugin-auth-node": "^0.2.1", + "@backstage/plugin-auth-node": "^0.2.2", "@types/express": "^4.17.6", "cross-fetch": "^3.1.5", "express": "^4.17.1", @@ -33,10 +33,10 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", + "@backstage/cli": "^0.17.2", "@types/supertest": "^2.0.8", "@types/uuid": "^8.0.0", - "msw": "^0.35.0", + "msw": "^0.42.0", "supertest": "^6.1.6" }, "files": [ diff --git a/plugins/example-todo-list-common/CHANGELOG.md b/plugins/example-todo-list-common/CHANGELOG.md index dab24680d0..564b1f57eb 100644 --- a/plugins/example-todo-list-common/CHANGELOG.md +++ b/plugins/example-todo-list-common/CHANGELOG.md @@ -1,5 +1,19 @@ # @internal/plugin-todo-list-common +## 1.0.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-common@0.6.2 + +## 1.0.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-common@0.6.2-next.0 + ## 1.0.1 ### Patch Changes diff --git a/plugins/example-todo-list-common/README.md b/plugins/example-todo-list-common/README.md index 021eca1033..4962d61bd0 100644 --- a/plugins/example-todo-list-common/README.md +++ b/plugins/example-todo-list-common/README.md @@ -1,3 +1,3 @@ # todo-list-common -This package provides a starting point to demonstrate how plugin authors can use the Backstage permission framework. Refer to the [documentation](https://backstage.io/docs/permission/plugin-authors/01-setup) to get started. +This package provides a starting point to demonstrate how plugin authors can use the Backstage permission framework. Refer to the [documentation](https://backstage.io/docs/permissions/plugin-authors/01-setup) to get started. diff --git a/plugins/example-todo-list-common/package.json b/plugins/example-todo-list-common/package.json index 8ab1270372..596a37b210 100644 --- a/plugins/example-todo-list-common/package.json +++ b/plugins/example-todo-list-common/package.json @@ -1,6 +1,6 @@ { "name": "@internal/plugin-todo-list-common", - "version": "1.0.1", + "version": "1.0.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -23,15 +23,15 @@ "postpack": "backstage-cli package postpack" }, "dependencies": { - "@backstage/plugin-permission-common": "^0.6.1" + "@backstage/plugin-permission-common": "^0.6.2" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/core-app-api": "^1.0.2", - "@backstage/dev-utils": "^1.0.2", - "@backstage/test-utils": "^1.1.0", + "@backstage/cli": "^0.17.2", + "@backstage/core-app-api": "^1.0.3", + "@backstage/dev-utils": "^1.0.3", + "@backstage/test-utils": "^1.1.1", "@types/node": "^16.11.26", - "msw": "^0.35.0", + "msw": "^0.42.0", "cross-fetch": "^3.1.5" }, "files": [ diff --git a/plugins/example-todo-list/CHANGELOG.md b/plugins/example-todo-list/CHANGELOG.md index 8772497922..31404fa73d 100644 --- a/plugins/example-todo-list/CHANGELOG.md +++ b/plugins/example-todo-list/CHANGELOG.md @@ -1,5 +1,28 @@ # @internal/plugin-todo-list +## 1.0.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + +## 1.0.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + +## 1.0.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.9.5-next.0 + ## 1.0.1 ### Patch Changes diff --git a/plugins/example-todo-list/README.md b/plugins/example-todo-list/README.md index bd77af87e8..8d5daab89b 100644 --- a/plugins/example-todo-list/README.md +++ b/plugins/example-todo-list/README.md @@ -1,3 +1,3 @@ # todo-list -This package provides a starting point to demonstrate how plugin authors can use the Backstage permission framework. Refer to the [documentation](https://backstage.io/docs/permission/plugin-authors/01-setup) to get started. +This package provides a starting point to demonstrate how plugin authors can use the Backstage permission framework. Refer to the [documentation](https://backstage.io/docs/permissions/plugin-authors/01-setup) to get started. diff --git a/plugins/example-todo-list/package.json b/plugins/example-todo-list/package.json index 416514f550..c80b9a48ea 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.1", + "version": "1.0.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,8 +21,8 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/core-components": "^0.9.4", - "@backstage/core-plugin-api": "^1.0.2", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", "@backstage/theme": "^0.2.15", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -33,16 +33,16 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/core-app-api": "^1.0.2", - "@backstage/dev-utils": "^1.0.2", - "@backstage/test-utils": "^1.1.0", + "@backstage/cli": "^0.17.2", + "@backstage/core-app-api": "^1.0.3", + "@backstage/dev-utils": "^1.0.3", + "@backstage/test-utils": "^1.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", "@types/jest": "^26.0.7", "@types/node": "^16.11.26", - "msw": "^0.35.0" + "msw": "^0.42.0" }, "files": [ "dist" diff --git a/plugins/explore-react/CHANGELOG.md b/plugins/explore-react/CHANGELOG.md index 9c5b38fd5d..f42c1dc9fc 100644 --- a/plugins/explore-react/CHANGELOG.md +++ b/plugins/explore-react/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-explore-react +## 0.0.18 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-plugin-api@1.0.3 + +## 0.0.18-next.0 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-plugin-api@1.0.3-next.0 + ## 0.0.17 ### Patch Changes diff --git a/plugins/explore-react/package.json b/plugins/explore-react/package.json index a563b66a23..00ec76258d 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.17", + "version": "0.0.18", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -33,19 +33,19 @@ "start": "backstage-cli package start" }, "dependencies": { - "@backstage/core-plugin-api": "^1.0.2" + "@backstage/core-plugin-api": "^1.0.3" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/dev-utils": "^1.0.2", - "@backstage/test-utils": "^1.1.0", + "@backstage/cli": "^0.17.2", + "@backstage/dev-utils": "^1.0.3", + "@backstage/test-utils": "^1.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", "@types/jest": "^26.0.7", "@types/node": "^16.11.26", "cross-fetch": "^3.1.5", - "msw": "^0.35.0" + "msw": "^0.42.0" }, "files": [ "dist" diff --git a/plugins/explore/CHANGELOG.md b/plugins/explore/CHANGELOG.md index 37eb6e4a39..712e3203fb 100644 --- a/plugins/explore/CHANGELOG.md +++ b/plugins/explore/CHANGELOG.md @@ -1,5 +1,37 @@ # @backstage/plugin-explore +## 0.3.37 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/plugin-explore-react@0.0.18 + - @backstage/catalog-model@1.0.3 + +## 0.3.37-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/plugin-explore-react@0.0.18-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## 0.3.37-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + ## 0.3.36 ### Patch Changes diff --git a/plugins/explore/package.json b/plugins/explore/package.json index e0b2704145..201be77085 100644 --- a/plugins/explore/package.json +++ b/plugins/explore/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-explore", "description": "A Backstage plugin for building an exploration page of your software ecosystem", - "version": "0.3.36", + "version": "0.3.37", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -34,11 +34,11 @@ "start": "backstage-cli package start" }, "dependencies": { - "@backstage/catalog-model": "^1.0.2", - "@backstage/core-components": "^0.9.4", - "@backstage/core-plugin-api": "^1.0.2", - "@backstage/plugin-catalog-react": "^1.1.0", - "@backstage/plugin-explore-react": "^0.0.17", + "@backstage/catalog-model": "^1.0.3", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", + "@backstage/plugin-catalog-react": "^1.1.1", + "@backstage/plugin-explore-react": "^0.0.18", "@backstage/theme": "^0.2.15", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -53,17 +53,17 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/core-app-api": "^1.0.2", - "@backstage/dev-utils": "^1.0.2", - "@backstage/test-utils": "^1.1.0", + "@backstage/cli": "^0.17.2", + "@backstage/core-app-api": "^1.0.3", + "@backstage/dev-utils": "^1.0.3", + "@backstage/test-utils": "^1.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", "@types/jest": "^26.0.7", "@types/node": "^16.11.26", "cross-fetch": "^3.1.5", - "msw": "^0.35.0" + "msw": "^0.42.0" }, "files": [ "dist" diff --git a/plugins/firehydrant/CHANGELOG.md b/plugins/firehydrant/CHANGELOG.md index fcfe424e76..065d6e0bd5 100644 --- a/plugins/firehydrant/CHANGELOG.md +++ b/plugins/firehydrant/CHANGELOG.md @@ -1,5 +1,33 @@ # @backstage/plugin-firehydrant +## 0.1.23 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + +## 0.1.23-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## 0.1.23-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + ## 0.1.22 ### Patch Changes diff --git a/plugins/firehydrant/package.json b/plugins/firehydrant/package.json index 9f8cb184e0..a86a122fd0 100644 --- a/plugins/firehydrant/package.json +++ b/plugins/firehydrant/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-firehydrant", "description": "A Backstage plugin that integrates towards FireHydrant", - "version": "0.1.22", + "version": "0.1.23", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -25,9 +25,9 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/core-components": "^0.9.4", - "@backstage/core-plugin-api": "^1.0.2", - "@backstage/plugin-catalog-react": "^1.1.0", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", + "@backstage/plugin-catalog-react": "^1.1.1", "@backstage/theme": "^0.2.15", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -39,17 +39,17 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/core-app-api": "^1.0.2", - "@backstage/dev-utils": "^1.0.2", - "@backstage/test-utils": "^1.1.0", + "@backstage/cli": "^0.17.2", + "@backstage/core-app-api": "^1.0.3", + "@backstage/dev-utils": "^1.0.3", + "@backstage/test-utils": "^1.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", "@types/jest": "^26.0.7", "@types/node": "^16.11.26", "cross-fetch": "^3.1.5", - "msw": "^0.35.0" + "msw": "^0.42.0" }, "files": [ "dist" diff --git a/plugins/fossa/CHANGELOG.md b/plugins/fossa/CHANGELOG.md index 9218c857c1..07880deb60 100644 --- a/plugins/fossa/CHANGELOG.md +++ b/plugins/fossa/CHANGELOG.md @@ -1,5 +1,35 @@ # @backstage/plugin-fossa +## 0.2.38 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## 0.2.38-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## 0.2.38-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + ## 0.2.37 ### Patch Changes diff --git a/plugins/fossa/package.json b/plugins/fossa/package.json index 156c5b4577..e97dc7c0f9 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.37", + "version": "0.2.38", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -35,11 +35,11 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-model": "^1.0.2", - "@backstage/core-components": "^0.9.4", - "@backstage/core-plugin-api": "^1.0.2", + "@backstage/catalog-model": "^1.0.3", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", "@backstage/errors": "^1.0.0", - "@backstage/plugin-catalog-react": "^1.1.0", + "@backstage/plugin-catalog-react": "^1.1.1", "@backstage/theme": "^0.2.15", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -53,16 +53,16 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/core-app-api": "^1.0.2", - "@backstage/dev-utils": "^1.0.2", - "@backstage/test-utils": "^1.1.0", + "@backstage/cli": "^0.17.2", + "@backstage/core-app-api": "^1.0.3", + "@backstage/dev-utils": "^1.0.3", + "@backstage/test-utils": "^1.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", "@types/jest": "^26.0.7", "@types/node": "^16.11.26", - "msw": "^0.35.0" + "msw": "^0.42.0" }, "files": [ "dist", diff --git a/plugins/gcalendar/CHANGELOG.md b/plugins/gcalendar/CHANGELOG.md index c8cdc093ee..c5d63a19db 100644 --- a/plugins/gcalendar/CHANGELOG.md +++ b/plugins/gcalendar/CHANGELOG.md @@ -1,5 +1,30 @@ # @backstage/plugin-gcalendar +## 0.3.2 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + +## 0.3.2-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + +## 0.3.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.9.5-next.0 + ## 0.3.1 ### Patch Changes diff --git a/plugins/gcalendar/package.json b/plugins/gcalendar/package.json index 0dd60a4cee..b74129b8bc 100644 --- a/plugins/gcalendar/package.json +++ b/plugins/gcalendar/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-gcalendar", - "version": "0.3.1", + "version": "0.3.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -22,8 +22,8 @@ "postpack": "backstage-cli package postpack" }, "dependencies": { - "@backstage/core-components": "^0.9.4", - "@backstage/core-plugin-api": "^1.0.2", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", "@backstage/errors": "^1.0.0", "@backstage/theme": "^0.2.15", "@material-ui/core": "^4.12.2", @@ -43,10 +43,10 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/core-app-api": "^1.0.2", - "@backstage/dev-utils": "^1.0.2", - "@backstage/test-utils": "^1.1.0", + "@backstage/cli": "^0.17.2", + "@backstage/core-app-api": "^1.0.3", + "@backstage/dev-utils": "^1.0.3", + "@backstage/test-utils": "^1.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", @@ -55,7 +55,7 @@ "@types/node": "^16.11.26", "@types/sanitize-html": "^2.6.2", "cross-fetch": "^3.1.5", - "msw": "^0.35.0" + "msw": "^0.42.0" }, "files": [ "dist" diff --git a/plugins/gcp-projects/CHANGELOG.md b/plugins/gcp-projects/CHANGELOG.md index 702e62b010..6c95f8ea4d 100644 --- a/plugins/gcp-projects/CHANGELOG.md +++ b/plugins/gcp-projects/CHANGELOG.md @@ -1,5 +1,32 @@ # @backstage/plugin-gcp-projects +## 0.3.25 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- 6968b65ba1: Updated dependency `@react-hookz/web` to `^14.0.0`. +- Updated dependencies + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + +## 0.3.25-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + +## 0.3.25-next.0 + +### Patch Changes + +- 6968b65ba1: Updated dependency `@react-hookz/web` to `^14.0.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.0 + ## 0.3.24 ### Patch Changes diff --git a/plugins/gcp-projects/package.json b/plugins/gcp-projects/package.json index 844c8e3fb3..0434e6b25a 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.24", + "version": "0.3.25", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -34,8 +34,8 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/core-components": "^0.9.4", - "@backstage/core-plugin-api": "^1.0.2", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", "@backstage/theme": "^0.2.15", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -47,17 +47,17 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/core-app-api": "^1.0.2", - "@backstage/dev-utils": "^1.0.2", - "@backstage/test-utils": "^1.1.0", + "@backstage/cli": "^0.17.2", + "@backstage/core-app-api": "^1.0.3", + "@backstage/dev-utils": "^1.0.3", + "@backstage/test-utils": "^1.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", "@types/jest": "^26.0.7", "@types/node": "^16.11.26", "cross-fetch": "^3.1.5", - "msw": "^0.35.0" + "msw": "^0.42.0" }, "files": [ "dist" diff --git a/plugins/git-release-manager/CHANGELOG.md b/plugins/git-release-manager/CHANGELOG.md index 2e39c9511a..7b3a0bc993 100644 --- a/plugins/git-release-manager/CHANGELOG.md +++ b/plugins/git-release-manager/CHANGELOG.md @@ -1,5 +1,33 @@ # @backstage/plugin-git-release-manager +## 0.3.19 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5 + - @backstage/integration@1.2.1 + - @backstage/core-plugin-api@1.0.3 + +## 0.3.19-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/integration@1.2.1-next.1 + +## 0.3.19-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.9.5-next.0 + - @backstage/integration@1.2.1-next.0 + ## 0.3.18 ### Patch Changes diff --git a/plugins/git-release-manager/package.json b/plugins/git-release-manager/package.json index 85615c1b60..9ae263b882 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.18", + "version": "0.3.19", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -24,9 +24,9 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/core-components": "^0.9.4", - "@backstage/core-plugin-api": "^1.0.2", - "@backstage/integration": "^1.2.0", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", + "@backstage/integration": "^1.2.1", "@backstage/theme": "^0.2.15", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -43,10 +43,10 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/core-app-api": "^1.0.2", - "@backstage/dev-utils": "^1.0.2", - "@backstage/test-utils": "^1.1.0", + "@backstage/cli": "^0.17.2", + "@backstage/core-app-api": "^1.0.3", + "@backstage/dev-utils": "^1.0.3", + "@backstage/test-utils": "^1.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/react-hooks": "^8.0.0", @@ -55,7 +55,7 @@ "@types/node": "^16.11.26", "@types/recharts": "^1.8.15", "cross-fetch": "^3.1.5", - "msw": "^0.35.0" + "msw": "^0.42.0" }, "files": [ "dist" diff --git a/plugins/github-actions/CHANGELOG.md b/plugins/github-actions/CHANGELOG.md index 6a75b05d3a..23e66cc1f6 100644 --- a/plugins/github-actions/CHANGELOG.md +++ b/plugins/github-actions/CHANGELOG.md @@ -1,5 +1,38 @@ # @backstage/plugin-github-actions +## 0.5.6 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/integration@1.2.1 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## 0.5.6-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/integration@1.2.1-next.1 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## 0.5.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + - @backstage/integration@1.2.1-next.0 + ## 0.5.5 ### Patch Changes diff --git a/plugins/github-actions/api-report.md b/plugins/github-actions/api-report.md index ee3e1d679d..08761bad56 100644 --- a/plugins/github-actions/api-report.md +++ b/plugins/github-actions/api-report.md @@ -71,18 +71,9 @@ export const EntityRecentGithubActionsRunsCard: ({ // @public (undocumented) export const GITHUB_ACTIONS_ANNOTATION = 'github.com/project-slug'; -// Warning: (ae-missing-release-tag) "GithubActionsApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export type GithubActionsApi = { - listWorkflowRuns: ({ - hostname, - owner, - repo, - pageSize, - page, - branch, - }: { + listWorkflowRuns: (options: { hostname?: string; owner: string; repo: string; @@ -92,12 +83,7 @@ export type GithubActionsApi = { }) => Promise< RestEndpointMethodTypes['actions']['listWorkflowRuns']['response']['data'] >; - getWorkflow: ({ - hostname, - owner, - repo, - id, - }: { + getWorkflow: (options: { hostname?: string; owner: string; repo: string; @@ -105,12 +91,7 @@ export type GithubActionsApi = { }) => Promise< RestEndpointMethodTypes['actions']['getWorkflow']['response']['data'] >; - getWorkflowRun: ({ - hostname, - owner, - repo, - id, - }: { + getWorkflowRun: (options: { hostname?: string; owner: string; repo: string; @@ -118,25 +99,13 @@ export type GithubActionsApi = { }) => Promise< RestEndpointMethodTypes['actions']['getWorkflowRun']['response']['data'] >; - reRunWorkflow: ({ - hostname, - owner, - repo, - runId, - }: { + reRunWorkflow: (options: { hostname?: string; owner: string; repo: string; runId: number; }) => Promise; - listJobsForWorkflowRun: ({ - hostname, - owner, - repo, - id, - pageSize, - page, - }: { + listJobsForWorkflowRun: (options: { hostname?: string; owner: string; repo: string; @@ -146,12 +115,7 @@ export type GithubActionsApi = { }) => Promise< RestEndpointMethodTypes['actions']['listJobsForWorkflowRun']['response']['data'] >; - downloadJobLogsForWorkflowRun: ({ - hostname, - owner, - repo, - runId, - }: { + downloadJobLogsForWorkflowRun: (options: { hostname?: string; owner: string; repo: string; @@ -166,18 +130,11 @@ export type GithubActionsApi = { // @public (undocumented) export const githubActionsApiRef: ApiRef; -// Warning: (ae-missing-release-tag) "GithubActionsClient" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export class GithubActionsClient implements GithubActionsApi { constructor(options: { configApi: ConfigApi; githubAuthApi: OAuthApi }); // (undocumented) - downloadJobLogsForWorkflowRun({ - hostname, - owner, - repo, - runId, - }: { + downloadJobLogsForWorkflowRun(options: { hostname?: string; owner: string; repo: string; @@ -186,12 +143,7 @@ export class GithubActionsClient implements GithubActionsApi { RestEndpointMethodTypes['actions']['downloadJobLogsForWorkflowRun']['response']['data'] >; // (undocumented) - getWorkflow({ - hostname, - owner, - repo, - id, - }: { + getWorkflow(options: { hostname?: string; owner: string; repo: string; @@ -200,12 +152,7 @@ export class GithubActionsClient implements GithubActionsApi { RestEndpointMethodTypes['actions']['getWorkflow']['response']['data'] >; // (undocumented) - getWorkflowRun({ - hostname, - owner, - repo, - id, - }: { + getWorkflowRun(options: { hostname?: string; owner: string; repo: string; @@ -214,14 +161,7 @@ export class GithubActionsClient implements GithubActionsApi { RestEndpointMethodTypes['actions']['getWorkflowRun']['response']['data'] >; // (undocumented) - listJobsForWorkflowRun({ - hostname, - owner, - repo, - id, - pageSize, - page, - }: { + listJobsForWorkflowRun(options: { hostname?: string; owner: string; repo: string; @@ -232,14 +172,7 @@ export class GithubActionsClient implements GithubActionsApi { RestEndpointMethodTypes['actions']['listJobsForWorkflowRun']['response']['data'] >; // (undocumented) - listWorkflowRuns({ - hostname, - owner, - repo, - pageSize, - page, - branch, - }: { + listWorkflowRuns(options: { hostname?: string; owner: string; repo: string; @@ -250,12 +183,7 @@ export class GithubActionsClient implements GithubActionsApi { RestEndpointMethodTypes['actions']['listWorkflowRuns']['response']['data'] >; // (undocumented) - reRunWorkflow({ - hostname, - owner, - repo, - runId, - }: { + reRunWorkflow(options: { hostname?: string; owner: string; repo: string; diff --git a/plugins/github-actions/package.json b/plugins/github-actions/package.json index 0bf5e604b4..f843755e9b 100644 --- a/plugins/github-actions/package.json +++ b/plugins/github-actions/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-github-actions", "description": "A Backstage plugin that integrates towards GitHub Actions", - "version": "0.5.5", + "version": "0.5.6", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -36,11 +36,11 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-model": "^1.0.2", - "@backstage/core-components": "^0.9.4", - "@backstage/core-plugin-api": "^1.0.2", - "@backstage/integration": "^1.2.0", - "@backstage/plugin-catalog-react": "^1.1.0", + "@backstage/catalog-model": "^1.0.3", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", + "@backstage/integration": "^1.2.1", + "@backstage/plugin-catalog-react": "^1.1.1", "@backstage/theme": "^0.2.15", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -55,17 +55,17 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/core-app-api": "^1.0.2", - "@backstage/dev-utils": "^1.0.2", - "@backstage/test-utils": "^1.1.0", + "@backstage/cli": "^0.17.2", + "@backstage/core-app-api": "^1.0.3", + "@backstage/dev-utils": "^1.0.3", + "@backstage/test-utils": "^1.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", "@types/jest": "^26.0.7", "@types/node": "^16.11.26", "cross-fetch": "^3.1.5", - "msw": "^0.35.0" + "msw": "^0.42.0" }, "files": [ "dist" diff --git a/plugins/github-actions/src/api/GithubActionsApi.ts b/plugins/github-actions/src/api/GithubActionsApi.ts index fe516a0211..25bbf0b809 100644 --- a/plugins/github-actions/src/api/GithubActionsApi.ts +++ b/plugins/github-actions/src/api/GithubActionsApi.ts @@ -21,15 +21,13 @@ export const githubActionsApiRef = createApiRef({ id: 'plugin.githubactions.service', }); +/** + * A client for fetching information about GitHub actions. + * + * @public + */ export type GithubActionsApi = { - listWorkflowRuns: ({ - hostname, - owner, - repo, - pageSize, - page, - branch, - }: { + listWorkflowRuns: (options: { hostname?: string; owner: string; repo: string; @@ -39,12 +37,8 @@ export type GithubActionsApi = { }) => Promise< RestEndpointMethodTypes['actions']['listWorkflowRuns']['response']['data'] >; - getWorkflow: ({ - hostname, - owner, - repo, - id, - }: { + + getWorkflow: (options: { hostname?: string; owner: string; repo: string; @@ -52,12 +46,8 @@ export type GithubActionsApi = { }) => Promise< RestEndpointMethodTypes['actions']['getWorkflow']['response']['data'] >; - getWorkflowRun: ({ - hostname, - owner, - repo, - id, - }: { + + getWorkflowRun: (options: { hostname?: string; owner: string; repo: string; @@ -65,25 +55,15 @@ export type GithubActionsApi = { }) => Promise< RestEndpointMethodTypes['actions']['getWorkflowRun']['response']['data'] >; - reRunWorkflow: ({ - hostname, - owner, - repo, - runId, - }: { + + reRunWorkflow: (options: { hostname?: string; owner: string; repo: string; runId: number; }) => Promise; - listJobsForWorkflowRun: ({ - hostname, - owner, - repo, - id, - pageSize, - page, - }: { + + listJobsForWorkflowRun: (options: { hostname?: string; owner: string; repo: string; @@ -93,12 +73,8 @@ export type GithubActionsApi = { }) => Promise< RestEndpointMethodTypes['actions']['listJobsForWorkflowRun']['response']['data'] >; - downloadJobLogsForWorkflowRun: ({ - hostname, - owner, - repo, - runId, - }: { + + downloadJobLogsForWorkflowRun: (options: { hostname?: string; owner: string; repo: string; diff --git a/plugins/github-actions/src/api/GithubActionsClient.ts b/plugins/github-actions/src/api/GithubActionsClient.ts index 97ee1860a1..ebe3d92c4f 100644 --- a/plugins/github-actions/src/api/GithubActionsClient.ts +++ b/plugins/github-actions/src/api/GithubActionsClient.ts @@ -19,6 +19,11 @@ import { GithubActionsApi } from './GithubActionsApi'; import { Octokit, RestEndpointMethodTypes } from '@octokit/rest'; import { ConfigApi, OAuthApi } from '@backstage/core-plugin-api'; +/** + * A client for fetching information about GitHub actions. + * + * @public + */ export class GithubActionsClient implements GithubActionsApi { private readonly configApi: ConfigApi; private readonly githubAuthApi: OAuthApi; @@ -41,17 +46,14 @@ export class GithubActionsClient implements GithubActionsApi { return new Octokit({ auth: token, baseUrl }); } - async reRunWorkflow({ - hostname, - owner, - repo, - runId, - }: { + async reRunWorkflow(options: { hostname?: string; owner: string; repo: string; runId: number; }): Promise { + const { hostname, owner, repo, runId } = options; + const octokit = await this.getOctokit(hostname); return octokit.actions.reRunWorkflow({ owner, @@ -59,14 +61,8 @@ export class GithubActionsClient implements GithubActionsApi { run_id: runId, }); } - async listWorkflowRuns({ - hostname, - owner, - repo, - pageSize = 100, - page = 0, - branch, - }: { + + async listWorkflowRuns(options: { hostname?: string; owner: string; repo: string; @@ -76,6 +72,8 @@ export class GithubActionsClient implements GithubActionsApi { }): Promise< RestEndpointMethodTypes['actions']['listWorkflowRuns']['response']['data'] > { + const { hostname, owner, repo, pageSize = 100, page = 0, branch } = options; + const octokit = await this.getOctokit(hostname); const workflowRuns = await octokit.actions.listWorkflowRunsForRepo({ owner, @@ -84,14 +82,11 @@ export class GithubActionsClient implements GithubActionsApi { page, ...(branch ? { branch } : {}), }); + return workflowRuns.data; } - async getWorkflow({ - hostname, - owner, - repo, - id, - }: { + + async getWorkflow(options: { hostname?: string; owner: string; repo: string; @@ -99,20 +94,19 @@ export class GithubActionsClient implements GithubActionsApi { }): Promise< RestEndpointMethodTypes['actions']['getWorkflow']['response']['data'] > { + const { hostname, owner, repo, id } = options; + const octokit = await this.getOctokit(hostname); const workflow = await octokit.actions.getWorkflow({ owner, repo, workflow_id: id, }); + return workflow.data; } - async getWorkflowRun({ - hostname, - owner, - repo, - id, - }: { + + async getWorkflowRun(options: { hostname?: string; owner: string; repo: string; @@ -120,22 +114,19 @@ export class GithubActionsClient implements GithubActionsApi { }): Promise< RestEndpointMethodTypes['actions']['getWorkflowRun']['response']['data'] > { + const { hostname, owner, repo, id } = options; + const octokit = await this.getOctokit(hostname); const run = await octokit.actions.getWorkflowRun({ owner, repo, run_id: id, }); + return run.data; } - async listJobsForWorkflowRun({ - hostname, - owner, - repo, - id, - pageSize = 100, - page = 0, - }: { + + async listJobsForWorkflowRun(options: { hostname?: string; owner: string; repo: string; @@ -145,6 +136,8 @@ export class GithubActionsClient implements GithubActionsApi { }): Promise< RestEndpointMethodTypes['actions']['listJobsForWorkflowRun']['response']['data'] > { + const { hostname, owner, repo, id, pageSize = 100, page = 0 } = options; + const octokit = await this.getOctokit(hostname); const jobs = await octokit.actions.listJobsForWorkflowRun({ owner, @@ -153,14 +146,11 @@ export class GithubActionsClient implements GithubActionsApi { per_page: pageSize, page, }); + return jobs.data; } - async downloadJobLogsForWorkflowRun({ - hostname, - owner, - repo, - runId, - }: { + + async downloadJobLogsForWorkflowRun(options: { hostname?: string; owner: string; repo: string; @@ -168,12 +158,15 @@ export class GithubActionsClient implements GithubActionsApi { }): Promise< RestEndpointMethodTypes['actions']['downloadJobLogsForWorkflowRun']['response']['data'] > { + const { hostname, owner, repo, runId } = options; + const octokit = await this.getOctokit(hostname); const workflow = await octokit.actions.downloadJobLogsForWorkflowRun({ owner, repo, job_id: runId, }); + return workflow.data; } } diff --git a/plugins/github-deployments/CHANGELOG.md b/plugins/github-deployments/CHANGELOG.md index aacb12d487..45a93e3b34 100644 --- a/plugins/github-deployments/CHANGELOG.md +++ b/plugins/github-deployments/CHANGELOG.md @@ -1,5 +1,41 @@ # @backstage/plugin-github-deployments +## 0.1.37 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/integration@1.2.1 + - @backstage/core-plugin-api@1.0.3 + - @backstage/integration-react@1.1.1 + - @backstage/catalog-model@1.0.3 + +## 0.1.37-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/integration-react@1.1.1-next.1 + - @backstage/integration@1.2.1-next.1 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## 0.1.37-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + - @backstage/integration@1.2.1-next.0 + - @backstage/integration-react@1.1.1-next.0 + ## 0.1.36 ### Patch Changes diff --git a/plugins/github-deployments/package.json b/plugins/github-deployments/package.json index d49b55150d..1a27d21aa2 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.36", + "version": "0.1.37", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -24,13 +24,13 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-model": "^1.0.2", - "@backstage/core-components": "^0.9.4", - "@backstage/core-plugin-api": "^1.0.2", + "@backstage/catalog-model": "^1.0.3", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", "@backstage/errors": "^1.0.0", - "@backstage/integration": "^1.2.0", - "@backstage/integration-react": "^1.1.0", - "@backstage/plugin-catalog-react": "^1.1.0", + "@backstage/integration": "^1.2.1", + "@backstage/integration-react": "^1.1.1", + "@backstage/plugin-catalog-react": "^1.1.1", "@backstage/theme": "^0.2.15", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -43,17 +43,17 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/core-app-api": "^1.0.2", - "@backstage/dev-utils": "^1.0.2", - "@backstage/test-utils": "^1.1.0", + "@backstage/cli": "^0.17.2", + "@backstage/core-app-api": "^1.0.3", + "@backstage/dev-utils": "^1.0.3", + "@backstage/test-utils": "^1.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", "@types/jest": "^26.0.7", "@types/node": "^16.11.26", "cross-fetch": "^3.1.5", - "msw": "^0.35.0" + "msw": "^0.42.0" }, "files": [ "dist" diff --git a/plugins/github-pull-requests-board/.eslintrc.js b/plugins/github-pull-requests-board/.eslintrc.js new file mode 100644 index 0000000000..13573efa9c --- /dev/null +++ b/plugins/github-pull-requests-board/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + extends: [require.resolve('@backstage/cli/config/eslint')], +}; diff --git a/plugins/github-pull-requests-board/CHANGELOG.md b/plugins/github-pull-requests-board/CHANGELOG.md new file mode 100644 index 0000000000..629713540e --- /dev/null +++ b/plugins/github-pull-requests-board/CHANGELOG.md @@ -0,0 +1,33 @@ +# @backstage/plugin-github-pull-requests-board + +## 0.1.0 + +### Minor Changes + +- fc9927c81d: Add Github Pull Requests board plugin + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/integration@1.2.1 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## 0.1.0-next.0 + +### Minor Changes + +- fc9927c81d: Add Github Pull Requests board plugin + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/integration@1.2.1-next.1 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 diff --git a/plugins/github-pull-requests-board/README.md b/plugins/github-pull-requests-board/README.md new file mode 100644 index 0000000000..c8b14fe581 --- /dev/null +++ b/plugins/github-pull-requests-board/README.md @@ -0,0 +1,79 @@ +# GitHub Pull Requests Board Plugin + +The GitHub Pull Requests Board Plugin helps to visualise all **Open Pull Requests** related to the owned team repository. + +![github-pull-requests-board](./docs/pull-requests-board.png) + +It will help you and your team stay on top of open pull requests, hopefully reducing the time from open to merged. It's particularly useful when your team deals with many repositories. + +## Prerequisites + +- [GitHub Authentication Provider](https://backstage.io/docs/auth/github/provider) + +## Getting started + +Install the plugin by running the following command **from your Backstage root directory** + +`yarn --cwd packages/app add @backstage/plugin-github-pull-requests-board` + +The plugin exports the **EntityTeamPullRequestsCard** component which can be added to the Overview page of the team at `backstage/packages/app/src/components/catalog/EntityPage.tsx` + +```javascript +import { EntityTeamPullRequestsCard } from '@backstage/plugin-github-pull-requests-board'; + +const groupPage = ( + + + + {entityWarningContent} + + + + + + + + + + + + + + + +); +``` + +Or you can also import the **EntityTeamPullRequestsContent** component which can be used to add a new tab under the group page at `backstage/packages/app/src/components/catalog/EntityPage.tsx` + +```javascript +import { EntityTeamPullRequestsContent } from '@backstage/plugin-github-pull-requests-board'; + +const groupPage = ( + + + + {entityWarningContent} + + + + + + + + + + + + + + + ; +) +``` diff --git a/plugins/github-pull-requests-board/api-report.md b/plugins/github-pull-requests-board/api-report.md new file mode 100644 index 0000000000..33814c76b4 --- /dev/null +++ b/plugins/github-pull-requests-board/api-report.md @@ -0,0 +1,21 @@ +## API Report File for "@backstage/plugin-github-pull-requests-board" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +/// + +import { FunctionComponent } from 'react'; + +// Warning: (ae-missing-release-tag) "EntityTeamPullRequestsCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const EntityTeamPullRequestsCard: FunctionComponent<{}>; + +// Warning: (ae-missing-release-tag) "EntityTeamPullRequestsContent" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const EntityTeamPullRequestsContent: FunctionComponent<{}>; + +// (No @packageDocumentation comment for this package) +``` diff --git a/plugins/github-pull-requests-board/docs/pull-requests-board.png b/plugins/github-pull-requests-board/docs/pull-requests-board.png new file mode 100644 index 0000000000..9b7daa981a Binary files /dev/null and b/plugins/github-pull-requests-board/docs/pull-requests-board.png differ diff --git a/plugins/github-pull-requests-board/package.json b/plugins/github-pull-requests-board/package.json new file mode 100644 index 0000000000..24909c5b5d --- /dev/null +++ b/plugins/github-pull-requests-board/package.json @@ -0,0 +1,71 @@ +{ + "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.0", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "publishConfig": { + "access": "public", + "main": "dist/index.esm.js", + "types": "dist/index.d.ts" + }, + "backstage": { + "role": "frontend-plugin" + }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/github-pull-requests-board" + }, + "keywords": [ + "backstage", + "github", + "pull requests" + ], + "scripts": { + "build": "backstage-cli package build", + "start": "backstage-cli package start", + "lint": "backstage-cli package lint", + "test": "backstage-cli package test", + "diff": "backstage-cli plugin:diff", + "prepack": "backstage-cli package prepack", + "postpack": "backstage-cli package postpack", + "clean": "backstage-cli package clean" + }, + "dependencies": { + "@backstage/catalog-model": "^1.0.3", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", + "@backstage/integration": "^1.2.1", + "@backstage/plugin-catalog-react": "^1.1.1", + "@backstage/theme": "^0.2.15", + "@material-ui/core": "^4.12.2", + "@material-ui/icons": "^4.9.1", + "@material-ui/lab": "4.0.0-alpha.57", + "@octokit/rest": "^18.12.0", + "moment": "^2.29.1", + "react-use": "^17.2.4" + }, + "devDependencies": { + "@backstage/cli": "^0.17.2", + "@backstage/dev-utils": "^1.0.3", + "@backstage/test-utils": "^1.1.1", + "@testing-library/jest-dom": "^5.10.1", + "@testing-library/react": "^12.1.3", + "@testing-library/user-event": "^14.0.0", + "@types/jest": "^26.0.7", + "@types/node": "^16.11.26", + "cross-fetch": "^3.1.5", + "msw": "^0.42.0" + }, + "peerDependencies": { + "@types/react": "^16.13.1 || ^17.0.0", + "react": "^16.13.1 || ^17.0.0", + "react-dom": "^16.13.1 || ^17.0.0" + }, + "files": [ + "dist" + ] +} diff --git a/plugins/github-pull-requests-board/src/api/useGetPullRequestDetails.ts b/plugins/github-pull-requests-board/src/api/useGetPullRequestDetails.ts new file mode 100644 index 0000000000..d44c36b54d --- /dev/null +++ b/plugins/github-pull-requests-board/src/api/useGetPullRequestDetails.ts @@ -0,0 +1,91 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React from 'react'; + +import { GraphQlPullRequest, PullRequest } from '../utils/types'; +import { useOctokitGraphQl } from './useOctokitGraphQl'; + +export const useGetPullRequestDetails = () => { + const graphql = useOctokitGraphQl>(); + + const fn = React.useRef( + async (repo: string, number: number): Promise => { + const [organisation, repositoryName] = repo.split('/'); + + const { repository } = await graphql( + ` + query ($name: String!, $owner: String!, $pull_number: Int!) { + repository(name: $name, owner: $owner) { + pullRequest(number: $pull_number) { + id + repository { + name + } + title + url + createdAt + lastEditedAt + latestReviews(first: 10) { + nodes { + author { + login + avatarUrl + ... on User { + id + email + name + login + } + } + state + } + } + mergeable + state + reviewDecision + isDraft + createdAt + author { + ... on User { + id + email + avatarUrl + name + login + } + ... on Bot { + id + avatarUrl + login + } + } + } + } + } + `, + { + name: repositoryName, + owner: organisation, + pull_number: number, + }, + ); + + return repository.pullRequest; + }, + ); + + return fn.current; +}; diff --git a/plugins/github-pull-requests-board/src/api/useGetPullRequestsFromRepository.ts b/plugins/github-pull-requests-board/src/api/useGetPullRequestsFromRepository.ts new file mode 100644 index 0000000000..bd5df89833 --- /dev/null +++ b/plugins/github-pull-requests-board/src/api/useGetPullRequestsFromRepository.ts @@ -0,0 +1,54 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React from 'react'; + +import { GraphQlPullRequests, PullRequestsNumber } from '../utils/types'; +import { useOctokitGraphQl } from './useOctokitGraphQl'; + +export const useGetPullRequestsFromRepository = () => { + const graphql = + useOctokitGraphQl>(); + + const fn = React.useRef( + async (repo: string): Promise => { + const [organisation, repositoryName] = repo.split('/'); + + const { repository } = await graphql( + ` + query ($name: String!, $owner: String!) { + repository(name: $name, owner: $owner) { + pullRequests(states: OPEN, first: 10) { + edges { + node { + number + } + } + } + } + } + `, + { + name: repositoryName, + owner: organisation, + }, + ); + + return repository.pullRequests.edges; + }, + ); + + return fn.current; +}; diff --git a/plugins/github-pull-requests-board/src/api/useOctokitGraphQl.ts b/plugins/github-pull-requests-board/src/api/useOctokitGraphQl.ts new file mode 100644 index 0000000000..a864f9950e --- /dev/null +++ b/plugins/github-pull-requests-board/src/api/useOctokitGraphQl.ts @@ -0,0 +1,47 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { Octokit } from '@octokit/rest'; +import { + useApi, + githubAuthApiRef, + configApiRef, +} from '@backstage/core-plugin-api'; +import { readGitHubIntegrationConfigs } from '@backstage/integration'; + +let octokit: any; + +export const useOctokitGraphQl = () => { + const auth = useApi(githubAuthApiRef); + const config = useApi(configApiRef); + + const baseUrl = readGitHubIntegrationConfigs( + config.getOptionalConfigArray('integrations.github') ?? [], + )[0].apiBaseUrl; + + return (path: string, options?: any): Promise => + auth + .getAccessToken(['repo']) + .then((token: string) => { + if (!octokit) { + octokit = new Octokit({ auth: token, ...(baseUrl && { baseUrl }) }); + } + + return octokit; + }) + .then(octokitInstance => { + return octokitInstance.graphql(path, options); + }); +}; diff --git a/plugins/github-pull-requests-board/src/components/Card/Card.tsx b/plugins/github-pull-requests-board/src/components/Card/Card.tsx new file mode 100644 index 0000000000..56e8c6a077 --- /dev/null +++ b/plugins/github-pull-requests-board/src/components/Card/Card.tsx @@ -0,0 +1,63 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React, { PropsWithChildren, FunctionComponent } from 'react'; +import { Box, Paper, CardActionArea } from '@material-ui/core'; +import CardHeader from './CardHeader'; + +type Props = { + title: string; + createdAt: string; + updatedAt?: string; + prUrl: string; + authorName: string; + authorAvatar?: string; + repositoryName: string; +}; + +const Card: FunctionComponent = (props: PropsWithChildren) => { + const { + title, + createdAt, + updatedAt, + prUrl, + authorName, + authorAvatar, + repositoryName, + children, + } = props; + + return ( + + + + + + {children} + + + + + ); +}; + +export default Card; diff --git a/plugins/github-pull-requests-board/src/components/Card/CardHeader.tsx b/plugins/github-pull-requests-board/src/components/Card/CardHeader.tsx new file mode 100644 index 0000000000..831423a464 --- /dev/null +++ b/plugins/github-pull-requests-board/src/components/Card/CardHeader.tsx @@ -0,0 +1,65 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React, { FunctionComponent } from 'react'; +import { Typography, Box } from '@material-ui/core'; +import { getElapsedTime } from '../../utils/functions'; +import { UserHeader } from '../UserHeader'; + +type Props = { + title: string; + createdAt: string; + updatedAt?: string; + authorName: string; + authorAvatar?: string; + repositoryName: string; +}; + +const CardHeader: FunctionComponent = (props: Props) => { + const { + title, + createdAt, + updatedAt, + authorName, + authorAvatar, + repositoryName, + } = props; + + return ( + <> + + + {repositoryName} + + + + + {title} + + + + Created at: {getElapsedTime(createdAt)} + + {updatedAt && ( + + Last update: {getElapsedTime(updatedAt)} + + )} + + + ); +}; + +export default CardHeader; diff --git a/plugins/github-pull-requests-board/src/components/Card/index.ts b/plugins/github-pull-requests-board/src/components/Card/index.ts new file mode 100644 index 0000000000..527bd23115 --- /dev/null +++ b/plugins/github-pull-requests-board/src/components/Card/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export { default as Card } from './Card'; diff --git a/plugins/github-pull-requests-board/src/components/EntityTeamPullRequestsCard/EntityTeamPullRequestsCard.tsx b/plugins/github-pull-requests-board/src/components/EntityTeamPullRequestsCard/EntityTeamPullRequestsCard.tsx new file mode 100644 index 0000000000..b19651536a --- /dev/null +++ b/plugins/github-pull-requests-board/src/components/EntityTeamPullRequestsCard/EntityTeamPullRequestsCard.tsx @@ -0,0 +1,113 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React, { FunctionComponent, useState } from 'react'; +import { Grid, Typography } from '@material-ui/core'; +import FullscreenIcon from '@material-ui/icons/Fullscreen'; + +import { Progress, InfoCard } from '@backstage/core-components'; + +import { InfoCardHeader } from '../InfoCardHeader'; +import { PullRequestBoardOptions } from '../PullRequestBoardOptions'; +import { Wrapper } from '../Wrapper'; +import { PullRequestCard } from '../PullRequestCard'; +import { usePullRequestsByTeam } from '../../hooks/usePullRequestsByTeam'; +import { PRCardFormating } from '../../utils/types'; +import { DraftPrIcon } from '../icons/DraftPr'; +import { useUserRepositories } from '../../hooks/useUserRepositories'; + +const EntityTeamPullRequestsCard: FunctionComponent = () => { + const [infoCardFormat, setInfoCardFormat] = useState([]); + const { repositories } = useUserRepositories(); + const { loading, pullRequests, refreshPullRequests } = + usePullRequestsByTeam(repositories); + + const header = ( + + setInfoCardFormat(newFormats)} + value={infoCardFormat} + options={[ + { + icon: , + value: 'draft', + ariaLabel: 'Show draft PRs', + }, + { + icon: , + value: 'fullscreen', + ariaLabel: 'Info card is set to fullscreen', + }, + ]} + /> + + ); + + const getContent = () => { + if (loading) { + return ; + } + + return ( + + {pullRequests.length ? ( + pullRequests.map(({ title: columnTitle, content }) => ( + + {columnTitle} + {content.map( + ( + { + id, + title, + createdAt, + lastEditedAt, + author, + url, + latestReviews, + repository, + isDraft, + }, + index, + ) => + infoCardFormat.includes('draft') === isDraft && ( + + ), + )} + + )) + ) : ( + No pull requests found + )} + + ); + }; + + return {getContent()}; +}; + +export default EntityTeamPullRequestsCard; diff --git a/plugins/github-pull-requests-board/src/components/EntityTeamPullRequestsCard/index.ts b/plugins/github-pull-requests-board/src/components/EntityTeamPullRequestsCard/index.ts new file mode 100644 index 0000000000..bac16f1424 --- /dev/null +++ b/plugins/github-pull-requests-board/src/components/EntityTeamPullRequestsCard/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export { default as EntityTeamPullRequestsCard } from './EntityTeamPullRequestsCard'; diff --git a/plugins/github-pull-requests-board/src/components/EntityTeamPullRequestsContent/EntityTeamPullRequestsContent.tsx b/plugins/github-pull-requests-board/src/components/EntityTeamPullRequestsContent/EntityTeamPullRequestsContent.tsx new file mode 100644 index 0000000000..33980e2364 --- /dev/null +++ b/plugins/github-pull-requests-board/src/components/EntityTeamPullRequestsContent/EntityTeamPullRequestsContent.tsx @@ -0,0 +1,103 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React, { FunctionComponent, useState } from 'react'; +import { Grid, Typography } from '@material-ui/core'; +import { Progress, InfoCard } from '@backstage/core-components'; + +import { InfoCardHeader } from '../InfoCardHeader'; +import { PullRequestBoardOptions } from '../PullRequestBoardOptions'; +import { Wrapper } from '../Wrapper'; +import { PullRequestCard } from '../PullRequestCard'; +import { usePullRequestsByTeam } from '../../hooks/usePullRequestsByTeam'; +import { PRCardFormating } from '../../utils/types'; +import { DraftPrIcon } from '../icons/DraftPr'; +import { useUserRepositories } from '../../hooks/useUserRepositories'; + +const EntityTeamPullRequestsContent: FunctionComponent = () => { + const [infoCardFormat, setInfoCardFormat] = useState([]); + const { repositories } = useUserRepositories(); + const { loading, pullRequests, refreshPullRequests } = + usePullRequestsByTeam(repositories); + + const header = ( + + setInfoCardFormat(newFormats)} + value={infoCardFormat} + options={[ + { + icon: , + value: 'draft', + ariaLabel: 'Show draft PRs', + }, + ]} + /> + + ); + + const getContent = () => { + if (loading) { + return ; + } + + return ( + + {pullRequests.length ? ( + pullRequests.map(({ title: columnTitle, content }) => ( + + {columnTitle} + {content.map( + ( + { + id, + title, + createdAt, + lastEditedAt, + author, + url, + latestReviews, + repository, + isDraft, + }, + index, + ) => + infoCardFormat.includes('draft') === isDraft && ( + + ), + )} + + )) + ) : ( + No pull requests found + )} + + ); + }; + + return {getContent()}; +}; + +export default EntityTeamPullRequestsContent; diff --git a/plugins/github-pull-requests-board/src/components/EntityTeamPullRequestsContent/index.ts b/plugins/github-pull-requests-board/src/components/EntityTeamPullRequestsContent/index.ts new file mode 100644 index 0000000000..c2be57464c --- /dev/null +++ b/plugins/github-pull-requests-board/src/components/EntityTeamPullRequestsContent/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export { default as EntityTeamPullRequestsContent } from './EntityTeamPullRequestsContent'; diff --git a/plugins/github-pull-requests-board/src/components/InfoCardHeader/InfoCardHeader.tsx b/plugins/github-pull-requests-board/src/components/InfoCardHeader/InfoCardHeader.tsx new file mode 100644 index 0000000000..8c126bcaa4 --- /dev/null +++ b/plugins/github-pull-requests-board/src/components/InfoCardHeader/InfoCardHeader.tsx @@ -0,0 +1,42 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React, { PropsWithChildren, FunctionComponent } from 'react'; +import { Typography, Box, IconButton } from '@material-ui/core'; +import RefreshIcon from '@material-ui/icons/Refresh'; + +type Props = { + onRefresh: () => void; +}; + +const InfoCardHeader: FunctionComponent = ( + props: PropsWithChildren, +) => { + const { children, onRefresh } = props; + + return ( + + + Open pull requests + + + + + {children} + + ); +}; + +export default InfoCardHeader; diff --git a/plugins/github-pull-requests-board/src/components/InfoCardHeader/index.ts b/plugins/github-pull-requests-board/src/components/InfoCardHeader/index.ts new file mode 100644 index 0000000000..393a04af55 --- /dev/null +++ b/plugins/github-pull-requests-board/src/components/InfoCardHeader/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export { default as InfoCardHeader } from './InfoCardHeader'; diff --git a/plugins/github-pull-requests-board/src/components/PullRequestBoardOptions/PullRequestBoardOptions.tsx b/plugins/github-pull-requests-board/src/components/PullRequestBoardOptions/PullRequestBoardOptions.tsx new file mode 100644 index 0000000000..6938210e68 --- /dev/null +++ b/plugins/github-pull-requests-board/src/components/PullRequestBoardOptions/PullRequestBoardOptions.tsx @@ -0,0 +1,59 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React, { ReactNode, FunctionComponent } from 'react'; +import { ToggleButton, ToggleButtonGroup } from '@material-ui/lab'; +import { Tooltip, Box } from '@material-ui/core'; +import { PRCardFormating } from '../../utils/types'; + +type Option = { + icon: ReactNode; + value: string; + ariaLabel: string; +}; + +type Props = { + value: string[]; + onClickOption: (selectedOptions: PRCardFormating[]) => void; + options: Option[]; +}; + +const PullRequestBoardOptions: FunctionComponent = (props: Props) => { + const { value, onClickOption, options } = props; + return ( + onClickOption(selectedOptions)} + aria-label="Pull Request board settings" + > + {options.map(({ icon, value: toggleValue, ariaLabel }, index) => ( + + + + {icon} + + + + ))} + + ); +}; + +export default PullRequestBoardOptions; diff --git a/plugins/github-pull-requests-board/src/components/PullRequestBoardOptions/index.ts b/plugins/github-pull-requests-board/src/components/PullRequestBoardOptions/index.ts new file mode 100644 index 0000000000..793ae8d0b7 --- /dev/null +++ b/plugins/github-pull-requests-board/src/components/PullRequestBoardOptions/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export { default as PullRequestBoardOptions } from './PullRequestBoardOptions'; diff --git a/plugins/github-pull-requests-board/src/components/PullRequestCard/PullRequestCard.tsx b/plugins/github-pull-requests-board/src/components/PullRequestCard/PullRequestCard.tsx new file mode 100644 index 0000000000..7e1596f5c2 --- /dev/null +++ b/plugins/github-pull-requests-board/src/components/PullRequestCard/PullRequestCard.tsx @@ -0,0 +1,91 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React, { FunctionComponent } from 'react'; +import { + getApprovedReviews, + getChangeRequests, + getCommentedReviews, +} from '../../utils/functions'; +import { Reviews, Author } from '../../utils/types'; +import { Card } from '../Card'; +import { UserHeaderList } from '../UserHeaderList'; + +type Props = { + title: string; + createdAt: string; + updatedAt?: string; + author: Author; + url: string; + reviews: Reviews; + repositoryName: string; + isDraft: boolean; +}; + +const PullRequestCard: FunctionComponent = (props: Props) => { + const { + title, + createdAt, + updatedAt, + author, + url, + reviews, + repositoryName, + isDraft, + } = props; + + const approvedReviews = getApprovedReviews(reviews); + const commentsReviews = getCommentedReviews(reviews); + const changeRequests = getChangeRequests(reviews); + + const cardTitle = isDraft ? `๐Ÿ”ง DRAFT - ${title}` : title; + + return ( + + {!!approvedReviews.length && ( + reviewAuthor, + )} + /> + )} + {!!commentsReviews.length && ( + reviewAuthor, + )} + /> + )} + {!!changeRequests.length && ( + reviewAuthor)} + /> + )} + + ); +}; + +export default PullRequestCard; diff --git a/plugins/github-pull-requests-board/src/components/PullRequestCard/index.ts b/plugins/github-pull-requests-board/src/components/PullRequestCard/index.ts new file mode 100644 index 0000000000..86da8f79af --- /dev/null +++ b/plugins/github-pull-requests-board/src/components/PullRequestCard/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export { default as PullRequestCard } from './PullRequestCard'; diff --git a/plugins/github-pull-requests-board/src/components/UserHeader/UserHeader.tsx b/plugins/github-pull-requests-board/src/components/UserHeader/UserHeader.tsx new file mode 100644 index 0000000000..85083925b7 --- /dev/null +++ b/plugins/github-pull-requests-board/src/components/UserHeader/UserHeader.tsx @@ -0,0 +1,46 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React, { FunctionComponent } from 'react'; +import { Typography, Box, Avatar, makeStyles } from '@material-ui/core'; + +type Props = { + name: string; + avatar?: string; +}; + +const useStyles = makeStyles(theme => ({ + small: { + width: theme.spacing(4), + height: theme.spacing(4), + marginLeft: theme.spacing(1), + }, +})); + +const UserHeader: FunctionComponent = (props: Props) => { + const { name, avatar } = props; + const classes = useStyles(); + + return ( + + + {name} + + + + ); +}; + +export default UserHeader; diff --git a/plugins/github-pull-requests-board/src/components/UserHeader/index.ts b/plugins/github-pull-requests-board/src/components/UserHeader/index.ts new file mode 100644 index 0000000000..3125396644 --- /dev/null +++ b/plugins/github-pull-requests-board/src/components/UserHeader/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export { default as UserHeader } from './UserHeader'; diff --git a/plugins/github-pull-requests-board/src/components/UserHeaderList/UserHeaderList.tsx b/plugins/github-pull-requests-board/src/components/UserHeaderList/UserHeaderList.tsx new file mode 100644 index 0000000000..a2c52a6d5e --- /dev/null +++ b/plugins/github-pull-requests-board/src/components/UserHeaderList/UserHeaderList.tsx @@ -0,0 +1,47 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React, { FunctionComponent } from 'react'; +import { Typography, Box } from '@material-ui/core'; +import { filterSameUser } from '../../utils/functions'; + +import { UserHeader } from '../UserHeader'; +import { Author } from '../../utils/types'; + +type Props = { + label?: string; + users: Author[]; +}; + +const UserHeaderList: FunctionComponent = (props: Props) => { + const { users, label } = props; + + return ( + + {label && {label}} + {filterSameUser(users).map(({ login, avatarUrl }) => ( + + ))} + + ); +}; + +export default UserHeaderList; diff --git a/plugins/github-pull-requests-board/src/components/UserHeaderList/index.ts b/plugins/github-pull-requests-board/src/components/UserHeaderList/index.ts new file mode 100644 index 0000000000..81a19b68bc --- /dev/null +++ b/plugins/github-pull-requests-board/src/components/UserHeaderList/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export { default as UserHeaderList } from './UserHeaderList'; diff --git a/plugins/github-pull-requests-board/src/components/Wrapper/Wrapper.tsx b/plugins/github-pull-requests-board/src/components/Wrapper/Wrapper.tsx new file mode 100644 index 0000000000..6d8f17cb72 --- /dev/null +++ b/plugins/github-pull-requests-board/src/components/Wrapper/Wrapper.tsx @@ -0,0 +1,35 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React, { PropsWithChildren, FunctionComponent } from 'react'; +import { Grid, Box } from '@material-ui/core'; + +type Props = { + fullscreen: boolean; +}; + +const Wrapper: FunctionComponent = (props: PropsWithChildren) => { + const { children, fullscreen } = props; + + return ( + + + {children} + + + ); +}; + +export default Wrapper; diff --git a/plugins/github-pull-requests-board/src/components/Wrapper/index.ts b/plugins/github-pull-requests-board/src/components/Wrapper/index.ts new file mode 100644 index 0000000000..18a368cd73 --- /dev/null +++ b/plugins/github-pull-requests-board/src/components/Wrapper/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export { default as Wrapper } from './Wrapper'; diff --git a/plugins/github-pull-requests-board/src/components/icons/DraftPr/DraftPr.tsx b/plugins/github-pull-requests-board/src/components/icons/DraftPr/DraftPr.tsx new file mode 100644 index 0000000000..05d21b21b9 --- /dev/null +++ b/plugins/github-pull-requests-board/src/components/icons/DraftPr/DraftPr.tsx @@ -0,0 +1,32 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React, { FunctionComponent } from 'react'; + +const DraftPr: FunctionComponent = () => ( + +); + +export default DraftPr; diff --git a/plugins/github-pull-requests-board/src/components/icons/DraftPr/index.ts b/plugins/github-pull-requests-board/src/components/icons/DraftPr/index.ts new file mode 100644 index 0000000000..f2ef0a43c1 --- /dev/null +++ b/plugins/github-pull-requests-board/src/components/icons/DraftPr/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export { default as DraftPrIcon } from './DraftPr'; diff --git a/plugins/github-pull-requests-board/src/hooks/usePullRequestsByTeam.tsx b/plugins/github-pull-requests-board/src/hooks/usePullRequestsByTeam.tsx new file mode 100644 index 0000000000..234a0baefd --- /dev/null +++ b/plugins/github-pull-requests-board/src/hooks/usePullRequestsByTeam.tsx @@ -0,0 +1,82 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { useCallback, useEffect, useState } from 'react'; +import { formatPRsByReviewDecision } from '../utils/functions'; +import { PullRequests, PullRequestsColumn } from '../utils/types'; +import { useGetPullRequestsFromRepository } from '../api/useGetPullRequestsFromRepository'; +import { useGetPullRequestDetails } from '../api/useGetPullRequestDetails'; + +export function usePullRequestsByTeam(repositories: string[]) { + const [pullRequests, setPullRequests] = useState([]); + const [loading, setLoading] = useState(true); + const getPullRequests = useGetPullRequestsFromRepository(); + const getPullRequestDetails = useGetPullRequestDetails(); + + const getPRsPerRepository = useCallback( + async (repository: string): Promise => { + const pullRequestsNumbers = await getPullRequests(repository); + + const pullRequestsWithDetails = await Promise.all( + pullRequestsNumbers.map(({ node }) => + getPullRequestDetails(repository, node.number), + ), + ); + + return pullRequestsWithDetails; + }, + [getPullRequests, getPullRequestDetails], + ); + + const getPRsFromTeam = useCallback( + async (teamRepositories: string[]): Promise => { + const teamRepositoriesPromises = teamRepositories.map(repository => + getPRsPerRepository(repository), + ); + + const teamPullRequests = await Promise.allSettled( + teamRepositoriesPromises, + ).then(promises => + promises.reduce((acc, curr) => { + if (curr.status === 'fulfilled') { + return [...acc, ...curr.value]; + } + return acc; + }, [] as PullRequests), + ); + + return teamPullRequests; + }, + [getPRsPerRepository], + ); + + const getAllPullRequests = useCallback(async () => { + setLoading(true); + + const teamPullRequests = await getPRsFromTeam(repositories); + setPullRequests(formatPRsByReviewDecision(teamPullRequests)); + setLoading(false); + }, [getPRsFromTeam, repositories]); + + useEffect(() => { + getAllPullRequests(); + }, [getAllPullRequests]); + + return { + pullRequests, + loading, + refreshPullRequests: getAllPullRequests, + }; +} diff --git a/plugins/github-pull-requests-board/src/hooks/useUserRepositories.tsx b/plugins/github-pull-requests-board/src/hooks/useUserRepositories.tsx new file mode 100644 index 0000000000..b905811aea --- /dev/null +++ b/plugins/github-pull-requests-board/src/hooks/useUserRepositories.tsx @@ -0,0 +1,51 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { useApi } from '@backstage/core-plugin-api'; +import { + catalogApiRef, + humanizeEntityRef, + useEntity, +} from '@backstage/plugin-catalog-react'; +import { useCallback, useEffect, useState } from 'react'; +import { getProjectNameFromEntity } from '../utils/functions'; + +export function useUserRepositories() { + const { entity: teamEntity } = useEntity(); + const catalogApi = useApi(catalogApiRef); + const [repositories, setRepositories] = useState([]); + + const getRepositoriesNames = useCallback(async () => { + const entitiesList = await catalogApi.getEntities({ + filter: { + 'spec.owner': humanizeEntityRef(teamEntity, { defaultKind: 'group' }), + }, + }); + + const entitiesNames: string[] = entitiesList.items.map(componentEntity => + getProjectNameFromEntity(componentEntity), + ); + + setRepositories([...new Set(entitiesNames)]); + }, [catalogApi, teamEntity]); + + useEffect(() => { + getRepositoriesNames(); + }, [getRepositoriesNames]); + + return { + repositories, + }; +} diff --git a/plugins/github-pull-requests-board/src/index.ts b/plugins/github-pull-requests-board/src/index.ts new file mode 100644 index 0000000000..d9cffaa468 --- /dev/null +++ b/plugins/github-pull-requests-board/src/index.ts @@ -0,0 +1,19 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export { + EntityTeamPullRequestsCard, + EntityTeamPullRequestsContent, +} from './plugin'; diff --git a/plugins/github-pull-requests-board/src/plugin.test.ts b/plugins/github-pull-requests-board/src/plugin.test.ts new file mode 100644 index 0000000000..84f5d801e6 --- /dev/null +++ b/plugins/github-pull-requests-board/src/plugin.test.ts @@ -0,0 +1,28 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { + EntityTeamPullRequestsCard, + EntityTeamPullRequestsContent, +} from './plugin'; + +describe('github-pull-requests-board', () => { + it('should export EntityTeamPullRequestsCard', () => { + expect(EntityTeamPullRequestsCard).toBeDefined(); + }); + it('should export EntityTeamPullRequestsContent', () => { + expect(EntityTeamPullRequestsContent).toBeDefined(); + }); +}); diff --git a/plugins/github-pull-requests-board/src/plugin.ts b/plugins/github-pull-requests-board/src/plugin.ts new file mode 100644 index 0000000000..c3ed426dec --- /dev/null +++ b/plugins/github-pull-requests-board/src/plugin.ts @@ -0,0 +1,52 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { + createPlugin, + createComponentExtension, + createRoutableExtension, +} from '@backstage/core-plugin-api'; +import { rootRouteRef } from './routes'; + +const githubPullRequestsBoardPlugin = createPlugin({ + id: 'github-pull-requests-board', + routes: { + root: rootRouteRef, + }, +}); + +export const EntityTeamPullRequestsCard = githubPullRequestsBoardPlugin.provide( + createComponentExtension({ + name: 'EntityTeamPullRequestsCard', + component: { + lazy: () => + import('./components/EntityTeamPullRequestsCard').then( + m => m.EntityTeamPullRequestsCard, + ), + }, + }), +); + +export const EntityTeamPullRequestsContent = + githubPullRequestsBoardPlugin.provide( + createRoutableExtension({ + name: 'PullRequestPage', + component: () => + import('./components/EntityTeamPullRequestsContent').then( + m => m.EntityTeamPullRequestsContent, + ), + mountPoint: rootRouteRef, + }), + ); diff --git a/plugins/github-pull-requests-board/src/routes.ts b/plugins/github-pull-requests-board/src/routes.ts new file mode 100644 index 0000000000..13e21ecacf --- /dev/null +++ b/plugins/github-pull-requests-board/src/routes.ts @@ -0,0 +1,20 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { createRouteRef } from '@backstage/core-plugin-api'; + +export const rootRouteRef = createRouteRef({ + id: 'github-pull-requests-board', +}); diff --git a/plugins/github-pull-requests-board/src/setupTests.ts b/plugins/github-pull-requests-board/src/setupTests.ts new file mode 100644 index 0000000000..9bb3e72355 --- /dev/null +++ b/plugins/github-pull-requests-board/src/setupTests.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import '@testing-library/jest-dom'; +import 'cross-fetch/polyfill'; diff --git a/plugins/github-pull-requests-board/src/utils/constants.ts b/plugins/github-pull-requests-board/src/utils/constants.ts new file mode 100644 index 0000000000..525fd9e1ce --- /dev/null +++ b/plugins/github-pull-requests-board/src/utils/constants.ts @@ -0,0 +1,20 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export const COLUMNS = Object.freeze({ + REVIEW_REQUIRED: '๐Ÿ” Review required', + REVIEW_IN_PROGRESS: '๐Ÿ“ Review in progress', + APPROVED: '๐Ÿ‘ Approved', +}); diff --git a/plugins/github-pull-requests-board/src/utils/functions.ts b/plugins/github-pull-requests-board/src/utils/functions.ts new file mode 100644 index 0000000000..bfb0e0edc5 --- /dev/null +++ b/plugins/github-pull-requests-board/src/utils/functions.ts @@ -0,0 +1,105 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { Entity } from '@backstage/catalog-model'; +import moment from 'moment'; +import { + Reviews, + PullRequests, + ReviewDecision, + PullRequestsColumn, + Author, +} from './types'; +import { COLUMNS } from './constants'; + +const GITHUB_PULL_REQUESTS_ANNOTATION = 'github.com/project-slug'; + +export const getProjectNameFromEntity = (entity: Entity): string => { + return entity?.metadata.annotations?.[GITHUB_PULL_REQUESTS_ANNOTATION] ?? ''; +}; + +export const getApprovedReviews = (reviews: Reviews = []): Reviews => { + return reviews.filter(({ state }) => state === 'APPROVED'); +}; + +export const getCommentedReviews = (reviews: Reviews = []): Reviews => { + return reviews.filter(({ state }) => state === 'COMMENTED'); +}; +export const getChangeRequests = (reviews: Reviews = []): Reviews => { + return reviews.filter(({ state }) => state === 'CHANGES_REQUESTED'); +}; + +export const filterSameUser = (users: Author[]): Author[] => { + return users.reduce((acc, curr) => { + const containsUser = acc.find(({ login }) => login === curr.login); + + if (!containsUser) { + return [...acc, curr]; + } + + return acc; + }, [] as Author[]); +}; + +export const getElapsedTime = (start: string): string => { + return moment(start).fromNow(); +}; + +export const formatPRsByReviewDecision = ( + prs: PullRequests, +): PullRequestsColumn[] => { + const reviewDecisions = prs.reduce( + (acc, curr) => { + const decision = curr.reviewDecision || 'REVIEW_REQUIRED'; + + if (decision !== 'APPROVED' && curr.latestReviews.nodes.length === 0) { + return { + ...acc, + REVIEW_REQUIRED: [...acc.REVIEW_REQUIRED, curr], + }; + } + + if (decision !== 'APPROVED' && curr.latestReviews.nodes.length > 0) { + return { + ...acc, + IN_PROGRESS: [...acc.IN_PROGRESS, curr], + }; + } + + if (decision === 'APPROVED') { + return { + ...acc, + APPROVED: [...acc.APPROVED, curr], + }; + } + + return acc; + }, + { + REVIEW_REQUIRED: [], + IN_PROGRESS: [], + APPROVED: [], + } as Record, + ); + + return [ + { + title: COLUMNS.REVIEW_REQUIRED, + content: reviewDecisions.REVIEW_REQUIRED, + }, + { title: COLUMNS.REVIEW_IN_PROGRESS, content: reviewDecisions.IN_PROGRESS }, + { title: COLUMNS.APPROVED, content: reviewDecisions.APPROVED }, + ]; +}; diff --git a/plugins/github-pull-requests-board/src/utils/types.tsx b/plugins/github-pull-requests-board/src/utils/types.tsx new file mode 100644 index 0000000000..a2b121a1b8 --- /dev/null +++ b/plugins/github-pull-requests-board/src/utils/types.tsx @@ -0,0 +1,84 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export type GraphQlPullRequest = { + repository: { + pullRequest: T; + }; +}; + +export type GraphQlPullRequests = { + repository: { + pullRequests: { + edges: T; + }; + }; +}; + +export type PullRequestsNumber = { + node: { + number: number; + }; +}; + +export type Review = { + state: + | 'PENDING' + | 'COMMENTED' + | 'APPROVED' + | 'CHANGES_REQUESTED' + | 'DISMISSED'; + author: Author; +}; + +export type Reviews = Review[]; + +export type Author = { + login: string; + avatarUrl: string; + id: string; + email: string; + name: string; +}; + +export type PullRequest = { + id: string; + repository: { + name: string; + }; + title: string; + url: string; + lastEditedAt: string; + latestReviews: { + nodes: Reviews; + }; + mergeable: boolean; + state: string; + reviewDecision: ReviewDecision | null; + isDraft: boolean; + createdAt: string; + author: Author; +}; + +export type PullRequests = PullRequest[]; + +export type PullRequestsColumn = { + title: string; + content: PullRequests; +}; + +export type PRCardFormating = 'compacted' | 'fullscreen' | 'draft'; + +export type ReviewDecision = 'IN_PROGRESS' | 'APPROVED' | 'REVIEW_REQUIRED'; diff --git a/plugins/gitops-profiles/CHANGELOG.md b/plugins/gitops-profiles/CHANGELOG.md index de00949059..eea6cc0554 100644 --- a/plugins/gitops-profiles/CHANGELOG.md +++ b/plugins/gitops-profiles/CHANGELOG.md @@ -1,5 +1,30 @@ # @backstage/plugin-gitops-profiles +## 0.3.24 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + +## 0.3.24-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + +## 0.3.24-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.9.5-next.0 + ## 0.3.23 ### Patch Changes diff --git a/plugins/gitops-profiles/package.json b/plugins/gitops-profiles/package.json index dbcde5f475..ee7b3da11b 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.23", + "version": "0.3.24", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -35,8 +35,8 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/core-components": "^0.9.4", - "@backstage/core-plugin-api": "^1.0.2", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", "@backstage/theme": "^0.2.15", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -48,17 +48,17 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/core-app-api": "^1.0.2", - "@backstage/dev-utils": "^1.0.2", - "@backstage/test-utils": "^1.1.0", + "@backstage/cli": "^0.17.2", + "@backstage/core-app-api": "^1.0.3", + "@backstage/dev-utils": "^1.0.3", + "@backstage/test-utils": "^1.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", "@types/jest": "^26.0.7", "@types/node": "^16.11.26", "cross-fetch": "^3.1.5", - "msw": "^0.35.0" + "msw": "^0.42.0" }, "files": [ "dist" diff --git a/plugins/gocd/CHANGELOG.md b/plugins/gocd/CHANGELOG.md index 3c55d7e85a..1df24b97cd 100644 --- a/plugins/gocd/CHANGELOG.md +++ b/plugins/gocd/CHANGELOG.md @@ -1,5 +1,35 @@ # @backstage/plugin-gocd +## 0.1.12 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## 0.1.12-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## 0.1.12-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + ## 0.1.11 ### Patch Changes diff --git a/plugins/gocd/package.json b/plugins/gocd/package.json index 07ace204fa..1bf938fb1d 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.11", + "version": "0.1.12", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -31,11 +31,11 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-model": "^1.0.2", - "@backstage/core-components": "^0.9.4", - "@backstage/core-plugin-api": "^1.0.2", + "@backstage/catalog-model": "^1.0.3", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", "@backstage/errors": "^1.0.0", - "@backstage/plugin-catalog-react": "^1.1.0", + "@backstage/plugin-catalog-react": "^1.1.1", "@backstage/theme": "^0.2.15", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -49,10 +49,10 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/core-app-api": "^1.0.2", - "@backstage/dev-utils": "^1.0.2", - "@backstage/test-utils": "^1.1.0", + "@backstage/cli": "^0.17.2", + "@backstage/core-app-api": "^1.0.3", + "@backstage/dev-utils": "^1.0.3", + "@backstage/test-utils": "^1.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", @@ -61,7 +61,7 @@ "@types/luxon": "^2.0.4", "@types/node": "^16.11.26", "cross-fetch": "^3.1.5", - "msw": "^0.35.0" + "msw": "^0.42.0" }, "files": [ "dist", diff --git a/plugins/graphiql/CHANGELOG.md b/plugins/graphiql/CHANGELOG.md index 6b23c5ab93..56cc49f3a4 100644 --- a/plugins/graphiql/CHANGELOG.md +++ b/plugins/graphiql/CHANGELOG.md @@ -1,5 +1,32 @@ # @backstage/plugin-graphiql +## 0.2.38 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- 464c33f932: Fix for custom headers not being included in requests. +- Updated dependencies + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + +## 0.2.38-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- 464c33f932: Fix for custom headers not being included in requests. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + +## 0.2.38-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.9.5-next.0 + ## 0.2.37 ### Patch Changes diff --git a/plugins/graphiql/package.json b/plugins/graphiql/package.json index d098cae1f3..4cbbc7f262 100644 --- a/plugins/graphiql/package.json +++ b/plugins/graphiql/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-graphiql", "description": "Backstage plugin for browsing GraphQL APIs", - "version": "0.2.37", + "version": "0.2.38", "private": false, "publishConfig": { "access": "public", @@ -34,25 +34,25 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/core-components": "^0.9.4", - "@backstage/core-plugin-api": "^1.0.2", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", "@backstage/theme": "^0.2.15", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", "graphiql": "^1.5.12", - "graphql-ws": "^5.4.1", "graphql": "^16.0.0", + "graphql-ws": "^5.4.1", "react-use": "^17.2.4" }, "peerDependencies": { "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/core-app-api": "^1.0.2", - "@backstage/dev-utils": "^1.0.2", - "@backstage/test-utils": "^1.1.0", + "@backstage/cli": "^0.17.2", + "@backstage/core-app-api": "^1.0.3", + "@backstage/dev-utils": "^1.0.3", + "@backstage/test-utils": "^1.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", @@ -60,7 +60,7 @@ "@types/jest": "^26.0.7", "@types/node": "^16.11.26", "cross-fetch": "^3.1.5", - "msw": "^0.35.0", + "msw": "^0.42.0", "react-router-dom": "6.0.0-beta.0" }, "files": [ diff --git a/plugins/graphiql/src/lib/api/GraphQLEndpoints.ts b/plugins/graphiql/src/lib/api/GraphQLEndpoints.ts index 7287eb579b..7f9c0ca4ee 100644 --- a/plugins/graphiql/src/lib/api/GraphQLEndpoints.ts +++ b/plugins/graphiql/src/lib/api/GraphQLEndpoints.ts @@ -59,11 +59,12 @@ export class GraphQLEndpoints implements GraphQLBrowseApi { return { id, title, - fetcher: async (params: any) => { + fetcher: async (params: any, options: any = {}) => { const body = JSON.stringify(params); const headers = { 'Content-Type': 'application/json', ...config.headers, + ...options.headers, }; const res = await fetch(url, { method, @@ -96,7 +97,7 @@ export class GraphQLEndpoints implements GraphQLBrowseApi { return { id, title, - fetcher: async (params: any) => { + fetcher: async (params: any, options: any = {}) => { let retried = false; const doRequest = async (): Promise => { @@ -105,6 +106,7 @@ export class GraphQLEndpoints implements GraphQLBrowseApi { headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${await githubAuthApi.getAccessToken()}`, + ...options.headers, }, body: JSON.stringify(params), }); diff --git a/plugins/graphql-backend/CHANGELOG.md b/plugins/graphql-backend/CHANGELOG.md index bbf46ec5b0..d1426552e0 100644 --- a/plugins/graphql-backend/CHANGELOG.md +++ b/plugins/graphql-backend/CHANGELOG.md @@ -1,5 +1,37 @@ # @backstage/plugin-graphql-backend +## 0.1.23 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/backend-common@0.14.0 + - @backstage/plugin-catalog-graphql@0.3.10 + +## 0.1.23-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0-next.2 + +## 0.1.23-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/backend-common@0.13.6-next.1 + - @backstage/plugin-catalog-graphql@0.3.10-next.0 + +## 0.1.23-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + ## 0.1.22 ### Patch Changes diff --git a/plugins/graphql-backend/package.json b/plugins/graphql-backend/package.json index 67afd2c910..d7b03bddf5 100644 --- a/plugins/graphql-backend/package.json +++ b/plugins/graphql-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-graphql-backend", "description": "An experimental Backstage backend plugin for GraphQL", - "version": "0.1.22", + "version": "0.1.23", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -34,9 +34,9 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/backend-common": "^0.13.3", + "@backstage/backend-common": "^0.14.0", "@backstage/config": "^1.0.1", - "@backstage/plugin-catalog-graphql": "^0.3.9", + "@backstage/plugin-catalog-graphql": "^0.3.10", "@graphql-tools/schema": "^8.3.1", "@types/express": "^4.17.6", "apollo-server": "^3.0.0", @@ -51,9 +51,9 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", + "@backstage/cli": "^0.17.2", "@types/supertest": "^2.0.8", - "msw": "^0.35.0", + "msw": "^0.42.0", "supertest": "^6.1.3" }, "files": [ diff --git a/plugins/home/CHANGELOG.md b/plugins/home/CHANGELOG.md index d775487e53..ef03c3797c 100644 --- a/plugins/home/CHANGELOG.md +++ b/plugins/home/CHANGELOG.md @@ -1,5 +1,38 @@ # @backstage/plugin-home +## 0.4.22 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/plugin-stack-overflow@0.1.2 + - @backstage/catalog-model@1.0.3 + +## 0.4.22-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/plugin-stack-overflow@0.1.2-next.1 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## 0.4.22-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + - @backstage/plugin-stack-overflow@0.1.2-next.0 + ## 0.4.21 ### Patch Changes diff --git a/plugins/home/package.json b/plugins/home/package.json index e18569b974..d1529a37fd 100644 --- a/plugins/home/package.json +++ b/plugins/home/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-home", "description": "A Backstage plugin that helps you build a home page", - "version": "0.4.21", + "version": "0.4.22", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -34,13 +34,13 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-model": "^1.0.2", - "@backstage/core-components": "^0.9.4", - "@backstage/core-plugin-api": "^1.0.2", - "@backstage/plugin-catalog-react": "^1.1.0", - "@backstage/plugin-stack-overflow": "^0.1.1", - "@backstage/theme": "^0.2.15", + "@backstage/catalog-model": "^1.0.3", "@backstage/config": "^1.0.1", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", + "@backstage/plugin-catalog-react": "^1.1.1", + "@backstage/plugin-stack-overflow": "^0.1.2", + "@backstage/theme": "^0.2.15", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -53,17 +53,17 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/core-app-api": "^1.0.2", - "@backstage/dev-utils": "^1.0.2", - "@backstage/test-utils": "^1.1.0", + "@backstage/cli": "^0.17.2", + "@backstage/core-app-api": "^1.0.3", + "@backstage/dev-utils": "^1.0.3", + "@backstage/test-utils": "^1.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", "@types/jest": "^26.0.7", "@types/node": "^16.11.26", "cross-fetch": "^3.1.5", - "msw": "^0.35.0" + "msw": "^0.42.0" }, "files": [ "dist" diff --git a/plugins/ilert/CHANGELOG.md b/plugins/ilert/CHANGELOG.md index 8216fa98eb..cd74af4e74 100644 --- a/plugins/ilert/CHANGELOG.md +++ b/plugins/ilert/CHANGELOG.md @@ -1,5 +1,35 @@ # @backstage/plugin-ilert +## 0.1.32 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## 0.1.32-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## 0.1.32-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + ## 0.1.31 ### Patch Changes diff --git a/plugins/ilert/package.json b/plugins/ilert/package.json index be179bd2b4..341c934836 100644 --- a/plugins/ilert/package.json +++ b/plugins/ilert/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-ilert", "description": "A Backstage plugin that integrates towards iLert", - "version": "0.1.31", + "version": "0.1.32", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -24,11 +24,11 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-model": "^1.0.2", - "@backstage/core-components": "^0.9.4", - "@backstage/core-plugin-api": "^1.0.2", + "@backstage/catalog-model": "^1.0.3", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", "@backstage/errors": "^1.0.0", - "@backstage/plugin-catalog-react": "^1.1.0", + "@backstage/plugin-catalog-react": "^1.1.1", "@backstage/theme": "^0.2.15", "@date-io/luxon": "1.x", "@material-ui/core": "^4.12.2", @@ -43,17 +43,17 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/core-app-api": "^1.0.2", - "@backstage/dev-utils": "^1.0.2", - "@backstage/test-utils": "^1.1.0", + "@backstage/cli": "^0.17.2", + "@backstage/core-app-api": "^1.0.3", + "@backstage/dev-utils": "^1.0.3", + "@backstage/test-utils": "^1.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", "@types/jest": "^26.0.7", "@types/node": "^16.11.26", "cross-fetch": "^3.1.5", - "msw": "^0.35.0" + "msw": "^0.42.0" }, "files": [ "dist", diff --git a/plugins/jenkins-backend/CHANGELOG.md b/plugins/jenkins-backend/CHANGELOG.md index be1d788f8d..ef18b5708d 100644 --- a/plugins/jenkins-backend/CHANGELOG.md +++ b/plugins/jenkins-backend/CHANGELOG.md @@ -1,5 +1,49 @@ # @backstage/plugin-jenkins-backend +## 0.1.23 + +### Patch Changes + +- 83f6a64d2c: bug fix: provide backstage token for rebuild api call +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/backend-common@0.14.0 + - @backstage/plugin-auth-node@0.2.2 + - @backstage/catalog-client@1.0.3 + - @backstage/plugin-permission-common@0.6.2 + - @backstage/catalog-model@1.0.3 + - @backstage/plugin-jenkins-common@0.1.5 + +## 0.1.23-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0-next.2 + - @backstage/plugin-auth-node@0.2.2-next.2 + +## 0.1.23-next.1 + +### Patch Changes + +- 83f6a64d2c: bug fix: provide backstage token for rebuild api call +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/backend-common@0.13.6-next.1 + - @backstage/catalog-client@1.0.3-next.0 + - @backstage/plugin-auth-node@0.2.2-next.1 + - @backstage/plugin-permission-common@0.6.2-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-jenkins-common@0.1.5-next.0 + +## 0.1.23-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + - @backstage/plugin-auth-node@0.2.2-next.0 + ## 0.1.22 ### Patch Changes diff --git a/plugins/jenkins-backend/package.json b/plugins/jenkins-backend/package.json index d9c28de86a..1ab98c3255 100644 --- a/plugins/jenkins-backend/package.json +++ b/plugins/jenkins-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-jenkins-backend", "description": "A Backstage backend plugin that integrates towards Jenkins", - "version": "0.1.22", + "version": "0.1.23", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -25,14 +25,14 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/backend-common": "^0.13.3", - "@backstage/catalog-client": "^1.0.2", - "@backstage/catalog-model": "^1.0.2", + "@backstage/backend-common": "^0.14.0", + "@backstage/catalog-client": "^1.0.3", + "@backstage/catalog-model": "^1.0.3", "@backstage/config": "^1.0.1", "@backstage/errors": "^1.0.0", - "@backstage/plugin-auth-node": "^0.2.1", - "@backstage/plugin-jenkins-common": "^0.1.4", - "@backstage/plugin-permission-common": "^0.6.1", + "@backstage/plugin-auth-node": "^0.2.2", + "@backstage/plugin-jenkins-common": "^0.1.5", + "@backstage/plugin-permission-common": "^0.6.2", "@types/express": "^4.17.6", "express": "^4.17.1", "express-promise-router": "^4.1.0", @@ -41,10 +41,10 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", + "@backstage/cli": "^0.17.2", "@types/jenkins": "^0.23.1", "@types/supertest": "^2.0.8", - "msw": "^0.35.0", + "msw": "^0.42.0", "supertest": "^6.1.6" }, "files": [ diff --git a/plugins/jenkins-backend/src/service/router.ts b/plugins/jenkins-backend/src/service/router.ts index fa357f8e9f..2c9e268165 100644 --- a/plugins/jenkins-backend/src/service/router.ts +++ b/plugins/jenkins-backend/src/service/router.ts @@ -131,6 +131,9 @@ export async function createRouter( '/v1/entity/:namespace/:kind/:name/job/:jobFullName/:buildNumber::rebuild', async (request, response) => { const { namespace, kind, name, jobFullName } = request.params; + const token = getBearerTokenFromAuthorizationHeader( + request.header('authorization'), + ); const jenkinsInfo = await jenkinsInfoProvider.getInstance({ entityRef: { kind, @@ -138,10 +141,8 @@ export async function createRouter( name, }, jobFullName, + backstageToken: token, }); - const token = getBearerTokenFromAuthorizationHeader( - request.header('authorization'), - ); const resourceRef = stringifyEntityRef({ kind, namespace, name }); await jenkinsApi.buildProject(jenkinsInfo, jobFullName, resourceRef, { diff --git a/plugins/jenkins-common/CHANGELOG.md b/plugins/jenkins-common/CHANGELOG.md index 77f7c42e82..53cf222e5a 100644 --- a/plugins/jenkins-common/CHANGELOG.md +++ b/plugins/jenkins-common/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-jenkins-common +## 0.1.5 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-common@1.0.3 + - @backstage/plugin-permission-common@0.6.2 + +## 0.1.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-common@0.6.2-next.0 + - @backstage/plugin-catalog-common@1.0.3-next.0 + ## 0.1.4 ### Patch Changes diff --git a/plugins/jenkins-common/package.json b/plugins/jenkins-common/package.json index 45684752be..c47ac94a2b 100644 --- a/plugins/jenkins-common/package.json +++ b/plugins/jenkins-common/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-jenkins-common", - "version": "0.1.4", + "version": "0.1.5", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -22,11 +22,11 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/plugin-catalog-common": "^1.0.2", - "@backstage/plugin-permission-common": "^0.6.1" + "@backstage/plugin-catalog-common": "^1.0.3", + "@backstage/plugin-permission-common": "^0.6.2" }, "devDependencies": { - "@backstage/cli": "^0.17.1" + "@backstage/cli": "^0.17.2" }, "files": [ "dist" diff --git a/plugins/jenkins/CHANGELOG.md b/plugins/jenkins/CHANGELOG.md index 4d44ae6ca4..fd4e4420e7 100644 --- a/plugins/jenkins/CHANGELOG.md +++ b/plugins/jenkins/CHANGELOG.md @@ -1,5 +1,37 @@ # @backstage/plugin-jenkins +## 0.7.5 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + - @backstage/plugin-jenkins-common@0.1.5 + +## 0.7.5-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + - @backstage/plugin-jenkins-common@0.1.5-next.0 + +## 0.7.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + ## 0.7.4 ### Patch Changes diff --git a/plugins/jenkins/package.json b/plugins/jenkins/package.json index c8205a177a..ae86828505 100644 --- a/plugins/jenkins/package.json +++ b/plugins/jenkins/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-jenkins", "description": "A Backstage plugin that integrates towards Jenkins", - "version": "0.7.4", + "version": "0.7.5", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -35,12 +35,12 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-model": "^1.0.2", - "@backstage/core-components": "^0.9.4", - "@backstage/core-plugin-api": "^1.0.2", + "@backstage/catalog-model": "^1.0.3", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", "@backstage/errors": "^1.0.0", - "@backstage/plugin-catalog-react": "^1.1.0", - "@backstage/plugin-jenkins-common": "^0.1.4", + "@backstage/plugin-catalog-react": "^1.1.1", + "@backstage/plugin-jenkins-common": "^0.1.5", "@backstage/theme": "^0.2.15", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -54,10 +54,10 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/core-app-api": "^1.0.2", - "@backstage/dev-utils": "^1.0.2", - "@backstage/test-utils": "^1.1.0", + "@backstage/cli": "^0.17.2", + "@backstage/core-app-api": "^1.0.3", + "@backstage/dev-utils": "^1.0.3", + "@backstage/test-utils": "^1.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", @@ -65,7 +65,7 @@ "@types/node": "^16.11.26", "@types/testing-library__jest-dom": "^5.9.1", "cross-fetch": "^3.1.5", - "msw": "^0.35.0" + "msw": "^0.42.0" }, "files": [ "dist" diff --git a/plugins/kafka-backend/CHANGELOG.md b/plugins/kafka-backend/CHANGELOG.md index c844f38af7..32b147ffdd 100644 --- a/plugins/kafka-backend/CHANGELOG.md +++ b/plugins/kafka-backend/CHANGELOG.md @@ -1,5 +1,35 @@ # @backstage/plugin-kafka-backend +## 0.2.26 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0 + - @backstage/catalog-model@1.0.3 + +## 0.2.26-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0-next.2 + +## 0.2.26-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.1 + - @backstage/catalog-model@1.0.3-next.0 + +## 0.2.26-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + ## 0.2.25 ### Patch Changes diff --git a/plugins/kafka-backend/package.json b/plugins/kafka-backend/package.json index e5821ce600..965b15bd6b 100644 --- a/plugins/kafka-backend/package.json +++ b/plugins/kafka-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-kafka-backend", "description": "A Backstage backend plugin that integrates towards Kafka", - "version": "0.2.25", + "version": "0.2.26", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -35,8 +35,8 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/backend-common": "^0.13.3", - "@backstage/catalog-model": "^1.0.2", + "@backstage/backend-common": "^0.14.0", + "@backstage/catalog-model": "^1.0.3", "@backstage/config": "^1.0.1", "@backstage/errors": "^1.0.0", "@types/express": "^4.17.6", @@ -47,7 +47,7 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/cli": "^0.17.1", + "@backstage/cli": "^0.17.2", "@types/jest-when": "^3.5.0", "@types/lodash": "^4.14.151", "jest-when": "^3.1.0", diff --git a/plugins/kafka/CHANGELOG.md b/plugins/kafka/CHANGELOG.md index 4f0caebf29..c2743bf92b 100644 --- a/plugins/kafka/CHANGELOG.md +++ b/plugins/kafka/CHANGELOG.md @@ -1,5 +1,35 @@ # @backstage/plugin-kafka +## 0.3.6 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## 0.3.6-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## 0.3.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + ## 0.3.5 ### Patch Changes diff --git a/plugins/kafka/package.json b/plugins/kafka/package.json index 3dafb4a731..43f4b8d35c 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.5", + "version": "0.3.6", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -24,10 +24,10 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-model": "^1.0.2", - "@backstage/core-components": "^0.9.4", - "@backstage/core-plugin-api": "^1.0.2", - "@backstage/plugin-catalog-react": "^1.1.0", + "@backstage/catalog-model": "^1.0.3", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", + "@backstage/plugin-catalog-react": "^1.1.1", "@backstage/theme": "^0.2.15", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -39,10 +39,10 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/core-app-api": "^1.0.2", - "@backstage/dev-utils": "^1.0.2", - "@backstage/test-utils": "^1.1.0", + "@backstage/cli": "^0.17.2", + "@backstage/core-app-api": "^1.0.3", + "@backstage/dev-utils": "^1.0.3", + "@backstage/test-utils": "^1.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/react-hooks": "^8.0.0", @@ -51,7 +51,7 @@ "@types/node": "^16.11.26", "cross-fetch": "^3.1.5", "jest-when": "^3.1.0", - "msw": "^0.35.0" + "msw": "^0.42.0" }, "files": [ "dist" diff --git a/plugins/kubernetes-backend/CHANGELOG.md b/plugins/kubernetes-backend/CHANGELOG.md index 8fc9affe3a..d829136819 100644 --- a/plugins/kubernetes-backend/CHANGELOG.md +++ b/plugins/kubernetes-backend/CHANGELOG.md @@ -1,5 +1,49 @@ # @backstage/plugin-kubernetes-backend +## 0.6.0 + +### Minor Changes + +- 4328737af6: Add support to fetch data for Stateful Sets from Kubernetes + +### Patch Changes + +- 0c70cd8e1d: cache and refresh Azure tokens to avoid excessive calls to Azure Identity +- 2aedf64ad3: Updated dependency `@google-cloud/container` to `^4.0.0`. +- Updated dependencies + - @backstage/backend-common@0.14.0 + - @backstage/plugin-kubernetes-common@0.3.0 + - @backstage/catalog-model@1.0.3 + +## 0.6.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0-next.2 + +## 0.6.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.1 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-kubernetes-common@0.3.0-next.1 + +## 0.6.0-next.0 + +### Minor Changes + +- 4328737af6: Add support to fetch data for Stateful Sets from Kubernetes + +### Patch Changes + +- 0c70cd8e1d: cache and refresh Azure tokens to avoid excessive calls to Azure Identity +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + - @backstage/plugin-kubernetes-common@0.3.0-next.0 + ## 0.5.1 ### Patch Changes diff --git a/plugins/kubernetes-backend/api-report.md b/plugins/kubernetes-backend/api-report.md index be81b4d5dd..41748f240b 100644 --- a/plugins/kubernetes-backend/api-report.md +++ b/plugins/kubernetes-backend/api-report.md @@ -5,18 +5,18 @@ ```ts import { Config } from '@backstage/config'; import { Duration } from 'luxon'; +import { Entity } from '@backstage/catalog-model'; import express from 'express'; import type { FetchResponse } from '@backstage/plugin-kubernetes-common'; import type { JsonObject } from '@backstage/types'; import type { KubernetesFetchError } from '@backstage/plugin-kubernetes-common'; +import type { KubernetesRequestAuth } from '@backstage/plugin-kubernetes-common'; import type { KubernetesRequestBody } from '@backstage/plugin-kubernetes-common'; import { Logger } from 'winston'; import type { ObjectsByEntityResponse } from '@backstage/plugin-kubernetes-common'; import { PodStatus } from '@kubernetes/client-node/dist/top'; -// Warning: (ae-missing-release-tag) "AWSClusterDetails" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @alpha (undocumented) export interface AWSClusterDetails extends ClusterDetails { // (undocumented) assumeRole?: string; @@ -24,14 +24,10 @@ export interface AWSClusterDetails extends ClusterDetails { externalId?: string; } -// Warning: (ae-missing-release-tag) "AzureClusterDetails" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @alpha (undocumented) export interface AzureClusterDetails extends ClusterDetails {} -// Warning: (ae-missing-release-tag) "ClusterDetails" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @alpha (undocumented) export interface ClusterDetails { // (undocumented) authProvider: string; @@ -51,27 +47,28 @@ export interface ClusterDetails { url: string; } -// Warning: (ae-missing-release-tag) "createRouter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public @deprecated +// @alpha @deprecated export function createRouter(options: RouterOptions): Promise; -// Warning: (ae-missing-release-tag) "CustomResource" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @alpha (undocumented) export interface CustomResource extends ObjectToFetch { // (undocumented) objectType: 'customresources'; } -// Warning: (ae-missing-release-tag) "DEFAULT_OBJECTS" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @alpha (undocumented) +export type CustomResourceMatcher = Omit; + +// @alpha (undocumented) +export interface CustomResourcesByEntity extends KubernetesObjectsByEntity { + // (undocumented) + customResources: CustomResourceMatcher[]; +} + +// @alpha (undocumented) export const DEFAULT_OBJECTS: ObjectToFetch[]; -// Warning: (ae-missing-release-tag) "FetchResponseWrapper" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @alpha (undocumented) export interface FetchResponseWrapper { // (undocumented) errors: KubernetesFetchError[]; @@ -79,14 +76,10 @@ export interface FetchResponseWrapper { responses: FetchResponse[]; } -// Warning: (ae-missing-release-tag) "GKEClusterDetails" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @alpha (undocumented) export interface GKEClusterDetails extends ClusterDetails {} -// Warning: (ae-missing-release-tag) "KubernetesBuilder" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @alpha (undocumented) export class KubernetesBuilder { constructor(env: KubernetesEnvironment); // (undocumented) @@ -145,7 +138,7 @@ export class KubernetesBuilder { setServiceLocator(serviceLocator?: KubernetesServiceLocator): this; } -// @public +// @alpha export type KubernetesBuilderReturn = Promise<{ router: express.Router; clusterSupplier: KubernetesClustersSupplier; @@ -155,16 +148,12 @@ export type KubernetesBuilderReturn = Promise<{ serviceLocator: KubernetesServiceLocator; }>; -// Warning: (ae-missing-release-tag) "KubernetesClustersSupplier" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @alpha export interface KubernetesClustersSupplier { getClusters(): Promise; } -// Warning: (ae-missing-release-tag) "KubernetesEnvironment" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @alpha (undocumented) export interface KubernetesEnvironment { // (undocumented) config: Config; @@ -172,9 +161,7 @@ export interface KubernetesEnvironment { logger: Logger; } -// Warning: (ae-missing-release-tag) "KubernetesFetcher" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @alpha export interface KubernetesFetcher { // (undocumented) fetchObjectsForService( @@ -187,19 +174,27 @@ export interface KubernetesFetcher { ): Promise; } -// Warning: (ae-missing-release-tag) "KubernetesObjectsProvider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @alpha (undocumented) +export interface KubernetesObjectsByEntity { + // (undocumented) + auth: KubernetesRequestAuth; + // (undocumented) + entity: Entity; +} + +// @alpha (undocumented) export interface KubernetesObjectsProvider { + // (undocumented) + getCustomResourcesByEntity( + customResourcesByEntity: CustomResourcesByEntity, + ): Promise; // (undocumented) getKubernetesObjectsByEntity( - request: ObjectsByEntityRequest, + kubernetesObjectsByEntity: KubernetesObjectsByEntity, ): Promise; } -// Warning: (ae-missing-release-tag) "KubernetesObjectsProviderOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @alpha (undocumented) export interface KubernetesObjectsProviderOptions { // (undocumented) customResources: CustomResource[]; @@ -213,9 +208,7 @@ export interface KubernetesObjectsProviderOptions { serviceLocator: KubernetesServiceLocator; } -// Warning: (ae-missing-release-tag) "KubernetesObjectTypes" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @alpha (undocumented) export type KubernetesObjectTypes = | 'pods' | 'services' @@ -229,17 +222,15 @@ export type KubernetesObjectTypes = | 'customresources' | 'statefulsets'; -// Warning: (ae-missing-release-tag) "KubernetesServiceLocator" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @alpha export interface KubernetesServiceLocator { // (undocumented) - getClustersByServiceId(serviceId: string): Promise; + getClustersByEntity(entity: Entity): Promise<{ + clusters: ClusterDetails[]; + }>; } -// Warning: (ae-missing-release-tag) "ObjectFetchParams" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @alpha (undocumented) export interface ObjectFetchParams { // (undocumented) clusterDetails: @@ -259,14 +250,10 @@ export interface ObjectFetchParams { serviceId: string; } -// Warning: (ae-missing-release-tag) "ObjectsByEntityRequest" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @alpha (undocumented) export type ObjectsByEntityRequest = KubernetesRequestBody; -// Warning: (ae-missing-release-tag) "ObjectToFetch" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @alpha (undocumented) export interface ObjectToFetch { // (undocumented) apiVersion: string; @@ -278,9 +265,7 @@ export interface ObjectToFetch { plural: string; } -// Warning: (ae-missing-release-tag) "RouterOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @alpha (undocumented) export interface RouterOptions { // (undocumented) clusterSupplier?: KubernetesClustersSupplier; @@ -290,13 +275,9 @@ export interface RouterOptions { logger: Logger; } -// Warning: (ae-missing-release-tag) "ServiceAccountClusterDetails" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @alpha (undocumented) export interface ServiceAccountClusterDetails extends ClusterDetails {} -// Warning: (ae-missing-release-tag) "ServiceLocatorMethod" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @alpha (undocumented) export type ServiceLocatorMethod = 'multiTenant' | 'http'; ``` diff --git a/plugins/kubernetes-backend/package.json b/plugins/kubernetes-backend/package.json index 973808a157..33dcc1dfff 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.5.1", + "version": "0.6.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -36,12 +36,12 @@ }, "dependencies": { "@azure/identity": "^2.0.4", - "@backstage/backend-common": "^0.13.3", - "@backstage/catalog-model": "^1.0.2", + "@backstage/backend-common": "^0.14.0", + "@backstage/catalog-model": "^1.0.3", "@backstage/config": "^1.0.1", "@backstage/errors": "^1.0.0", - "@backstage/plugin-kubernetes-common": "^0.2.10", - "@google-cloud/container": "^3.0.0", + "@backstage/plugin-kubernetes-common": "^0.3.0", + "@google-cloud/container": "^4.0.0", "@kubernetes/client-node": "^0.16.0", "@types/express": "^4.17.6", "@types/luxon": "^2.0.4", @@ -61,7 +61,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", + "@backstage/cli": "^0.17.2", "@types/aws4": "^1.5.1", "aws-sdk-mock": "^5.2.1", "supertest": "^6.1.3" diff --git a/plugins/kubernetes-backend/src/kubernetes-auth-translator/AzureIdentityKubernetesAuthTranslator.test.ts b/plugins/kubernetes-backend/src/kubernetes-auth-translator/AzureIdentityKubernetesAuthTranslator.test.ts new file mode 100644 index 0000000000..a28f825434 --- /dev/null +++ b/plugins/kubernetes-backend/src/kubernetes-auth-translator/AzureIdentityKubernetesAuthTranslator.test.ts @@ -0,0 +1,130 @@ +/* + * 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 { AccessToken, TokenCredential } from '@azure/identity'; +import { getVoidLogger } from '@backstage/backend-common'; +import { AzureIdentityKubernetesAuthTranslator } from './AzureIdentityKubernetesAuthTranslator'; + +const logger = getVoidLogger(); + +class StaticTokenCredential implements TokenCredential { + private count: number = 0; + + constructor(private expiryInMs: number) {} + + getToken(): Promise { + this.count++; + + if (this.count === 3) { + return Promise.reject(new Error('Third time never works.')); + } + + return Promise.resolve({ + token: `MY_TOKEN_${this.count}`, + expiresOnTimestamp: Date.now() + this.expiryInMs, + }); + } +} + +describe('AzureIdentityKubernetesAuthTranslator tests', () => { + const cd = { + authProvider: 'Azure', + name: 'My Cluster', + url: 'mycluster.privatelink.westeurope.azmk8s.io', + }; + + it('should decorate cluster with Azure token', async () => { + const authTranslator = new AzureIdentityKubernetesAuthTranslator( + logger, + new StaticTokenCredential(5 * 60 * 1000), + ); + + const response = await authTranslator.decorateClusterDetailsWithAuth(cd); + expect(response.serviceAccountToken).toEqual('MY_TOKEN_1'); + }); + + it('should re-use token before expiry', async () => { + const authTranslator = new AzureIdentityKubernetesAuthTranslator( + logger, + new StaticTokenCredential(20 * 60 * 1000), + ); + + const response = await authTranslator.decorateClusterDetailsWithAuth(cd); + expect(response.serviceAccountToken).toEqual('MY_TOKEN_1'); + + const response2 = await authTranslator.decorateClusterDetailsWithAuth(cd); + expect(response2.serviceAccountToken).toEqual('MY_TOKEN_1'); + }); + + it('should issue new token 15 minutes befory expiry', async () => { + const authTranslator = new AzureIdentityKubernetesAuthTranslator( + logger, + new StaticTokenCredential(16 * 60 * 1000), // token expires in 16min + ); + + const response = await authTranslator.decorateClusterDetailsWithAuth(cd); + expect(response.serviceAccountToken).toEqual('MY_TOKEN_1'); + + jest.useFakeTimers().setSystemTime(Date.now() + 2 * 60 * 1000); // advance time by 2mins + + const response2 = await authTranslator.decorateClusterDetailsWithAuth(cd); + expect(response2.serviceAccountToken).toEqual('MY_TOKEN_2'); + }); + + it('should re-use existing token if there is afailure', async () => { + const authTranslator = new AzureIdentityKubernetesAuthTranslator( + logger, + new StaticTokenCredential(16 * 60 * 1000), // new tokens expires in 16min + ); + + const response = await authTranslator.decorateClusterDetailsWithAuth(cd); + expect(response.serviceAccountToken).toEqual('MY_TOKEN_1'); + + jest.useFakeTimers().setSystemTime(Date.now() + 2 * 60 * 1000); // advance time by 2min + + const response2 = await authTranslator.decorateClusterDetailsWithAuth(cd); + expect(response2.serviceAccountToken).toEqual('MY_TOKEN_2'); + + jest.useFakeTimers().setSystemTime(Date.now() + 2 * 60 * 1000); // advance time by 2min + + const response3 = await authTranslator.decorateClusterDetailsWithAuth(cd); + expect(response3.serviceAccountToken).toEqual('MY_TOKEN_2'); + + const response4 = await authTranslator.decorateClusterDetailsWithAuth(cd); + expect(response4.serviceAccountToken).toEqual('MY_TOKEN_4'); + }); + + it('should throw if existing token expired and failed to fetch a new one', async () => { + const authTranslator = new AzureIdentityKubernetesAuthTranslator( + logger, + new StaticTokenCredential(16 * 60 * 1000), // new tokens expires in 16min + ); + + const response = await authTranslator.decorateClusterDetailsWithAuth(cd); + expect(response.serviceAccountToken).toEqual('MY_TOKEN_1'); + + jest.useFakeTimers().setSystemTime(Date.now() + 2 * 60 * 1000); // advance time by 2min + + const response2 = await authTranslator.decorateClusterDetailsWithAuth(cd); + expect(response2.serviceAccountToken).toEqual('MY_TOKEN_2'); + + jest.useFakeTimers().setSystemTime(Date.now() + 17 * 60 * 1000); // advance time by 17min + + await expect( + authTranslator.decorateClusterDetailsWithAuth(cd), + ).rejects.toThrow(); + }); +}); diff --git a/plugins/kubernetes-backend/src/kubernetes-auth-translator/AzureIdentityKubernetesAuthTranslator.ts b/plugins/kubernetes-backend/src/kubernetes-auth-translator/AzureIdentityKubernetesAuthTranslator.ts index 20b519c269..5b8a709270 100644 --- a/plugins/kubernetes-backend/src/kubernetes-auth-translator/AzureIdentityKubernetesAuthTranslator.ts +++ b/plugins/kubernetes-backend/src/kubernetes-auth-translator/AzureIdentityKubernetesAuthTranslator.ts @@ -14,15 +14,28 @@ * limitations under the License. */ +import { Logger } from 'winston'; import { KubernetesAuthTranslator } from './types'; import { AzureClusterDetails } from '../types/types'; -import { DefaultAzureCredential } from '@azure/identity'; +import { + AccessToken, + DefaultAzureCredential, + TokenCredential, +} from '@azure/identity'; const aksScope = '6dae42f8-4368-4678-94ff-3960e28e3630/.default'; // This scope is the same for all Azure Managed Kubernetes export class AzureIdentityKubernetesAuthTranslator implements KubernetesAuthTranslator { + private accessToken: AccessToken = { token: '', expiresOnTimestamp: 0 }; + private newTokenPromise: Promise | undefined; + + constructor( + private readonly logger: Logger, + private readonly tokenCredential: TokenCredential = new DefaultAzureCredential(), + ) {} + async decorateClusterDetailsWithAuth( clusterDetails: AzureClusterDetails, ): Promise { @@ -31,11 +44,54 @@ export class AzureIdentityKubernetesAuthTranslator clusterDetails, ); - const credentials = new DefaultAzureCredential(); - - // TODO: can we cache this? It's inneficiant to get a new token every time - const accessToken = await credentials.getToken(aksScope); - clusterDetailsWithAuthToken.serviceAccountToken = accessToken.token; + clusterDetailsWithAuthToken.serviceAccountToken = await this.getToken(); return clusterDetailsWithAuthToken; } + + private async getToken(): Promise { + if (!this.tokenRequiresRefresh()) { + return this.accessToken.token; + } + + if (!this.newTokenPromise) { + this.newTokenPromise = this.fetchNewToken(); + } + + return this.newTokenPromise; + } + + private async fetchNewToken(): Promise { + try { + this.logger.info('Fetching new Azure token for AKS'); + + const newAccessToken = await this.tokenCredential.getToken(aksScope, { + requestOptions: { timeout: 10_000 }, // 10 seconds + }); + if (!newAccessToken) { + throw new Error('AccessToken is null'); + } + + this.accessToken = newAccessToken; + } catch (err) { + this.logger.error('Unable to fetch Azure token', err); + + // only throw the error if the token has already expired, otherwise re-use existing until we're able to fetch a new token + if (this.tokenExpired()) { + throw err; + } + } + + this.newTokenPromise = undefined; + return this.accessToken.token; + } + + private tokenRequiresRefresh(): boolean { + // Set tokens to expire 15 minutes before its actual expiry time + const expiresOn = this.accessToken.expiresOnTimestamp - 15 * 60 * 1000; + return Date.now() >= expiresOn; + } + + private tokenExpired(): boolean { + return Date.now() >= this.accessToken.expiresOnTimestamp; + } } diff --git a/plugins/kubernetes-backend/src/kubernetes-auth-translator/GoogleKubernetesAuthTranslator.ts b/plugins/kubernetes-backend/src/kubernetes-auth-translator/GoogleKubernetesAuthTranslator.ts index 8c74945ec7..741e6b2533 100644 --- a/plugins/kubernetes-backend/src/kubernetes-auth-translator/GoogleKubernetesAuthTranslator.ts +++ b/plugins/kubernetes-backend/src/kubernetes-auth-translator/GoogleKubernetesAuthTranslator.ts @@ -16,20 +16,20 @@ import { KubernetesAuthTranslator } from './types'; import { GKEClusterDetails } from '../types/types'; -import { KubernetesRequestBody } from '@backstage/plugin-kubernetes-common'; +import { KubernetesRequestAuth } from '@backstage/plugin-kubernetes-common'; export class GoogleKubernetesAuthTranslator implements KubernetesAuthTranslator { async decorateClusterDetailsWithAuth( clusterDetails: GKEClusterDetails, - requestBody: KubernetesRequestBody, + authConfig: KubernetesRequestAuth, ): Promise { const clusterDetailsWithAuthToken: GKEClusterDetails = Object.assign( {}, clusterDetails, ); - const authToken: string | undefined = requestBody.auth?.google; + const authToken: string | undefined = authConfig.google; if (authToken) { clusterDetailsWithAuthToken.serviceAccountToken = authToken; diff --git a/plugins/kubernetes-backend/src/kubernetes-auth-translator/KubernetesAuthTranslatorGenerator.test.ts b/plugins/kubernetes-backend/src/kubernetes-auth-translator/KubernetesAuthTranslatorGenerator.test.ts index b31443cdd1..f8abdde9cf 100644 --- a/plugins/kubernetes-backend/src/kubernetes-auth-translator/KubernetesAuthTranslatorGenerator.test.ts +++ b/plugins/kubernetes-backend/src/kubernetes-auth-translator/KubernetesAuthTranslatorGenerator.test.ts @@ -17,41 +17,44 @@ import { KubernetesAuthTranslator } from './types'; import { GoogleKubernetesAuthTranslator } from './GoogleKubernetesAuthTranslator'; import { KubernetesAuthTranslatorGenerator } from './KubernetesAuthTranslatorGenerator'; -import { ServiceAccountKubernetesAuthTranslator } from './ServiceAccountKubernetesAuthTranslator'; +import { NoopKubernetesAuthTranslator } from './NoopKubernetesAuthTranslator'; import { AwsIamKubernetesAuthTranslator } from './AwsIamKubernetesAuthTranslator'; import { OidcKubernetesAuthTranslator } from './OidcKubernetesAuthTranslator'; +import { getVoidLogger } from '@backstage/backend-common'; + +const logger = getVoidLogger(); describe('getKubernetesAuthTranslatorInstance', () => { const sut = KubernetesAuthTranslatorGenerator; it('can return an auth translator for google auth', () => { const authTranslator: KubernetesAuthTranslator = - sut.getKubernetesAuthTranslatorInstance('google'); + sut.getKubernetesAuthTranslatorInstance('google', { logger }); expect(authTranslator instanceof GoogleKubernetesAuthTranslator).toBe(true); }); it('can return an auth translator for aws auth', () => { const authTranslator: KubernetesAuthTranslator = - sut.getKubernetesAuthTranslatorInstance('aws'); + sut.getKubernetesAuthTranslatorInstance('aws', { logger }); expect(authTranslator instanceof AwsIamKubernetesAuthTranslator).toBe(true); }); it('can return an auth translator for serviceAccount auth', () => { const authTranslator: KubernetesAuthTranslator = - sut.getKubernetesAuthTranslatorInstance('serviceAccount'); - expect( - authTranslator instanceof ServiceAccountKubernetesAuthTranslator, - ).toBe(true); + sut.getKubernetesAuthTranslatorInstance('serviceAccount', { logger }); + expect(authTranslator instanceof NoopKubernetesAuthTranslator).toBe(true); }); it('can return an auth translator for oidc auth', () => { const authTranslator: KubernetesAuthTranslator = - sut.getKubernetesAuthTranslatorInstance('oidc'); + sut.getKubernetesAuthTranslatorInstance('oidc', { logger }); expect(authTranslator instanceof OidcKubernetesAuthTranslator).toBe(true); }); it('throws an error when asked for an auth translator for an unsupported auth type', () => { - expect(() => sut.getKubernetesAuthTranslatorInstance('linode')).toThrow( + expect(() => + sut.getKubernetesAuthTranslatorInstance('linode', { logger }), + ).toThrow( 'authProvider "linode" has no KubernetesAuthTranslator associated with it', ); }); diff --git a/plugins/kubernetes-backend/src/kubernetes-auth-translator/KubernetesAuthTranslatorGenerator.ts b/plugins/kubernetes-backend/src/kubernetes-auth-translator/KubernetesAuthTranslatorGenerator.ts index 4b17653359..c2df3e6c23 100644 --- a/plugins/kubernetes-backend/src/kubernetes-auth-translator/KubernetesAuthTranslatorGenerator.ts +++ b/plugins/kubernetes-backend/src/kubernetes-auth-translator/KubernetesAuthTranslatorGenerator.ts @@ -14,9 +14,10 @@ * limitations under the License. */ +import { Logger } from 'winston'; import { KubernetesAuthTranslator } from './types'; import { GoogleKubernetesAuthTranslator } from './GoogleKubernetesAuthTranslator'; -import { ServiceAccountKubernetesAuthTranslator } from './ServiceAccountKubernetesAuthTranslator'; +import { NoopKubernetesAuthTranslator } from './NoopKubernetesAuthTranslator'; import { AwsIamKubernetesAuthTranslator } from './AwsIamKubernetesAuthTranslator'; import { GoogleServiceAccountAuthTranslator } from './GoogleServiceAccountAuthProvider'; import { AzureIdentityKubernetesAuthTranslator } from './AzureIdentityKubernetesAuthTranslator'; @@ -25,6 +26,9 @@ import { OidcKubernetesAuthTranslator } from './OidcKubernetesAuthTranslator'; export class KubernetesAuthTranslatorGenerator { static getKubernetesAuthTranslatorInstance( authProvider: string, + options: { + logger: Logger; + }, ): KubernetesAuthTranslator { switch (authProvider) { case 'google': { @@ -34,10 +38,10 @@ export class KubernetesAuthTranslatorGenerator { return new AwsIamKubernetesAuthTranslator(); } case 'azure': { - return new AzureIdentityKubernetesAuthTranslator(); + return new AzureIdentityKubernetesAuthTranslator(options.logger); } case 'serviceAccount': { - return new ServiceAccountKubernetesAuthTranslator(); + return new NoopKubernetesAuthTranslator(); } case 'googleServiceAccount': { return new GoogleServiceAccountAuthTranslator(); diff --git a/plugins/kubernetes-backend/src/kubernetes-auth-translator/ServiceAccountKubernetesAuthTranslator.ts b/plugins/kubernetes-backend/src/kubernetes-auth-translator/NoopKubernetesAuthTranslator.ts similarity index 80% rename from plugins/kubernetes-backend/src/kubernetes-auth-translator/ServiceAccountKubernetesAuthTranslator.ts rename to plugins/kubernetes-backend/src/kubernetes-auth-translator/NoopKubernetesAuthTranslator.ts index ec90f82c91..d68b1a4365 100644 --- a/plugins/kubernetes-backend/src/kubernetes-auth-translator/ServiceAccountKubernetesAuthTranslator.ts +++ b/plugins/kubernetes-backend/src/kubernetes-auth-translator/NoopKubernetesAuthTranslator.ts @@ -16,14 +16,10 @@ import { KubernetesAuthTranslator } from './types'; import { ServiceAccountClusterDetails } from '../types/types'; -import { KubernetesRequestBody } from '@backstage/plugin-kubernetes-common'; -export class ServiceAccountKubernetesAuthTranslator - implements KubernetesAuthTranslator -{ +export class NoopKubernetesAuthTranslator implements KubernetesAuthTranslator { async decorateClusterDetailsWithAuth( clusterDetails: ServiceAccountClusterDetails, - _requestBody: KubernetesRequestBody, ): Promise { return clusterDetails; } diff --git a/plugins/kubernetes-backend/src/kubernetes-auth-translator/OidcKubernetesAuthTranslator.test.ts b/plugins/kubernetes-backend/src/kubernetes-auth-translator/OidcKubernetesAuthTranslator.test.ts index 9f983aedc3..68f24bd020 100644 --- a/plugins/kubernetes-backend/src/kubernetes-auth-translator/OidcKubernetesAuthTranslator.test.ts +++ b/plugins/kubernetes-backend/src/kubernetes-auth-translator/OidcKubernetesAuthTranslator.test.ts @@ -16,15 +16,9 @@ import { OidcKubernetesAuthTranslator } from './OidcKubernetesAuthTranslator'; import { ClusterDetails } from '../types/types'; -import { Entity } from '@backstage/catalog-model'; describe('OidcKubernetesAuthTranslator tests', () => { const at = new OidcKubernetesAuthTranslator(); - const entity: Entity = { - apiVersion: 'v1', - kind: 'service', - metadata: { name: 'test' }, - }; const baseClusterDetails: ClusterDetails = { name: 'test', authProvider: 'oidc', @@ -38,10 +32,7 @@ describe('OidcKubernetesAuthTranslator tests', () => { ...baseClusterDetails, }, { - auth: { - oidc: { okta: 'fakeToken' }, - }, - entity, + oidc: { okta: 'fakeToken' }, }, ); @@ -50,7 +41,7 @@ describe('OidcKubernetesAuthTranslator tests', () => { it('returns error when oidcTokenProvider is not configured', async () => { await expect( - at.decorateClusterDetailsWithAuth(baseClusterDetails, { entity }), + at.decorateClusterDetailsWithAuth(baseClusterDetails, {}), ).rejects.toThrow( 'oidc authProvider requires a configured oidcTokenProvider', ); @@ -60,7 +51,7 @@ describe('OidcKubernetesAuthTranslator tests', () => { await expect( at.decorateClusterDetailsWithAuth( { oidcTokenProvider: 'okta', ...baseClusterDetails }, - { entity }, + {}, ), ).rejects.toThrow('Auth token not found under oidc.okta in request body'); }); diff --git a/plugins/kubernetes-backend/src/kubernetes-auth-translator/OidcKubernetesAuthTranslator.ts b/plugins/kubernetes-backend/src/kubernetes-auth-translator/OidcKubernetesAuthTranslator.ts index c53269abf3..8ca546a25b 100644 --- a/plugins/kubernetes-backend/src/kubernetes-auth-translator/OidcKubernetesAuthTranslator.ts +++ b/plugins/kubernetes-backend/src/kubernetes-auth-translator/OidcKubernetesAuthTranslator.ts @@ -16,12 +16,12 @@ import { KubernetesAuthTranslator } from './types'; import { ClusterDetails } from '../types/types'; -import { KubernetesRequestBody } from '@backstage/plugin-kubernetes-common'; +import { KubernetesRequestAuth } from '@backstage/plugin-kubernetes-common'; export class OidcKubernetesAuthTranslator implements KubernetesAuthTranslator { async decorateClusterDetailsWithAuth( clusterDetails: ClusterDetails, - requestBody: KubernetesRequestBody, + authConfig: KubernetesRequestAuth, ): Promise { const clusterDetailsWithAuthToken: ClusterDetails = Object.assign( {}, @@ -36,8 +36,7 @@ export class OidcKubernetesAuthTranslator implements KubernetesAuthTranslator { ); } - const authToken: string | undefined = - requestBody.auth?.oidc?.[oidcTokenProvider]; + const authToken: string | undefined = authConfig.oidc?.[oidcTokenProvider]; if (authToken) { clusterDetailsWithAuthToken.serviceAccountToken = authToken; diff --git a/plugins/kubernetes-backend/src/kubernetes-auth-translator/types.ts b/plugins/kubernetes-backend/src/kubernetes-auth-translator/types.ts index f14ba877b7..640a20303e 100644 --- a/plugins/kubernetes-backend/src/kubernetes-auth-translator/types.ts +++ b/plugins/kubernetes-backend/src/kubernetes-auth-translator/types.ts @@ -15,11 +15,11 @@ */ import { ClusterDetails } from '../types/types'; -import { KubernetesRequestBody } from '@backstage/plugin-kubernetes-common'; +import { KubernetesRequestAuth } from '@backstage/plugin-kubernetes-common'; export interface KubernetesAuthTranslator { decorateClusterDetailsWithAuth( clusterDetails: ClusterDetails, - requestBody: KubernetesRequestBody, + authConfig: KubernetesRequestAuth, ): Promise; } diff --git a/plugins/kubernetes-backend/src/service-locator/MultiTenantServiceLocator.test.ts b/plugins/kubernetes-backend/src/service-locator/MultiTenantServiceLocator.test.ts index 0dce2305bf..2ead460848 100644 --- a/plugins/kubernetes-backend/src/service-locator/MultiTenantServiceLocator.test.ts +++ b/plugins/kubernetes-backend/src/service-locator/MultiTenantServiceLocator.test.ts @@ -15,6 +15,7 @@ */ import '@backstage/backend-common'; +import { Entity } from '@backstage/catalog-model'; import { MultiTenantServiceLocator } from './MultiTenantServiceLocator'; describe('MultiTenantConfigClusterLocator', () => { @@ -23,9 +24,9 @@ describe('MultiTenantConfigClusterLocator', () => { getClusters: async () => [], }); - const result = await sut.getClustersByServiceId('ignored'); + const result = await sut.getClustersByEntity({} as Entity); - expect(result).toStrictEqual([]); + expect(result).toStrictEqual({ clusters: [] }); }); it('one clusters returns one cluster details', async () => { @@ -42,16 +43,18 @@ describe('MultiTenantConfigClusterLocator', () => { }, }); - const result = await sut.getClustersByServiceId('ignored'); + const result = await sut.getClustersByEntity({} as Entity); - expect(result).toStrictEqual([ - { - name: 'cluster1', - serviceAccountToken: '12345', - url: 'http://localhost:8080', - authProvider: 'serviceAccount', - }, - ]); + expect(result).toStrictEqual({ + clusters: [ + { + name: 'cluster1', + serviceAccountToken: '12345', + url: 'http://localhost:8080', + authProvider: 'serviceAccount', + }, + ], + }); }); it('two clusters returns two cluster details', async () => { @@ -73,20 +76,22 @@ describe('MultiTenantConfigClusterLocator', () => { }, }); - const result = await sut.getClustersByServiceId('ignored'); + const result = await sut.getClustersByEntity({} as Entity); - expect(result).toStrictEqual([ - { - name: 'cluster1', - serviceAccountToken: 'token', - url: 'http://localhost:8080', - authProvider: 'serviceAccount', - }, - { - name: 'cluster2', - url: 'http://localhost:8081', - authProvider: 'google', - }, - ]); + expect(result).toStrictEqual({ + clusters: [ + { + name: 'cluster1', + serviceAccountToken: 'token', + url: 'http://localhost:8080', + authProvider: 'serviceAccount', + }, + { + name: 'cluster2', + url: 'http://localhost:8081', + authProvider: 'google', + }, + ], + }); }); }); diff --git a/plugins/kubernetes-backend/src/service-locator/MultiTenantServiceLocator.ts b/plugins/kubernetes-backend/src/service-locator/MultiTenantServiceLocator.ts index 30c66a38d1..7bafbb10f3 100644 --- a/plugins/kubernetes-backend/src/service-locator/MultiTenantServiceLocator.ts +++ b/plugins/kubernetes-backend/src/service-locator/MultiTenantServiceLocator.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import { Entity } from '@backstage/catalog-model'; import { ClusterDetails, KubernetesClustersSupplier, @@ -30,8 +31,9 @@ export class MultiTenantServiceLocator implements KubernetesServiceLocator { } // As this implementation always returns all clusters serviceId is ignored here - // eslint-disable-next-line @typescript-eslint/no-unused-vars - async getClustersByServiceId(_serviceId: string): Promise { - return this.clusterSupplier.getClusters(); + getClustersByEntity( + _entity: Entity, + ): Promise<{ clusters: ClusterDetails[] }> { + return this.clusterSupplier.getClusters().then(clusters => ({ clusters })); } } diff --git a/plugins/kubernetes-backend/src/service/KubernetesBuilder.test.ts b/plugins/kubernetes-backend/src/service/KubernetesBuilder.test.ts index 37dad8c3e4..ecaf9f3095 100644 --- a/plugins/kubernetes-backend/src/service/KubernetesBuilder.test.ts +++ b/plugins/kubernetes-backend/src/service/KubernetesBuilder.test.ts @@ -15,6 +15,7 @@ */ import { getVoidLogger } from '@backstage/backend-common'; +import { Entity } from '@backstage/catalog-model'; import { Config, ConfigReader } from '@backstage/config'; import { ObjectsByEntityResponse } from '@backstage/plugin-kubernetes-common'; import express from 'express'; @@ -207,8 +208,10 @@ describe('KubernetesBuilder', () => { }; const serviceLocator: KubernetesServiceLocator = { - getClustersByServiceId(_serviceId: string): Promise { - return Promise.resolve([someCluster]); + getClustersByEntity( + _entity: Entity, + ): Promise<{ clusters: ClusterDetails[] }> { + return Promise.resolve({ clusters: [someCluster] }); }, }; @@ -244,7 +247,15 @@ describe('KubernetesBuilder', () => { .build(); app = express().use(router); - const response = await request(app).post('/services/test-service'); + const response = await request(app) + .post('/services/test-service') + .send({ + entity: { + metadata: { + name: 'thing', + }, + }, + }); expect(response.body).toEqual(result); expect(response.status).toEqual(200); diff --git a/plugins/kubernetes-backend/src/service/KubernetesBuilder.ts b/plugins/kubernetes-backend/src/service/KubernetesBuilder.ts index 18c3fdc931..1b058752c9 100644 --- a/plugins/kubernetes-backend/src/service/KubernetesBuilder.ts +++ b/plugins/kubernetes-backend/src/service/KubernetesBuilder.ts @@ -38,6 +38,10 @@ import { } from './KubernetesFanOutHandler'; import { KubernetesClientBasedFetcher } from './KubernetesFetcher'; +/** + * + * @alpha + */ export interface KubernetesEnvironment { logger: Logger; config: Config; @@ -46,7 +50,7 @@ export interface KubernetesEnvironment { /** * The return type of the `KubernetesBuilder.build` method * - * @public + * @alpha */ export type KubernetesBuilderReturn = Promise<{ router: express.Router; @@ -57,6 +61,10 @@ export type KubernetesBuilderReturn = Promise<{ serviceLocator: KubernetesServiceLocator; }>; +/** + * + * @alpha + */ export class KubernetesBuilder { private clusterSupplier?: KubernetesClustersSupplier; private defaultClusterRefreshInterval: Duration = Duration.fromObject({ @@ -227,9 +235,10 @@ export class KubernetesBuilder { const serviceId = req.params.serviceId; const requestBody: ObjectsByEntityRequest = req.body; try { - const response = await objectsProvider.getKubernetesObjectsByEntity( - requestBody, - ); + const response = await objectsProvider.getKubernetesObjectsByEntity({ + entity: requestBody.entity, + auth: requestBody.auth || {}, + }); res.json(response); } catch (e) { logger.error( diff --git a/plugins/kubernetes-backend/src/service/KubernetesFanOutHandler.test.ts b/plugins/kubernetes-backend/src/service/KubernetesFanOutHandler.test.ts index da8f703ba0..176498839d 100644 --- a/plugins/kubernetes-backend/src/service/KubernetesFanOutHandler.test.ts +++ b/plugins/kubernetes-backend/src/service/KubernetesFanOutHandler.test.ts @@ -22,7 +22,7 @@ import { PodStatus } from '@kubernetes/client-node/dist/top'; const fetchObjectsForService = jest.fn(); const fetchPodMetricsByNamespace = jest.fn(); -const getClustersByServiceId = jest.fn(); +const getClustersByEntity = jest.fn(); const POD_METRICS_FIXTURE = { containers: [], @@ -166,13 +166,15 @@ describe('handleGetKubernetesObjectsForService', () => { }); it('retrieve objects for one cluster', async () => { - getClustersByServiceId.mockImplementation(() => - Promise.resolve([ - { - name: 'test-cluster', - authProvider: 'serviceAccount', - }, - ]), + getClustersByEntity.mockImplementation(() => + Promise.resolve({ + clusters: [ + { + name: 'test-cluster', + authProvider: 'serviceAccount', + }, + ], + }), ); mockFetch(fetchObjectsForService); @@ -185,7 +187,7 @@ describe('handleGetKubernetesObjectsForService', () => { fetchPodMetricsByNamespace, }, serviceLocator: { - getClustersByServiceId, + getClustersByEntity, }, customResources: [], }); @@ -207,9 +209,10 @@ describe('handleGetKubernetesObjectsForService', () => { owner: 'joe', }, }, + auth: {}, }); - expect(getClustersByServiceId.mock.calls.length).toBe(1); + expect(getClustersByEntity.mock.calls.length).toBe(1); expect(fetchObjectsForService.mock.calls.length).toBe(1); expect(fetchPodMetricsByNamespace.mock.calls.length).toBe(1); expect(fetchPodMetricsByNamespace.mock.calls[0][1]).toBe( @@ -265,13 +268,15 @@ describe('handleGetKubernetesObjectsForService', () => { }); it('dont call top for the same namespace twice', async () => { - getClustersByServiceId.mockImplementation(() => - Promise.resolve([ - { - name: 'test-cluster', - authProvider: 'serviceAccount', - }, - ]), + getClustersByEntity.mockImplementation(() => + Promise.resolve({ + clusters: [ + { + name: 'test-cluster', + authProvider: 'serviceAccount', + }, + ], + }), ); fetchObjectsForService.mockImplementation((_: ObjectFetchParams) => @@ -314,7 +319,7 @@ describe('handleGetKubernetesObjectsForService', () => { fetchPodMetricsByNamespace, }, serviceLocator: { - getClustersByServiceId, + getClustersByEntity, }, customResources: [], }); @@ -336,9 +341,10 @@ describe('handleGetKubernetesObjectsForService', () => { owner: 'joe', }, }, + auth: {}, }); - expect(getClustersByServiceId.mock.calls.length).toBe(1); + expect(getClustersByEntity.mock.calls.length).toBe(1); expect(fetchObjectsForService.mock.calls.length).toBe(1); expect(fetchPodMetricsByNamespace.mock.calls.length).toBe(2); expect(fetchPodMetricsByNamespace.mock.calls[0][1]).toBe('ns-a'); @@ -383,18 +389,20 @@ describe('handleGetKubernetesObjectsForService', () => { }); it('retrieve objects for two clusters', async () => { - getClustersByServiceId.mockImplementation(() => - Promise.resolve([ - { - name: 'test-cluster', - authProvider: 'serviceAccount', - dashboardUrl: 'https://k8s.foo.coom', - }, - { - name: 'other-cluster', - authProvider: 'google', - }, - ]), + getClustersByEntity.mockImplementation(() => + Promise.resolve({ + clusters: [ + { + name: 'test-cluster', + authProvider: 'serviceAccount', + dashboardUrl: 'https://k8s.foo.coom', + }, + { + name: 'other-cluster', + authProvider: 'google', + }, + ], + }), ); mockFetch(fetchObjectsForService); @@ -407,15 +415,12 @@ describe('handleGetKubernetesObjectsForService', () => { fetchPodMetricsByNamespace, }, serviceLocator: { - getClustersByServiceId, + getClustersByEntity, }, customResources: [], }); const result = await sut.getKubernetesObjectsByEntity({ - auth: { - google: 'google_token_123', - }, entity: { apiVersion: 'backstage.io/v1beta1', kind: 'Component', @@ -432,9 +437,12 @@ describe('handleGetKubernetesObjectsForService', () => { owner: 'joe', }, }, + auth: { + google: 'google_token_123', + }, }); - expect(getClustersByServiceId.mock.calls.length).toBe(1); + expect(getClustersByEntity.mock.calls.length).toBe(1); expect(fetchObjectsForService.mock.calls.length).toBe(2); expect(result).toStrictEqual({ items: [ @@ -527,21 +535,23 @@ describe('handleGetKubernetesObjectsForService', () => { }); }); it('retrieve objects for three clusters, only two have resources and show in ui', async () => { - getClustersByServiceId.mockImplementation(() => - Promise.resolve([ - { - name: 'test-cluster', - authProvider: 'serviceAccount', - }, - { - name: 'other-cluster', - authProvider: 'google', - }, - { - name: 'empty-cluster', - authProvider: 'google', - }, - ]), + getClustersByEntity.mockImplementation(() => + Promise.resolve({ + clusters: [ + { + name: 'test-cluster', + authProvider: 'serviceAccount', + }, + { + name: 'other-cluster', + authProvider: 'google', + }, + { + name: 'empty-cluster', + authProvider: 'google', + }, + ], + }), ); mockFetch(fetchObjectsForService); @@ -554,15 +564,12 @@ describe('handleGetKubernetesObjectsForService', () => { fetchPodMetricsByNamespace, }, serviceLocator: { - getClustersByServiceId, + getClustersByEntity, }, customResources: [], }); const result = await sut.getKubernetesObjectsByEntity({ - auth: { - google: 'google_token_123', - }, entity: { apiVersion: 'backstage.io/v1beta1', kind: 'Component', @@ -579,9 +586,12 @@ describe('handleGetKubernetesObjectsForService', () => { owner: 'joe', }, }, + auth: { + google: 'google_token_123', + }, }); - expect(getClustersByServiceId.mock.calls.length).toBe(1); + expect(getClustersByEntity.mock.calls.length).toBe(1); expect(fetchObjectsForService.mock.calls.length).toBe(3); expect(result).toStrictEqual({ items: [ @@ -673,25 +683,27 @@ describe('handleGetKubernetesObjectsForService', () => { }); }); it('retrieve objects for four clusters, two have resources and one error cluster', async () => { - getClustersByServiceId.mockImplementation(() => - Promise.resolve([ - { - name: 'test-cluster', - authProvider: 'serviceAccount', - }, - { - name: 'other-cluster', - authProvider: 'google', - }, - { - name: 'empty-cluster', - authProvider: 'google', - }, - { - name: 'error-cluster', - authProvider: 'google', - }, - ]), + getClustersByEntity.mockImplementation(() => + Promise.resolve({ + clusters: [ + { + name: 'test-cluster', + authProvider: 'serviceAccount', + }, + { + name: 'other-cluster', + authProvider: 'google', + }, + { + name: 'empty-cluster', + authProvider: 'google', + }, + { + name: 'error-cluster', + authProvider: 'google', + }, + ], + }), ); mockFetch(fetchObjectsForService); @@ -704,15 +716,12 @@ describe('handleGetKubernetesObjectsForService', () => { fetchPodMetricsByNamespace, }, serviceLocator: { - getClustersByServiceId, + getClustersByEntity, }, customResources: [], }); const result = await sut.getKubernetesObjectsByEntity({ - auth: { - google: 'google_token_123', - }, entity: { apiVersion: 'backstage.io/v1beta1', kind: 'Component', @@ -729,9 +738,12 @@ describe('handleGetKubernetesObjectsForService', () => { owner: 'joe', }, }, + auth: { + google: 'google_token_123', + }, }); - expect(getClustersByServiceId.mock.calls.length).toBe(1); + expect(getClustersByEntity.mock.calls.length).toBe(1); expect(fetchObjectsForService.mock.calls.length).toBe(4); expect(result).toStrictEqual({ items: [ diff --git a/plugins/kubernetes-backend/src/service/KubernetesFanOutHandler.ts b/plugins/kubernetes-backend/src/service/KubernetesFanOutHandler.ts index 6ce98d1f5f..ac2ffaf2ed 100644 --- a/plugins/kubernetes-backend/src/service/KubernetesFanOutHandler.ts +++ b/plugins/kubernetes-backend/src/service/KubernetesFanOutHandler.ts @@ -14,16 +14,20 @@ * limitations under the License. */ +import { Entity } from '@backstage/catalog-model'; import { Logger } from 'winston'; import { ClusterDetails, - CustomResource, KubernetesFetcher, KubernetesObjectsProviderOptions, KubernetesServiceLocator, ObjectsByEntityRequest, FetchResponseWrapper, ObjectToFetch, + CustomResource, + CustomResourceMatcher, + CustomResourcesByEntity, + KubernetesObjectsByEntity, } from '../types/types'; import { KubernetesAuthTranslator } from '../kubernetes-auth-translator/types'; import { KubernetesAuthTranslatorGenerator } from '../kubernetes-auth-translator/KubernetesAuthTranslatorGenerator'; @@ -35,6 +39,7 @@ import { FetchResponse, ObjectsByEntityResponse, PodFetchResponse, + KubernetesRequestAuth, } from '@backstage/plugin-kubernetes-common'; import { ContainerStatus, @@ -42,6 +47,10 @@ import { PodStatus, } from '@kubernetes/client-node'; +/** + * + * @alpha + */ export const DEFAULT_OBJECTS: ObjectToFetch[] = [ { group: '', @@ -161,6 +170,7 @@ export class KubernetesFanOutHandler { private readonly serviceLocator: KubernetesServiceLocator; private readonly customResources: CustomResource[]; private readonly objectTypesToFetch: Set; + private readonly authTranslators: Record; constructor({ logger, @@ -174,33 +184,47 @@ export class KubernetesFanOutHandler { this.serviceLocator = serviceLocator; this.customResources = customResources; this.objectTypesToFetch = new Set(objectTypesToFetch); + this.authTranslators = {}; } - async getKubernetesObjectsByEntity( - requestBody: KubernetesRequestBody, - ): Promise { - const entityName = - requestBody.entity?.metadata?.annotations?.[ - 'backstage.io/kubernetes-id' - ] || requestBody.entity?.metadata?.name; - - const clusterDetails: ClusterDetails[] = - await this.serviceLocator.getClustersByServiceId(entityName); - - // Execute all of these async actions simultaneously/without blocking sequentially as no common object is modified by them - const promises: Promise[] = clusterDetails.map(cd => { - const kubernetesAuthTranslator: KubernetesAuthTranslator = - KubernetesAuthTranslatorGenerator.getKubernetesAuthTranslatorInstance( - cd.authProvider, - ); - return kubernetesAuthTranslator.decorateClusterDetailsWithAuth( - cd, - requestBody, - ); - }); - const clusterDetailsDecoratedForAuth: ClusterDetails[] = await Promise.all( - promises, + async getCustomResourcesByEntity({ + entity, + auth, + customResources, + }: CustomResourcesByEntity): Promise { + // Don't fetch the default object types only the provided custom resources + return this.fanOutRequests( + entity, + auth, + new Set(), + customResources, ); + } + + async getKubernetesObjectsByEntity({ + entity, + auth, + }: KubernetesObjectsByEntity): Promise { + return this.fanOutRequests( + entity, + auth, + this.objectTypesToFetch, + this.customResources, + ); + } + + private async fanOutRequests( + entity: Entity, + auth: KubernetesRequestAuth, + objectTypesToFetch: Set, + customResources: CustomResourceMatcher[], + ) { + const entityName = + entity.metadata?.annotations?.['backstage.io/kubernetes-id'] || + entity.metadata?.name; + + const clusterDetailsDecoratedForAuth: ClusterDetails[] = + await this.decorateClusterDetailsWithAuth(entity, auth); this.logger.info( `entity.metadata.name=${entityName} clusterDetails=[${clusterDetailsDecoratedForAuth @@ -209,14 +233,12 @@ export class KubernetesFanOutHandler { ); const labelSelector: string = - requestBody.entity?.metadata?.annotations?.[ + entity.metadata?.annotations?.[ 'backstage.io/kubernetes-label-selector' ] || `backstage.io/kubernetes-id=${entityName}`; const namespace = - requestBody.entity?.metadata?.annotations?.[ - 'backstage.io/kubernetes-namespace' - ]; + entity.metadata?.annotations?.['backstage.io/kubernetes-namespace']; return Promise.all( clusterDetailsDecoratedForAuth.map(clusterDetailsItem => { @@ -224,9 +246,12 @@ export class KubernetesFanOutHandler { .fetchObjectsForService({ serviceId: entityName, clusterDetails: clusterDetailsItem, - objectTypesToFetch: this.objectTypesToFetch, + objectTypesToFetch: objectTypesToFetch, labelSelector, - customResources: this.customResources, + customResources: customResources.map(c => ({ + ...c, + objectType: 'customresources', + })), namespace, }) .then(result => this.getMetricsForPods(clusterDetailsItem, result)) @@ -235,6 +260,27 @@ export class KubernetesFanOutHandler { ).then(this.toObjectsByEntityResponse); } + private async decorateClusterDetailsWithAuth( + entity: Entity, + auth: KubernetesRequestAuth, + ) { + const clusterDetails: ClusterDetails[] = await ( + await this.serviceLocator.getClustersByEntity(entity) + ).clusters; + + // Execute all of these async actions simultaneously/without blocking sequentially as no common object is modified by them + return await Promise.all( + clusterDetails.map(cd => { + const kubernetesAuthTranslator: KubernetesAuthTranslator = + this.getAuthTranslator(cd.authProvider); + return kubernetesAuthTranslator.decorateClusterDetailsWithAuth( + cd, + auth, + ); + }), + ); + } + toObjectsByEntityResponse( clusterObjects: ClusterObjects[], ): ObjectsByEntityResponse { @@ -294,4 +340,19 @@ export class KubernetesFanOutHandler { return Promise.all([result, Promise.all(podMetrics)]); } + + private getAuthTranslator(provider: string): KubernetesAuthTranslator { + if (this.authTranslators[provider]) { + return this.authTranslators[provider]; + } + + this.authTranslators[provider] = + KubernetesAuthTranslatorGenerator.getKubernetesAuthTranslatorInstance( + provider, + { + logger: this.logger, + }, + ); + return this.authTranslators[provider]; + } } diff --git a/plugins/kubernetes-backend/src/service/router.ts b/plugins/kubernetes-backend/src/service/router.ts index 61082267be..7bd7cca8a9 100644 --- a/plugins/kubernetes-backend/src/service/router.ts +++ b/plugins/kubernetes-backend/src/service/router.ts @@ -20,6 +20,10 @@ import { KubernetesClustersSupplier } from '../types/types'; import express from 'express'; import { KubernetesBuilder } from './KubernetesBuilder'; +/** + * + * @alpha + */ export interface RouterOptions { logger: Logger; config: Config; @@ -38,6 +42,8 @@ export interface RouterOptions { * config, * }).build(); * ``` + * + * @alpha */ export async function createRouter( options: RouterOptions, diff --git a/plugins/kubernetes-backend/src/types/types.ts b/plugins/kubernetes-backend/src/types/types.ts index 67456d12c8..6302a49fa5 100644 --- a/plugins/kubernetes-backend/src/types/types.ts +++ b/plugins/kubernetes-backend/src/types/types.ts @@ -14,16 +14,22 @@ * limitations under the License. */ +import { Entity } from '@backstage/catalog-model'; import { Logger } from 'winston'; import type { JsonObject } from '@backstage/types'; import type { FetchResponse, KubernetesFetchError, + KubernetesRequestAuth, KubernetesRequestBody, ObjectsByEntityResponse, } from '@backstage/plugin-kubernetes-common'; import { PodStatus } from '@kubernetes/client-node/dist/top'; +/** + * + * @alpha + */ export interface ObjectFetchParams { serviceId: string; clusterDetails: @@ -37,8 +43,11 @@ export interface ObjectFetchParams { namespace?: string; } -// Fetches information from a kubernetes cluster using the cluster details object -// to target a specific cluster +/** + * Fetches information from a kubernetes cluster using the cluster details object to target a specific cluster + * + * @alpha + */ export interface KubernetesFetcher { fetchObjectsForService( params: ObjectFetchParams, @@ -49,13 +58,19 @@ export interface KubernetesFetcher { ): Promise; } +/** + * + * @alpha + */ export interface FetchResponseWrapper { errors: KubernetesFetchError[]; responses: FetchResponse[]; } -// TODO fairly sure there's a easier way to do this - +/** + * + * @alpha + */ export interface ObjectToFetch { objectType: KubernetesObjectTypes; group: string; @@ -63,10 +78,24 @@ export interface ObjectToFetch { plural: string; } +/** + * + * @alpha + */ export interface CustomResource extends ObjectToFetch { objectType: 'customresources'; } +/** + * + * @alpha + */ +export type CustomResourceMatcher = Omit; + +/** + * + * @alpha + */ export type KubernetesObjectTypes = | 'pods' | 'services' @@ -80,7 +109,10 @@ export type KubernetesObjectTypes = | 'customresources' | 'statefulsets'; -// Used to load cluster details from different sources +/** + * Used to load cluster details from different sources + * @alpha + */ export interface KubernetesClustersSupplier { /** * Returns the cached list of clusters. @@ -91,13 +123,24 @@ export interface KubernetesClustersSupplier { getClusters(): Promise; } -// Used to locate which cluster(s) a service is running on +/** + * Used to locate which cluster(s) a service is running on + * @alpha + */ export interface KubernetesServiceLocator { - getClustersByServiceId(serviceId: string): Promise; + getClustersByEntity(entity: Entity): Promise<{ clusters: ClusterDetails[] }>; } +/** + * + * @alpha + */ export type ServiceLocatorMethod = 'multiTenant' | 'http'; // TODO implement http +/** + * + * @alpha + */ export interface ClusterDetails { /** * Specifies the name of the Kubernetes cluster. @@ -151,14 +194,37 @@ export interface ClusterDetails { dashboardParameters?: JsonObject; } +/** + * + * @alpha + */ export interface GKEClusterDetails extends ClusterDetails {} + +/** + * + * @alpha + */ export interface AzureClusterDetails extends ClusterDetails {} + +/** + * + * @alpha + */ export interface ServiceAccountClusterDetails extends ClusterDetails {} + +/** + * + * @alpha + */ export interface AWSClusterDetails extends ClusterDetails { assumeRole?: string; externalId?: string; } +/** + * + * @alpha + */ export interface KubernetesObjectsProviderOptions { logger: Logger; fetcher: KubernetesFetcher; @@ -167,10 +233,38 @@ export interface KubernetesObjectsProviderOptions { objectTypesToFetch?: ObjectToFetch[]; } +/** + * + * @alpha + */ export type ObjectsByEntityRequest = KubernetesRequestBody; +/** + * + * @alpha + */ +export interface KubernetesObjectsByEntity { + entity: Entity; + auth: KubernetesRequestAuth; +} + +/** + * + * @alpha + */ +export interface CustomResourcesByEntity extends KubernetesObjectsByEntity { + customResources: CustomResourceMatcher[]; +} + +/** + * + * @alpha + */ export interface KubernetesObjectsProvider { getKubernetesObjectsByEntity( - request: ObjectsByEntityRequest, + kubernetesObjectsByEntity: KubernetesObjectsByEntity, + ): Promise; + getCustomResourcesByEntity( + customResourcesByEntity: CustomResourcesByEntity, ): Promise; } diff --git a/plugins/kubernetes-common/CHANGELOG.md b/plugins/kubernetes-common/CHANGELOG.md index 65b5358733..d9f7e60c13 100644 --- a/plugins/kubernetes-common/CHANGELOG.md +++ b/plugins/kubernetes-common/CHANGELOG.md @@ -1,5 +1,29 @@ # @backstage/plugin-kubernetes-common +## 0.3.0 + +### Minor Changes + +- 4328737af6: Add support to fetch data for Stateful Sets + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.0.3 + +## 0.3.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.0.3-next.0 + +## 0.3.0-next.0 + +### Minor Changes + +- 4328737af6: Add support to fetch data for Stateful Sets + ## 0.2.10 ### Patch Changes diff --git a/plugins/kubernetes-common/api-report.md b/plugins/kubernetes-common/api-report.md index cca4a9760f..d59db7422f 100644 --- a/plugins/kubernetes-common/api-report.md +++ b/plugins/kubernetes-common/api-report.md @@ -190,17 +190,24 @@ export interface KubernetesFetchError { statusCode?: number; } +// Warning: (ae-missing-release-tag) "KubernetesRequestAuth" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface KubernetesRequestAuth { + // (undocumented) + google?: string; + // (undocumented) + oidc?: { + [key: string]: string; + }; +} + // Warning: (ae-missing-release-tag) "KubernetesRequestBody" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) export interface KubernetesRequestBody { // (undocumented) - auth?: { - google?: string; - oidc?: { - [key: string]: string; - }; - }; + auth?: KubernetesRequestAuth; // (undocumented) entity: Entity; } diff --git a/plugins/kubernetes-common/package.json b/plugins/kubernetes-common/package.json index 44298c0407..62c9eb9745 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.2.10", + "version": "0.3.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -38,11 +38,11 @@ "url": "https://github.com/backstage/backstage/issues" }, "dependencies": { - "@backstage/catalog-model": "^1.0.2", + "@backstage/catalog-model": "^1.0.3", "@kubernetes/client-node": "^0.16.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1" + "@backstage/cli": "^0.17.2" }, "jest": { "roots": [ diff --git a/plugins/kubernetes-common/src/types.ts b/plugins/kubernetes-common/src/types.ts index 3d4746ca44..68fbe09301 100644 --- a/plugins/kubernetes-common/src/types.ts +++ b/plugins/kubernetes-common/src/types.ts @@ -29,13 +29,15 @@ import { } from '@kubernetes/client-node'; import { Entity } from '@backstage/catalog-model'; -export interface KubernetesRequestBody { - auth?: { - google?: string; - oidc?: { - [key: string]: string; - }; +export interface KubernetesRequestAuth { + google?: string; + oidc?: { + [key: string]: string; }; +} + +export interface KubernetesRequestBody { + auth?: KubernetesRequestAuth; entity: Entity; } diff --git a/plugins/kubernetes/CHANGELOG.md b/plugins/kubernetes/CHANGELOG.md index 22060e3415..29f0ed0241 100644 --- a/plugins/kubernetes/CHANGELOG.md +++ b/plugins/kubernetes/CHANGELOG.md @@ -1,5 +1,53 @@ # @backstage/plugin-kubernetes +## 0.6.6 + +### Patch Changes + +- 4328737af6: Add support to fetch data for Stateful Sets and display an accordion in the same way as with Deployments +- b9b8bbc7d9: show request/limit CPU and Memory on the UI +- 5553f09e80: ability to configure refresh interval on Kubernetes tab +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- 81304e3e91: Fix for HPA matching when deploying same HPA in multiple namespaces +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/plugin-kubernetes-common@0.3.0 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## 0.6.6-next.2 + +### Patch Changes + +- 5553f09e80: ability to configure refresh interval on Kubernetes tab +- Updated dependencies + - @backstage/core-components@0.9.5-next.2 + +## 0.6.6-next.1 + +### Patch Changes + +- b9b8bbc7d9: show request/limit CPU and Memory on the UI +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + - @backstage/plugin-kubernetes-common@0.3.0-next.1 + +## 0.6.6-next.0 + +### Patch Changes + +- 4328737af6: Add support to fetch data for Stateful Sets and display an accordion in the same way as with Deployments +- 81304e3e91: Fix for HPA matching when deploying same HPA in multiple namespaces +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + - @backstage/plugin-kubernetes-common@0.3.0-next.0 + ## 0.6.5 ### Patch Changes diff --git a/plugins/kubernetes/api-report.md b/plugins/kubernetes/api-report.md index 6e63f3c34e..dc2c222712 100644 --- a/plugins/kubernetes/api-report.md +++ b/plugins/kubernetes/api-report.md @@ -112,7 +112,14 @@ export interface DeploymentResources { // Warning: (ae-missing-release-tag) "EntityKubernetesContent" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const EntityKubernetesContent: (_props: {}) => JSX.Element; +export const EntityKubernetesContent: ( + props: EntityKubernetesContentProps, +) => JSX.Element; + +// @public +export type EntityKubernetesContentProps = { + refreshIntervalMs?: number; +}; // Warning: (ae-forgotten-export) The symbol "ErrorPanelProps" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "ErrorPanel" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -303,6 +310,7 @@ export class KubernetesBackendClient implements KubernetesApi { // @public (undocumented) export const KubernetesContent: ({ entity, + refreshIntervalMs, }: KubernetesContentProps) => JSX.Element; // Warning: (ae-forgotten-export) The symbol "KubernetesDrawerable" needs to be exported by the entry point index.d.ts @@ -373,11 +381,10 @@ export const PodsTable: ({ extraColumns, }: PodsTablesProps) => JSX.Element; -// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "Router" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const Router: (_props: Props) => JSX.Element; +export const Router: (props: { refreshIntervalMs?: number }) => JSX.Element; // Warning: (ae-missing-release-tag) "ServiceAccountKubernetesAuthProvider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // diff --git a/plugins/kubernetes/package.json b/plugins/kubernetes/package.json index b83e961e9c..1b63484224 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.6.5", + "version": "0.6.6", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -34,12 +34,12 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-model": "^1.0.2", + "@backstage/catalog-model": "^1.0.3", "@backstage/config": "^1.0.1", - "@backstage/core-components": "^0.9.4", - "@backstage/core-plugin-api": "^1.0.2", - "@backstage/plugin-catalog-react": "^1.1.0", - "@backstage/plugin-kubernetes-common": "^0.2.10", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", + "@backstage/plugin-catalog-react": "^1.1.1", + "@backstage/plugin-kubernetes-common": "^0.3.0", "@backstage/theme": "^0.2.15", "@kubernetes/client-node": "^0.16.0", "@material-ui/core": "^4.12.2", @@ -57,10 +57,10 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/core-app-api": "^1.0.2", - "@backstage/dev-utils": "^1.0.2", - "@backstage/test-utils": "^1.1.0", + "@backstage/cli": "^0.17.2", + "@backstage/core-app-api": "^1.0.3", + "@backstage/dev-utils": "^1.0.3", + "@backstage/test-utils": "^1.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/react-hooks": "^8.0.0", @@ -68,7 +68,7 @@ "@types/jest": "^26.0.7", "@types/node": "^16.11.26", "cross-fetch": "^3.1.5", - "msw": "^0.35.0" + "msw": "^0.42.0" }, "files": [ "dist" diff --git a/plugins/kubernetes/src/Router.tsx b/plugins/kubernetes/src/Router.tsx index deb6475ad5..fd3a4bd0ce 100644 --- a/plugins/kubernetes/src/Router.tsx +++ b/plugins/kubernetes/src/Router.tsx @@ -32,9 +32,7 @@ export const isKubernetesAvailable = (entity: Entity) => entity.metadata.annotations?.[KUBERNETES_LABEL_SELECTOR_QUERY_ANNOTATION], ); -type Props = {}; - -export const Router = (_props: Props) => { +export const Router = (props: { refreshIntervalMs?: number }) => { const { entity } = useEntity(); const kubernetesAnnotationValue = @@ -49,7 +47,15 @@ export const Router = (_props: Props) => { ) { return ( - } /> + + } + /> ); } diff --git a/plugins/kubernetes/src/components/KubernetesContent.tsx b/plugins/kubernetes/src/components/KubernetesContent.tsx index 036bcdb608..3fabfae604 100644 --- a/plugins/kubernetes/src/components/KubernetesContent.tsx +++ b/plugins/kubernetes/src/components/KubernetesContent.tsx @@ -25,10 +25,20 @@ import EmptyStateImage from '../assets/emptystate.svg'; import { useKubernetesObjects } from '../hooks'; import { Content, Page, Progress } from '@backstage/core-components'; -type KubernetesContentProps = { entity: Entity; children?: React.ReactNode }; +type KubernetesContentProps = { + entity: Entity; + refreshIntervalMs?: number; + children?: React.ReactNode; +}; -export const KubernetesContent = ({ entity }: KubernetesContentProps) => { - const { kubernetesObjects, error } = useKubernetesObjects(entity); +export const KubernetesContent = ({ + entity, + refreshIntervalMs, +}: KubernetesContentProps) => { + const { kubernetesObjects, error } = useKubernetesObjects( + entity, + refreshIntervalMs, + ); const clustersWithErrors = kubernetesObjects?.items.filter(r => r.errors.length > 0) ?? []; diff --git a/plugins/kubernetes/src/components/Pods/PodsTable.test.tsx b/plugins/kubernetes/src/components/Pods/PodsTable.test.tsx index 1201ad106d..d24b3053eb 100644 --- a/plugins/kubernetes/src/components/Pods/PodsTable.test.tsx +++ b/plugins/kubernetes/src/components/Pods/PodsTable.test.tsx @@ -108,10 +108,10 @@ describe('PodsTable', () => { expect(getByText('1/1')).toBeInTheDocument(); expect(getByText('0')).toBeInTheDocument(); expect(getByText('OK')).toBeInTheDocument(); - expect(getByText('requests: 99%')).toBeInTheDocument(); - expect(getByText('limits: 99%')).toBeInTheDocument(); - expect(getByText('requests: 1%')).toBeInTheDocument(); - expect(getByText('limits: 0%')).toBeInTheDocument(); + expect(getByText('requests: 99% of 50m')).toBeInTheDocument(); + expect(getByText('limits: 99% of 50m')).toBeInTheDocument(); + expect(getByText('requests: 1% of 64MiB')).toBeInTheDocument(); + expect(getByText('limits: 0% of 128MiB')).toBeInTheDocument(); }); it('should render placehoplder when empty metrics context', async () => { const podNameToClientPodStatus = new Map(); diff --git a/plugins/kubernetes/src/index.ts b/plugins/kubernetes/src/index.ts index 688d52d7e1..6e8de777ca 100644 --- a/plugins/kubernetes/src/index.ts +++ b/plugins/kubernetes/src/index.ts @@ -25,6 +25,7 @@ export { kubernetesPlugin as plugin, EntityKubernetesContent, } from './plugin'; +export type { EntityKubernetesContentProps } from './plugin'; export { Router, isKubernetesAvailable } from './Router'; export * from './api'; export * from './kubernetes-auth-provider'; diff --git a/plugins/kubernetes/src/plugin.ts b/plugins/kubernetes/src/plugin.ts index b54850307e..124bff831b 100644 --- a/plugins/kubernetes/src/plugin.ts +++ b/plugins/kubernetes/src/plugin.ts @@ -76,7 +76,21 @@ export const kubernetesPlugin = createPlugin({ }, }); -export const EntityKubernetesContent = kubernetesPlugin.provide( +/** + * Props of EntityKubernetesContent + * + * @public + */ +export type EntityKubernetesContentProps = { + /** + * Sets the refresh interval in milliseconds. The default value is 10000 (10 seconds) + */ + refreshIntervalMs?: number; +}; + +export const EntityKubernetesContent: ( + props: EntityKubernetesContentProps, +) => JSX.Element = kubernetesPlugin.provide( createRoutableExtension({ name: 'EntityKubernetesContent', component: () => import('./Router').then(m => m.Router), diff --git a/plugins/kubernetes/src/utils/pod.test.tsx b/plugins/kubernetes/src/utils/pod.test.tsx index 5822a16b86..06d4e22371 100644 --- a/plugins/kubernetes/src/utils/pod.test.tsx +++ b/plugins/kubernetes/src/utils/pod.test.tsx @@ -15,7 +15,11 @@ */ import React from 'react'; -import { currentToDeclaredResourceToPerc, podStatusToCpuUtil } from './pod'; +import { + currentToDeclaredResourceToPerc, + podStatusToCpuUtil, + podStatusToMemoryUtil, +} from './pod'; import { SubvalueCell } from '@backstage/core-components'; describe('pod', () => { @@ -46,7 +50,30 @@ describe('pod', () => { }, } as any); expect(result).toStrictEqual( - , + , + ); + }); + }); + describe('podStatusToMemoryUtil', () => { + it('does use correct units', () => { + const result = podStatusToMemoryUtil({ + memory: { + // ~91.5 MiB + currentUsage: '95948800', + // 320 MiB + limitTotal: '335544320', + // 192 MiB + requestTotal: '201326592', + }, + } as any); + expect(result).toStrictEqual( + , ); }); }); diff --git a/plugins/kubernetes/src/utils/pod.tsx b/plugins/kubernetes/src/utils/pod.tsx index 7bed160a5f..2f657e7658 100644 --- a/plugins/kubernetes/src/utils/pod.tsx +++ b/plugins/kubernetes/src/utils/pod.tsx @@ -130,6 +130,10 @@ export const currentToDeclaredResourceToPerc = ( return `${(numerator * BigInt(100)) / denominator}%`; }; +const formatMilicores = (value: string | number): string => { + return `${parseFloat(value.toString()) * 1000}m`; +}; + export const podStatusToCpuUtil = (podStatus: ClientPodStatus): ReactNode => { const cpuUtil = podStatus.cpu; @@ -146,15 +150,19 @@ export const podStatusToCpuUtil = (podStatus: ClientPodStatus): ReactNode => { value={`requests: ${currentToDeclaredResourceToPerc( currentUsage, cpuUtil.requestTotal, - )}`} + )} of ${formatMilicores(cpuUtil.requestTotal)}`} subvalue={`limits: ${currentToDeclaredResourceToPerc( currentUsage, cpuUtil.limitTotal, - )}`} + )} of ${formatMilicores(cpuUtil.limitTotal)}`} /> ); }; +const bytesToMiB = (value: string | number): string => { + return `${parseFloat(value.toString()) / 1024 / 1024}MiB`; +}; + export const podStatusToMemoryUtil = ( podStatus: ClientPodStatus, ): ReactNode => { @@ -165,11 +173,11 @@ export const podStatusToMemoryUtil = ( value={`requests: ${currentToDeclaredResourceToPerc( memUtil.currentUsage, memUtil.requestTotal, - )}`} + )} of ${bytesToMiB(memUtil.requestTotal)}`} subvalue={`limits: ${currentToDeclaredResourceToPerc( memUtil.currentUsage, memUtil.limitTotal, - )}`} + )} of ${bytesToMiB(memUtil.limitTotal)}`} /> ); }; diff --git a/plugins/lighthouse/CHANGELOG.md b/plugins/lighthouse/CHANGELOG.md index 2de9ffda82..7dc3e30b1f 100644 --- a/plugins/lighthouse/CHANGELOG.md +++ b/plugins/lighthouse/CHANGELOG.md @@ -1,5 +1,35 @@ # @backstage/plugin-lighthouse +## 0.3.6 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## 0.3.6-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## 0.3.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + ## 0.3.5 ### Patch Changes diff --git a/plugins/lighthouse/package.json b/plugins/lighthouse/package.json index f3c7d7621f..5f7097c704 100644 --- a/plugins/lighthouse/package.json +++ b/plugins/lighthouse/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-lighthouse", "description": "A Backstage plugin that integrates towards Lighthouse", - "version": "0.3.5", + "version": "0.3.6", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -35,11 +35,11 @@ "start": "backstage-cli package start" }, "dependencies": { - "@backstage/catalog-model": "^1.0.2", + "@backstage/catalog-model": "^1.0.3", "@backstage/config": "^1.0.1", - "@backstage/core-components": "^0.9.4", - "@backstage/core-plugin-api": "^1.0.2", - "@backstage/plugin-catalog-react": "^1.1.0", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", + "@backstage/plugin-catalog-react": "^1.1.1", "@backstage/theme": "^0.2.15", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -51,10 +51,10 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/core-app-api": "^1.0.2", - "@backstage/dev-utils": "^1.0.2", - "@backstage/test-utils": "^1.1.0", + "@backstage/cli": "^0.17.2", + "@backstage/core-app-api": "^1.0.3", + "@backstage/dev-utils": "^1.0.3", + "@backstage/test-utils": "^1.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/react-hooks": "^8.0.0", @@ -63,7 +63,7 @@ "@types/node": "^16.11.26", "@types/react": "^16.13.1 || ^17.0.0", "cross-fetch": "^3.1.5", - "msw": "^0.35.0" + "msw": "^0.42.0" }, "files": [ "dist" diff --git a/plugins/newrelic-dashboard/CHANGELOG.md b/plugins/newrelic-dashboard/CHANGELOG.md index f14da874e3..54a058fcb9 100644 --- a/plugins/newrelic-dashboard/CHANGELOG.md +++ b/plugins/newrelic-dashboard/CHANGELOG.md @@ -1,5 +1,33 @@ # @backstage/plugin-newrelic-dashboard +## 0.1.14 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## 0.1.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## 0.1.14-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + ## 0.1.13 ### Patch Changes diff --git a/plugins/newrelic-dashboard/package.json b/plugins/newrelic-dashboard/package.json index ad65c1538d..8166928a6a 100644 --- a/plugins/newrelic-dashboard/package.json +++ b/plugins/newrelic-dashboard/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-newrelic-dashboard", - "version": "0.1.13", + "version": "0.1.14", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -23,19 +23,19 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-model": "^1.0.2", - "@backstage/core-components": "^0.9.4", - "@backstage/core-plugin-api": "^1.0.2", + "@backstage/catalog-model": "^1.0.3", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", "@backstage/errors": "^1.0.0", - "@backstage/plugin-catalog-react": "^1.1.0", + "@backstage/plugin-catalog-react": "^1.1.1", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", "react-use": "^17.2.4" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/dev-utils": "^1.0.2", + "@backstage/cli": "^0.17.2", + "@backstage/dev-utils": "^1.0.3", "@testing-library/jest-dom": "^5.10.1", "@types/react": "^16.13.1 || ^17.0.0", "cross-fetch": "^3.1.5" diff --git a/plugins/newrelic/CHANGELOG.md b/plugins/newrelic/CHANGELOG.md index b9ff452035..97da241a56 100644 --- a/plugins/newrelic/CHANGELOG.md +++ b/plugins/newrelic/CHANGELOG.md @@ -1,5 +1,30 @@ # @backstage/plugin-newrelic +## 0.3.24 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + +## 0.3.24-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + +## 0.3.24-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.9.5-next.0 + ## 0.3.23 ### Patch Changes diff --git a/plugins/newrelic/package.json b/plugins/newrelic/package.json index ff48d7a40a..09f3d3196d 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.23", + "version": "0.3.24", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -35,8 +35,8 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/core-components": "^0.9.4", - "@backstage/core-plugin-api": "^1.0.2", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", "@backstage/theme": "^0.2.15", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -47,17 +47,17 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/core-app-api": "^1.0.2", - "@backstage/dev-utils": "^1.0.2", - "@backstage/test-utils": "^1.1.0", + "@backstage/cli": "^0.17.2", + "@backstage/core-app-api": "^1.0.3", + "@backstage/dev-utils": "^1.0.3", + "@backstage/test-utils": "^1.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", "@types/jest": "^26.0.7", "@types/node": "^16.11.26", "cross-fetch": "^3.1.5", - "msw": "^0.35.0" + "msw": "^0.42.0" }, "files": [ "dist" diff --git a/plugins/org/CHANGELOG.md b/plugins/org/CHANGELOG.md index 142c92b4a3..73129feed6 100644 --- a/plugins/org/CHANGELOG.md +++ b/plugins/org/CHANGELOG.md @@ -1,5 +1,88 @@ # @backstage/plugin-org +## 0.5.6 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- 306d0b4fdd: Added the ability to use an additional `filter` when fetching groups in `MyGroupsSidebarItem` component. Example: + + ```diff + // app/src/components/Root/Root.tsx + + + //... + }> + {/* Global nav, not org-specific */} + //... + + + //... + + + + ``` + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## 0.5.6-next.2 + +### Patch Changes + +- 306d0b4fdd: Added the ability to use an additional `filter` when fetching groups in `MyGroupsSidebarItem` component. Example: + + ```diff + // app/src/components/Root/Root.tsx + + + //... + }> + {/* Global nav, not org-specific */} + //... + + + //... + + + + ``` + +- Updated dependencies + - @backstage/core-components@0.9.5-next.2 + +## 0.5.6-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## 0.5.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + ## 0.5.5 ### Patch Changes diff --git a/plugins/org/api-report.md b/plugins/org/api-report.md index 5f71be44bc..d020dfb438 100644 --- a/plugins/org/api-report.md +++ b/plugins/org/api-report.md @@ -50,6 +50,7 @@ export const MyGroupsSidebarItem: (props: { singularTitle: string; pluralTitle: string; icon: IconComponent; + filter?: Record; }) => JSX.Element | null; // @public (undocumented) diff --git a/plugins/org/package.json b/plugins/org/package.json index b7b907f888..2b62e81683 100644 --- a/plugins/org/package.json +++ b/plugins/org/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-org", "description": "A Backstage plugin that helps you create entity pages for your organization", - "version": "0.5.5", + "version": "0.5.6", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -29,10 +29,10 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-model": "^1.0.2", - "@backstage/core-components": "^0.9.4", - "@backstage/core-plugin-api": "^1.0.2", - "@backstage/plugin-catalog-react": "^1.1.0", + "@backstage/catalog-model": "^1.0.3", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", + "@backstage/plugin-catalog-react": "^1.1.1", "@backstage/theme": "^0.2.15", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -48,18 +48,18 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/catalog-client": "^1.0.2", - "@backstage/cli": "^0.17.1", - "@backstage/core-app-api": "^1.0.2", - "@backstage/dev-utils": "^1.0.2", - "@backstage/test-utils": "^1.1.0", + "@backstage/catalog-client": "^1.0.3", + "@backstage/cli": "^0.17.2", + "@backstage/core-app-api": "^1.0.3", + "@backstage/dev-utils": "^1.0.3", + "@backstage/test-utils": "^1.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", "@types/jest": "^26.0.7", "@types/node": "^16.11.26", "cross-fetch": "^3.1.5", - "msw": "^0.35.0" + "msw": "^0.42.0" }, "files": [ "dist" diff --git a/plugins/org/src/components/MyGroupsSidebarItem/MyGroupsSidebarItem.test.tsx b/plugins/org/src/components/MyGroupsSidebarItem/MyGroupsSidebarItem.test.tsx index d290001eb8..bd212bf972 100644 --- a/plugins/org/src/components/MyGroupsSidebarItem/MyGroupsSidebarItem.test.tsx +++ b/plugins/org/src/components/MyGroupsSidebarItem/MyGroupsSidebarItem.test.tsx @@ -196,4 +196,92 @@ describe('MyGroupsSidebarItem Test', () => { expect(rendered.getByLabelText('My Squads')).toBeInTheDocument(); }); }); + + describe('When an additional filter is not provided', () => { + it('catalogApi.getEntities() should be called with the default filter', async () => { + const identityApi: Partial = { + getBackstageIdentity: async () => ({ + type: 'user', + userEntityRef: 'user:default/guest', + ownershipEntityRefs: ['user:default/guest'], + }), + }; + const mockCatalogApi: Partial = { + getEntities: jest.fn(), + }; + await renderInTestApp( + + + , + { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': entityRouteRef, + }, + }, + ); + expect(mockCatalogApi.getEntities).toHaveBeenCalledWith({ + filter: [ + { + kind: 'group', + 'relations.hasMember': 'user:default/guest', + }, + ], + fields: ['metadata', 'kind'], + }); + }); + }); + + describe('When an additional filter is provided', () => { + it('catalogApi.getEntities() should be called with an additional filter item', async () => { + const identityApi: Partial = { + getBackstageIdentity: async () => ({ + type: 'user', + userEntityRef: 'user:default/guest', + ownershipEntityRefs: ['user:default/guest'], + }), + }; + const mockCatalogApi: Partial = { + getEntities: jest.fn(), + }; + await renderInTestApp( + + + , + { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': entityRouteRef, + }, + }, + ); + expect(mockCatalogApi.getEntities).toHaveBeenCalledWith({ + filter: [ + { + kind: 'group', + 'relations.hasMember': 'user:default/guest', + 'spec.type': 'team', + }, + ], + fields: ['metadata', 'kind'], + }); + }); + }); }); diff --git a/plugins/org/src/components/MyGroupsSidebarItem/MyGroupsSidebarItem.tsx b/plugins/org/src/components/MyGroupsSidebarItem/MyGroupsSidebarItem.tsx index 5a3cc42628..fa8f8405ff 100644 --- a/plugins/org/src/components/MyGroupsSidebarItem/MyGroupsSidebarItem.tsx +++ b/plugins/org/src/components/MyGroupsSidebarItem/MyGroupsSidebarItem.tsx @@ -45,8 +45,9 @@ export const MyGroupsSidebarItem = (props: { singularTitle: string; pluralTitle: string; icon: IconComponent; + filter?: Record; }) => { - const { singularTitle, pluralTitle, icon } = props; + const { singularTitle, pluralTitle, icon, filter } = props; const identityApi = useApi(identityApiRef); const catalogApi: CatalogApi = useApi(catalogApiRef); @@ -56,7 +57,13 @@ export const MyGroupsSidebarItem = (props: { const profile = await identityApi.getBackstageIdentity(); const response = await catalogApi.getEntities({ - filter: [{ kind: 'group', 'relations.hasMember': profile.userEntityRef }], + filter: [ + { + kind: 'group', + 'relations.hasMember': profile.userEntityRef, + ...(filter ?? {}), + }, + ], fields: ['metadata', 'kind'], }); diff --git a/plugins/pagerduty/CHANGELOG.md b/plugins/pagerduty/CHANGELOG.md index 30e6171682..3f6a54f367 100644 --- a/plugins/pagerduty/CHANGELOG.md +++ b/plugins/pagerduty/CHANGELOG.md @@ -1,5 +1,52 @@ # @backstage/plugin-pagerduty +## 0.4.0 + +### Minor Changes + +- b157c2eb1c: **Breaking**: Use identityApi to provide auth token for pagerduty API calls. + +### Patch Changes + +- 76bf6400fe: Fix alert that was not showing after creating an incident. +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## 0.4.0-next.2 + +### Minor Changes + +- b157c2eb1c: **Breaking**: Use identityApi to provide auth token for pagerduty API calls. + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.9.5-next.2 + +## 0.3.33-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## 0.3.33-next.0 + +### Patch Changes + +- 76bf6400fe: Fix alert that was not showing after creating an incident. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + ## 0.3.32 ### Patch Changes diff --git a/plugins/pagerduty/README.md b/plugins/pagerduty/README.md index 71050dc54b..7b3124ab0f 100644 --- a/plugins/pagerduty/README.md +++ b/plugins/pagerduty/README.md @@ -7,6 +7,7 @@ - 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. + - _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 @@ -117,6 +118,26 @@ This will proxy the request by adding an `Authorization` header with the provide ### 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 diff --git a/plugins/pagerduty/api-report.md b/plugins/pagerduty/api-report.md index bd26531da6..be3fa9fa23 100644 --- a/plugins/pagerduty/api-report.md +++ b/plugins/pagerduty/api-report.md @@ -10,6 +10,7 @@ import { BackstagePlugin } from '@backstage/core-plugin-api'; import { ConfigApi } from '@backstage/core-plugin-api'; import { DiscoveryApi } from '@backstage/core-plugin-api'; import { Entity } from '@backstage/catalog-model'; +import { FetchApi } from '@backstage/core-plugin-api'; import { ReactNode } from 'react'; // Warning: (ae-missing-release-tag) "EntityPagerDutyCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -30,44 +31,138 @@ export { isPluginApplicableToEntity }; // @public (undocumented) export const pagerDutyApiRef: ApiRef; +// Warning: (ae-missing-release-tag) "PagerDutyAssignee" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type PagerDutyAssignee = { + id: string; + summary: string; + html_url: string; +}; + // Warning: (ae-missing-release-tag) "PagerDutyCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) export const PagerDutyCard: () => JSX.Element; +// Warning: (ae-missing-release-tag) "PagerDutyChangeEvent" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type PagerDutyChangeEvent = { + id: string; + integration: [ + { + service: PagerDutyService; + }, + ]; + source: string; + html_url: string; + links: [ + { + href: string; + text: string; + }, + ]; + summary: string; + timestamp: string; +}; + +// Warning: (ae-missing-release-tag) "PagerDutyChangeEventsResponse" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type PagerDutyChangeEventsResponse = { + change_events: PagerDutyChangeEvent[]; +}; + // Warning: (ae-missing-release-tag) "PagerDutyClient" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) export class PagerDutyClient implements PagerDutyApi { - // Warning: (ae-forgotten-export) The symbol "ClientApiConfig" needs to be exported by the entry point index.d.ts - constructor(config: ClientApiConfig); + constructor(config: PagerDutyClientApiConfig); // (undocumented) static fromConfig( configApi: ConfigApi, - discoveryApi: DiscoveryApi, + { discoveryApi, fetchApi }: PagerDutyClientApiDependencies, ): PagerDutyClient; - // Warning: (ae-forgotten-export) The symbol "ChangeEvent" needs to be exported by the entry point index.d.ts - // // (undocumented) - getChangeEventsByServiceId(serviceId: string): Promise; - // Warning: (ae-forgotten-export) The symbol "Incident" needs to be exported by the entry point index.d.ts - // + getChangeEventsByServiceId( + serviceId: string, + ): Promise; // (undocumented) - getIncidentsByServiceId(serviceId: string): Promise; - // Warning: (ae-forgotten-export) The symbol "OnCall" needs to be exported by the entry point index.d.ts - // + getIncidentsByServiceId( + serviceId: string, + ): Promise; // (undocumented) - getOnCallByPolicyId(policyId: string): Promise; - // Warning: (ae-forgotten-export) The symbol "Service" needs to be exported by the entry point index.d.ts - // + getOnCallByPolicyId(policyId: string): Promise; // (undocumented) - getServiceByIntegrationKey(integrationKey: string): Promise; - // Warning: (ae-forgotten-export) The symbol "TriggerAlarmRequest" needs to be exported by the entry point index.d.ts - // + getServiceByEntity(entity: Entity): Promise; // (undocumented) - triggerAlarm(request: TriggerAlarmRequest): Promise; + triggerAlarm(request: PagerDutyTriggerAlarmRequest): Promise; } +// Warning: (ae-missing-release-tag) "PagerDutyClientApiConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type PagerDutyClientApiConfig = PagerDutyClientApiDependencies & { + eventsBaseUrl?: string; +}; + +// Warning: (ae-missing-release-tag) "PagerDutyClientApiDependencies" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type PagerDutyClientApiDependencies = { + discoveryApi: DiscoveryApi; + fetchApi: FetchApi; +}; + +// Warning: (ae-missing-release-tag) "PagerDutyEntity" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type PagerDutyEntity = { + integrationKey?: string; + serviceId?: string; + name: string; +}; + +// Warning: (ae-missing-release-tag) "PagerDutyIncident" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type PagerDutyIncident = { + id: string; + title: string; + status: string; + html_url: string; + assignments: [ + { + assignee: PagerDutyAssignee; + }, + ]; + serviceId: string; + created_at: string; +}; + +// Warning: (ae-missing-release-tag) "PagerDutyIncidentsResponse" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type PagerDutyIncidentsResponse = { + incidents: PagerDutyIncident[]; +}; + +// Warning: (ae-missing-release-tag) "PagerDutyOnCall" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type PagerDutyOnCall = { + user: PagerDutyUser; + escalation_level: number; +}; + +// Warning: (ae-missing-release-tag) "PagerDutyOnCallsResponse" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type PagerDutyOnCallsResponse = { + oncalls: PagerDutyOnCall[]; +}; + // Warning: (ae-missing-release-tag) "pagerDutyPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -75,6 +170,49 @@ const pagerDutyPlugin: BackstagePlugin<{}, {}>; export { pagerDutyPlugin }; export { pagerDutyPlugin as plugin }; +// Warning: (ae-missing-release-tag) "PagerDutyService" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type PagerDutyService = { + id: string; + name: string; + html_url: string; + integrationKey: string; + escalation_policy: { + id: string; + user: PagerDutyUser; + html_url: string; + }; +}; + +// Warning: (ae-missing-release-tag) "PagerDutyServiceResponse" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type PagerDutyServiceResponse = { + service: PagerDutyService; +}; + +// Warning: (ae-missing-release-tag) "PagerDutyTriggerAlarmRequest" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type PagerDutyTriggerAlarmRequest = { + integrationKey: string; + source: string; + description: string; + userName: string; +}; + +// Warning: (ae-missing-release-tag) "PagerDutyUser" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type PagerDutyUser = { + id: string; + summary: string; + email: string; + html_url: string; + name: string; +}; + // Warning: (ae-forgotten-export) The symbol "TriggerButtonProps" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "TriggerButton" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // diff --git a/plugins/pagerduty/package.json b/plugins/pagerduty/package.json index ce4df376ac..6d11ee8d69 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.3.32", + "version": "0.4.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -34,10 +34,11 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-model": "^1.0.2", - "@backstage/core-components": "^0.9.4", - "@backstage/core-plugin-api": "^1.0.2", - "@backstage/plugin-catalog-react": "^1.1.0", + "@backstage/catalog-model": "^1.0.3", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", + "@backstage/errors": "^1.0.0", + "@backstage/plugin-catalog-react": "^1.1.1", "@backstage/theme": "^0.2.15", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -52,17 +53,17 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/core-app-api": "^1.0.2", - "@backstage/dev-utils": "^1.0.2", - "@backstage/test-utils": "^1.1.0", + "@backstage/cli": "^0.17.2", + "@backstage/core-app-api": "^1.0.3", + "@backstage/dev-utils": "^1.0.3", + "@backstage/test-utils": "^1.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", "@types/jest": "^26.0.7", "@types/node": "^16.11.26", "cross-fetch": "^3.1.5", - "msw": "^0.35.0" + "msw": "^0.42.0" }, "files": [ "dist" diff --git a/plugins/pagerduty/src/api/client.test.ts b/plugins/pagerduty/src/api/client.test.ts new file mode 100644 index 0000000000..a32a533e68 --- /dev/null +++ b/plugins/pagerduty/src/api/client.test.ts @@ -0,0 +1,376 @@ +/* + * 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 { MockFetchApi } from '@backstage/test-utils'; +import { DiscoveryApi } from '@backstage/core-plugin-api'; +import { PagerDutyClient, UnauthorizedError } from './client'; +import { PagerDutyService, PagerDutyUser } from '../components/types'; +import { NotFoundError } from '@backstage/errors'; +import { Entity } from '@backstage/catalog-model'; + +const mockFetch = jest.fn().mockName('fetch'); +const mockDiscoveryApi: jest.Mocked = { + getBaseUrl: jest + .fn() + .mockName('discoveryApi') + .mockResolvedValue('http://localhost:7007/proxy'), +}; +const mockFetchApi: MockFetchApi = new MockFetchApi({ + baseImplementation: mockFetch, +}); + +let client: PagerDutyClient; +let entity: Entity; + +const user: PagerDutyUser = { + name: 'person1', + id: 'p1', + summary: 'person1', + email: 'person1@example.com', + html_url: 'http://a.com/id1', +}; + +const service: PagerDutyService = { + id: 'def456', + name: 'pagerduty-name', + html_url: 'www.example.com', + escalation_policy: { + id: 'def', + user: user, + html_url: 'http://a.com/id1', + }, + integrationKey: 'abc123', +}; + +const requestHeaders = { + headers: { + Accept: 'application/vnd.pagerduty+json;version=2', + 'Content-Type': 'application/json', + }, + method: 'GET', +}; + +describe('PagerDutyClient', () => { + beforeEach(() => { + mockFetch.mockReset(); + + client = new PagerDutyClient({ + eventsBaseUrl: 'https://events.pagerduty.com/v2', + discoveryApi: mockDiscoveryApi, + fetchApi: mockFetchApi, + }); + }); + + describe('getServiceByEntity', () => { + describe('when provided entity has an integrationKey value', () => { + beforeEach(() => { + entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'pagerduty-test', + annotations: { + 'pagerduty.com/integration-key': 'abc123', + }, + }, + }; + }); + + it('queries proxy path by integration id', async () => { + mockFetch.mockResolvedValueOnce({ + status: 200, + ok: true, + json: () => Promise.resolve({ services: [service] }), + }); + + expect(await client.getServiceByEntity(entity)).toEqual({ + service, + }); + expect(mockFetch).toHaveBeenCalledWith( + 'http://localhost:7007/proxy/pagerduty/services?time_zone=UTC&include[]=integrations&include[]=escalation_policies&query=abc123', + requestHeaders, + ); + }); + + describe('on 401 response code', () => { + beforeEach(() => { + mockFetch.mockResolvedValueOnce({ + status: 401, + ok: false, + json: () => Promise.resolve({}), + }); + }); + + it('throws UnauthorizedError', async () => { + await expect(client.getServiceByEntity(entity)).rejects.toThrowError( + UnauthorizedError, + ); + }); + }); + + describe('on 404 response code', () => { + beforeEach(() => { + mockFetch.mockResolvedValueOnce({ + status: 404, + ok: false, + json: () => Promise.resolve({}), + }); + }); + + it('throws NotFoundError', async () => { + await expect(client.getServiceByEntity(entity)).rejects.toThrowError( + NotFoundError, + ); + }); + }); + + describe('on other non-ok response', () => { + beforeEach(() => { + mockFetch.mockResolvedValueOnce({ + status: 500, + ok: false, + json: () => + Promise.resolve({ + errors: ['Not valid request', 'internal error occurred'], + }), + }); + }); + + it('throws NotFoundError', async () => { + await expect(client.getServiceByEntity(entity)).rejects.toThrowError( + 'Request failed with 500, Not valid request internal error occurred', + ); + }); + }); + + describe('when services response is empty', () => { + beforeEach(() => { + mockFetch.mockResolvedValueOnce({ + status: 200, + ok: true, + json: () => Promise.resolve({ services: [] }), + }); + }); + + it('throws NotFoundError', async () => { + await expect(client.getServiceByEntity(entity)).rejects.toThrowError( + NotFoundError, + ); + }); + }); + }); + + describe('when provided entity has a serviceId value', () => { + beforeEach(() => { + entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'pagerduty-test', + annotations: { + 'pagerduty.com/service-id': 'def456', + }, + }, + }; + }); + + it('queries proxy path by integration id', async () => { + mockFetch.mockResolvedValueOnce({ + status: 200, + ok: true, + json: () => Promise.resolve({ service }), + }); + + expect(await client.getServiceByEntity(entity)).toEqual({ + service, + }); + expect(mockFetch).toHaveBeenCalledWith( + 'http://localhost:7007/proxy/pagerduty/services/def456?time_zone=UTC&include[]=integrations&include[]=escalation_policies', + requestHeaders, + ); + }); + + describe('on 401 response code', () => { + beforeEach(() => { + mockFetch.mockResolvedValueOnce({ + status: 401, + ok: false, + json: () => Promise.resolve({}), + }); + }); + + it('throws UnauthorizedError', async () => { + await expect(client.getServiceByEntity(entity)).rejects.toThrowError( + UnauthorizedError, + ); + }); + }); + + describe('on 404 response code', () => { + beforeEach(() => { + mockFetch.mockResolvedValueOnce({ + status: 404, + ok: false, + json: () => Promise.resolve({}), + }); + }); + + it('throws NotFoundError', async () => { + await expect(client.getServiceByEntity(entity)).rejects.toThrowError( + NotFoundError, + ); + }); + }); + + describe('on other non-ok response', () => { + beforeEach(() => { + mockFetch.mockResolvedValueOnce({ + status: 500, + ok: false, + json: () => + Promise.resolve({ + errors: ['Not valid request', 'internal error occurred'], + }), + }); + }); + + it('throws NotFoundError', async () => { + await expect(client.getServiceByEntity(entity)).rejects.toThrowError( + 'Request failed with 500, Not valid request internal error occurred', + ); + }); + }); + }); + + describe('when provided entity has both integrationKey and serviceId', () => { + beforeEach(() => { + entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'pagerduty-test', + annotations: { + 'pagerduty.com/integration-key': 'abc123', + 'pagerduty.com/service-id': 'def456', + }, + }, + }; + }); + + it('queries proxy path by integration id', async () => { + mockFetch.mockResolvedValueOnce({ + status: 200, + ok: true, + json: () => Promise.resolve({ services: [service] }), + }); + + expect(await client.getServiceByEntity(entity)).toEqual({ + service, + }); + expect(mockFetch).toHaveBeenCalledWith( + 'http://localhost:7007/proxy/pagerduty/services?time_zone=UTC&include[]=integrations&include[]=escalation_policies&query=abc123', + requestHeaders, + ); + }); + + describe('on 401 response code', () => { + beforeEach(() => { + mockFetch.mockResolvedValueOnce({ + status: 401, + ok: false, + json: () => Promise.resolve({}), + }); + }); + + it('throws UnauthorizedError', async () => { + await expect(client.getServiceByEntity(entity)).rejects.toThrowError( + UnauthorizedError, + ); + }); + }); + + describe('on 404 response code', () => { + beforeEach(() => { + mockFetch.mockResolvedValueOnce({ + status: 404, + ok: false, + json: () => Promise.resolve({}), + }); + }); + + it('throws NotFoundError', async () => { + await expect(client.getServiceByEntity(entity)).rejects.toThrowError( + NotFoundError, + ); + }); + }); + + describe('on other non-ok response', () => { + beforeEach(() => { + mockFetch.mockResolvedValueOnce({ + status: 500, + ok: false, + json: () => + Promise.resolve({ + errors: ['Not valid request', 'internal error occurred'], + }), + }); + }); + + it('throws NotFoundError', async () => { + await expect(client.getServiceByEntity(entity)).rejects.toThrowError( + 'Request failed with 500, Not valid request internal error occurred', + ); + }); + }); + + describe('when services response is empty', () => { + beforeEach(() => { + mockFetch.mockResolvedValueOnce({ + status: 200, + ok: true, + json: () => Promise.resolve({ services: [] }), + }); + }); + + it('throws NotFoundError', async () => { + await expect(client.getServiceByEntity(entity)).rejects.toThrowError( + NotFoundError, + ); + }); + }); + }); + + describe('when provided entity has no integrationKey or serviceId values', () => { + beforeEach(() => { + entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'pagerduty-test', + annotations: {}, + }, + }; + }); + + it('throws NotFoundError', async () => { + await expect(client.getServiceByEntity(entity)).rejects.toThrowError( + NotFoundError, + ); + expect(mockFetch).not.toHaveBeenCalled(); + }); + }); + }); +}); diff --git a/plugins/pagerduty/src/api/client.ts b/plugins/pagerduty/src/api/client.ts index 81d3792ecf..69a702f66d 100644 --- a/plugins/pagerduty/src/api/client.ts +++ b/plugins/pagerduty/src/api/client.ts @@ -14,22 +14,22 @@ * limitations under the License. */ -import { Service, Incident, ChangeEvent, OnCall } from '../components/types'; import { PagerDutyApi, - TriggerAlarmRequest, - ServicesResponse, - IncidentsResponse, - OnCallsResponse, - ClientApiConfig, + PagerDutyTriggerAlarmRequest, + PagerDutyServicesResponse, + PagerDutyServiceResponse, + PagerDutyIncidentsResponse, + PagerDutyOnCallsResponse, + PagerDutyClientApiDependencies, + PagerDutyClientApiConfig, RequestOptions, - ChangeEventsResponse, + PagerDutyChangeEventsResponse, } from './types'; -import { - createApiRef, - DiscoveryApi, - ConfigApi, -} from '@backstage/core-plugin-api'; +import { createApiRef, ConfigApi } from '@backstage/core-plugin-api'; +import { NotFoundError } from '@backstage/errors'; +import { Entity } from '@backstage/catalog-model'; +import { getPagerDutyEntity } from '../components/pagerDutyEntity'; export class UnauthorizedError extends Error {} @@ -37,60 +37,89 @@ export const pagerDutyApiRef = createApiRef({ id: 'plugin.pagerduty.api', }); +const commonGetServiceParams = + 'time_zone=UTC&include[]=integrations&include[]=escalation_policies'; + export class PagerDutyClient implements PagerDutyApi { - static fromConfig(configApi: ConfigApi, discoveryApi: DiscoveryApi) { + static fromConfig( + configApi: ConfigApi, + { discoveryApi, fetchApi }: PagerDutyClientApiDependencies, + ) { const eventsBaseUrl: string = configApi.getOptionalString('pagerDuty.eventsBaseUrl') ?? 'https://events.pagerduty.com/v2'; + return new PagerDutyClient({ eventsBaseUrl, discoveryApi, + fetchApi, }); } - constructor(private readonly config: ClientApiConfig) {} + constructor(private readonly config: PagerDutyClientApiConfig) {} - async getServiceByIntegrationKey(integrationKey: string): Promise { - const params = `time_zone=UTC&include[]=integrations&include[]=escalation_policies&query=${integrationKey}`; - const url = `${await this.config.discoveryApi.getBaseUrl( - 'proxy', - )}/pagerduty/services?${params}`; - const { services } = await this.getByUrl(url); + async getServiceByEntity(entity: Entity): Promise { + const { integrationKey, serviceId } = getPagerDutyEntity(entity); - return services; + let response: PagerDutyServiceResponse; + let url: string; + + if (integrationKey) { + url = `${await this.config.discoveryApi.getBaseUrl( + 'proxy', + )}/pagerduty/services?${commonGetServiceParams}&query=${integrationKey}`; + const { services } = await this.getByUrl(url); + const service = services[0]; + + if (!service) throw new NotFoundError(); + + response = { service }; + } else if (serviceId) { + url = `${await this.config.discoveryApi.getBaseUrl( + 'proxy', + )}/pagerduty/services/${serviceId}?${commonGetServiceParams}`; + + response = await this.getByUrl(url); + } else { + throw new NotFoundError(); + } + + return response; } - async getIncidentsByServiceId(serviceId: string): Promise { + async getIncidentsByServiceId( + serviceId: string, + ): Promise { const params = `time_zone=UTC&sort_by=created_at&statuses[]=triggered&statuses[]=acknowledged&service_ids[]=${serviceId}`; const url = `${await this.config.discoveryApi.getBaseUrl( 'proxy', )}/pagerduty/incidents?${params}`; - const { incidents } = await this.getByUrl(url); - return incidents; + return await this.getByUrl(url); } - async getChangeEventsByServiceId(serviceId: string): Promise { + async getChangeEventsByServiceId( + serviceId: string, + ): Promise { const params = `limit=5&time_zone=UTC&sort_by=timestamp`; const url = `${await this.config.discoveryApi.getBaseUrl( 'proxy', )}/pagerduty/services/${serviceId}/change_events?${params}`; - const { change_events } = await this.getByUrl(url); - - return change_events; + return await this.getByUrl(url); } - async getOnCallByPolicyId(policyId: string): Promise { + async getOnCallByPolicyId( + policyId: string, + ): Promise { const params = `time_zone=UTC&include[]=users&escalation_policy_ids[]=${policyId}`; const url = `${await this.config.discoveryApi.getBaseUrl( 'proxy', )}/pagerduty/oncalls?${params}`; - const { oncalls } = await this.getByUrl(url); - return oncalls; + return await this.getByUrl(url); } - triggerAlarm(request: TriggerAlarmRequest): Promise { + triggerAlarm(request: PagerDutyTriggerAlarmRequest): Promise { const { integrationKey, source, description, userName } = request; const body = JSON.stringify({ @@ -139,10 +168,15 @@ export class PagerDutyClient implements PagerDutyApi { url: string, options: RequestOptions, ): Promise { - const response = await fetch(url, options); + const response = await this.config.fetchApi.fetch(url, options); if (response.status === 401) { throw new UnauthorizedError(); } + + if (response.status === 404) { + throw new NotFoundError(); + } + if (!response.ok) { const payload = await response.json(); const errors = payload.errors.map((error: string) => error).join(' '); diff --git a/plugins/pagerduty/src/api/index.ts b/plugins/pagerduty/src/api/index.ts index 015204b1e8..9a0c08973d 100644 --- a/plugins/pagerduty/src/api/index.ts +++ b/plugins/pagerduty/src/api/index.ts @@ -15,4 +15,12 @@ */ export { PagerDutyClient, pagerDutyApiRef, UnauthorizedError } from './client'; -export type { PagerDutyApi } from './types'; +export type { + PagerDutyServiceResponse, + PagerDutyIncidentsResponse, + PagerDutyChangeEventsResponse, + PagerDutyOnCallsResponse, + PagerDutyTriggerAlarmRequest, + PagerDutyClientApiDependencies, + PagerDutyClientApiConfig, +} from './types'; diff --git a/plugins/pagerduty/src/api/types.ts b/plugins/pagerduty/src/api/types.ts index a11cc9d63b..786b0bdffd 100644 --- a/plugins/pagerduty/src/api/types.ts +++ b/plugins/pagerduty/src/api/types.ts @@ -14,10 +14,36 @@ * limitations under the License. */ -import { Incident, ChangeEvent, OnCall, Service } from '../components/types'; -import { DiscoveryApi } from '@backstage/core-plugin-api'; +import { + PagerDutyIncident, + PagerDutyChangeEvent, + PagerDutyOnCall, + PagerDutyService, +} from '../components/types'; +import { DiscoveryApi, FetchApi } from '@backstage/core-plugin-api'; +import { Entity } from '@backstage/catalog-model'; -export type TriggerAlarmRequest = { +export type PagerDutyServicesResponse = { + services: PagerDutyService[]; +}; + +export type PagerDutyServiceResponse = { + service: PagerDutyService; +}; + +export type PagerDutyIncidentsResponse = { + incidents: PagerDutyIncident[]; +}; + +export type PagerDutyChangeEventsResponse = { + change_events: PagerDutyChangeEvent[]; +}; + +export type PagerDutyOnCallsResponse = { + oncalls: PagerDutyOnCall[]; +}; + +export type PagerDutyTriggerAlarmRequest = { integrationKey: string; source: string; description: string; @@ -26,54 +52,46 @@ export type TriggerAlarmRequest = { export interface PagerDutyApi { /** - * Fetches a list of services, filtered by the provided integration key. + * Fetches the service for the provided Entity. * */ - getServiceByIntegrationKey(integrationKey: string): Promise; + getServiceByEntity(entity: Entity): Promise; /** * Fetches a list of incidents a provided service has. * */ - getIncidentsByServiceId(serviceId: string): Promise; + getIncidentsByServiceId( + serviceId: string, + ): Promise; /** * Fetches a list of change events a provided service has. * */ - getChangeEventsByServiceId(serviceId: string): Promise; + getChangeEventsByServiceId( + serviceId: string, + ): Promise; /** * Fetches the list of users in an escalation policy. * */ - getOnCallByPolicyId(policyId: string): Promise; + getOnCallByPolicyId(policyId: string): Promise; /** * Triggers an incident to whoever is on-call. */ - triggerAlarm(request: TriggerAlarmRequest): Promise; + triggerAlarm(request: PagerDutyTriggerAlarmRequest): Promise; } -export type ServicesResponse = { - services: Service[]; -}; - -export type IncidentsResponse = { - incidents: Incident[]; -}; - -export type ChangeEventsResponse = { - change_events: ChangeEvent[]; -}; - -export type OnCallsResponse = { - oncalls: OnCall[]; -}; - -export type ClientApiConfig = { - eventsBaseUrl?: string; +export type PagerDutyClientApiDependencies = { discoveryApi: DiscoveryApi; + fetchApi: FetchApi; +}; + +export type PagerDutyClientApiConfig = PagerDutyClientApiDependencies & { + eventsBaseUrl?: string; }; export type RequestOptions = { diff --git a/plugins/pagerduty/src/components/ChangeEvents/ChangeEventListItem.tsx b/plugins/pagerduty/src/components/ChangeEvents/ChangeEventListItem.tsx index 4912277cee..7b531bf1de 100644 --- a/plugins/pagerduty/src/components/ChangeEvents/ChangeEventListItem.tsx +++ b/plugins/pagerduty/src/components/ChangeEvents/ChangeEventListItem.tsx @@ -26,7 +26,7 @@ import { Typography, } from '@material-ui/core'; import { DateTime, Duration } from 'luxon'; -import { ChangeEvent } from '../types'; +import { PagerDutyChangeEvent } from '../types'; import OpenInBrowserIcon from '@material-ui/icons/OpenInBrowser'; import { BackstageTheme } from '@backstage/theme'; @@ -44,7 +44,7 @@ const useStyles = makeStyles({ }); type Props = { - changeEvent: ChangeEvent; + changeEvent: PagerDutyChangeEvent; }; export const ChangeEventListItem = ({ changeEvent }: Props) => { @@ -54,7 +54,7 @@ export const ChangeEventListItem = ({ changeEvent }: Props) => { const changedAt = DateTime.local() .minus(Duration.fromMillis(duration)) .toRelative({ locale: 'en' }); - let externalLinkElem = null; + let externalLinkElem: JSX.Element | undefined; if (changeEvent.links.length > 0) { const text: string = changeEvent.links[0].text; externalLinkElem = ( diff --git a/plugins/pagerduty/src/components/ChangeEvents/ChangeEvents.test.tsx b/plugins/pagerduty/src/components/ChangeEvents/ChangeEvents.test.tsx index fca2422198..a518e0caea 100644 --- a/plugins/pagerduty/src/components/ChangeEvents/ChangeEvents.test.tsx +++ b/plugins/pagerduty/src/components/ChangeEvents/ChangeEvents.test.tsx @@ -15,7 +15,7 @@ */ import React from 'react'; import { render, waitFor } from '@testing-library/react'; -import { ChangeEvent } from '../types'; +import { PagerDutyChangeEvent } from '../types'; import { TestApiRegistry, wrapInTestApp } from '@backstage/test-utils'; import { pagerDutyApiRef } from '../../api'; import { ApiProvider } from '@backstage/core-app-api'; @@ -30,7 +30,7 @@ describe('Incidents', () => { it('Renders an empty state when there are no change events', async () => { mockPagerDutyApi.getChangeEventsByServiceId = jest .fn() - .mockImplementationOnce(async () => []); + .mockImplementationOnce(async () => ({ change_events: [] })); const { getByText, queryByTestId } = render( wrapInTestApp( @@ -46,37 +46,36 @@ describe('Incidents', () => { it('Renders all change events', async () => { mockPagerDutyApi.getChangeEventsByServiceId = jest .fn() - .mockImplementationOnce( - async () => - [ - { - id: 'id1', - source: 'changeSource1', - html_url: 'www.pdlink.com', - links: [ - { - href: 'www.externalLink1.com', - text: 'link1', - }, - ], - summary: 'summary of event', - timestamp: '2020-07-17T08:42:58.315+0000', - }, - { - id: 'id2', - source: 'changeSource1', - html_url: 'www.pdlink.com/link', - links: [ - { - href: 'www.externalLink1.com', - text: 'link1', - }, - ], - summary: 'sum of EVENT', - timestamp: '2020-07-18T08:42:58.315+0000', - }, - ] as ChangeEvent[], - ); + .mockImplementationOnce(async () => ({ + change_events: [ + { + id: 'id1', + source: 'changeSource1', + html_url: 'www.pdlink.com', + links: [ + { + href: 'www.externalLink1.com', + text: 'link1', + }, + ], + summary: 'summary of event', + timestamp: '2020-07-17T08:42:58.315+0000', + }, + { + id: 'id2', + source: 'changeSource1', + html_url: 'www.pdlink.com/link', + links: [ + { + href: 'www.externalLink1.com', + text: 'link1', + }, + ], + summary: 'sum of EVENT', + timestamp: '2020-07-18T08:42:58.315+0000', + }, + ] as PagerDutyChangeEvent[], + })); const { getByText, getAllByTitle, queryByTestId } = render( wrapInTestApp( @@ -95,36 +94,35 @@ describe('Incidents', () => { it('Does not render a pagerduty link when html_url is not present in response', async () => { mockPagerDutyApi.getChangeEventsByServiceId = jest .fn() - .mockImplementationOnce( - async () => - [ - { - id: 'id1', - source: 'changeSource1', - links: [ - { - href: 'www.externalLink1.com', - text: 'link1', - }, - ], - summary: 'summary of event', - timestamp: '2020-07-17T08:42:58.315+0000', - }, - { - id: 'id2', - source: 'changeSource1', - html_url: 'www.pdlink.com/link', - links: [ - { - href: 'www.externalLink1.com', - text: 'link1', - }, - ], - summary: 'sum of EVENT', - timestamp: '2020-07-18T08:42:58.315+0000', - }, - ] as ChangeEvent[], - ); + .mockImplementationOnce(async () => ({ + change_events: [ + { + id: 'id1', + source: 'changeSource1', + links: [ + { + href: 'www.externalLink1.com', + text: 'link1', + }, + ], + summary: 'summary of event', + timestamp: '2020-07-17T08:42:58.315+0000', + }, + { + id: 'id2', + source: 'changeSource1', + html_url: 'www.pdlink.com/link', + links: [ + { + href: 'www.externalLink1.com', + text: 'link1', + }, + ], + summary: 'sum of EVENT', + timestamp: '2020-07-18T08:42:58.315+0000', + }, + ] as PagerDutyChangeEvent[], + })); const { getByText, getAllByTitle, queryByTestId } = render( wrapInTestApp( diff --git a/plugins/pagerduty/src/components/ChangeEvents/ChangeEvents.tsx b/plugins/pagerduty/src/components/ChangeEvents/ChangeEvents.tsx index 58b4dd6acf..4b19257cdc 100644 --- a/plugins/pagerduty/src/components/ChangeEvents/ChangeEvents.tsx +++ b/plugins/pagerduty/src/components/ChangeEvents/ChangeEvents.tsx @@ -33,7 +33,10 @@ export const ChangeEvents = ({ serviceId, refreshEvents }: Props) => { const api = useApi(pagerDutyApiRef); const [{ value: changeEvents, loading, error }, getChangeEvents] = useAsyncFn( - async () => await api.getChangeEventsByServiceId(serviceId), + async () => { + const { change_events } = await api.getChangeEventsByServiceId(serviceId); + return change_events; + }, ); useEffect(() => { diff --git a/plugins/pagerduty/src/components/Errors/ServiceNotFoundError.tsx b/plugins/pagerduty/src/components/Errors/ServiceNotFoundError.tsx new file mode 100644 index 0000000000..d8b61ef7cd --- /dev/null +++ b/plugins/pagerduty/src/components/Errors/ServiceNotFoundError.tsx @@ -0,0 +1,35 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React from 'react'; +import { Button } from '@material-ui/core'; +import { EmptyState } from '@backstage/core-components'; + +export const ServiceNotFoundError = () => ( + + Read More + + } + /> +); diff --git a/plugins/pagerduty/src/components/Errors/index.ts b/plugins/pagerduty/src/components/Errors/index.ts index df255749e0..6c35e0bfd1 100644 --- a/plugins/pagerduty/src/components/Errors/index.ts +++ b/plugins/pagerduty/src/components/Errors/index.ts @@ -15,3 +15,4 @@ */ export { MissingTokenError } from './MissingTokenError'; +export { ServiceNotFoundError } from './ServiceNotFoundError'; diff --git a/plugins/pagerduty/src/components/Escalation/Escalation.test.tsx b/plugins/pagerduty/src/components/Escalation/Escalation.test.tsx index 47f18002fe..97c02a37f3 100644 --- a/plugins/pagerduty/src/components/Escalation/Escalation.test.tsx +++ b/plugins/pagerduty/src/components/Escalation/Escalation.test.tsx @@ -17,7 +17,7 @@ import React from 'react'; import { render, waitFor } from '@testing-library/react'; import { EscalationPolicy } from './EscalationPolicy'; import { TestApiRegistry, wrapInTestApp } from '@backstage/test-utils'; -import { User } from '../types'; +import { PagerDutyUser } from '../types'; import { pagerDutyApiRef } from '../../api'; import { ApiProvider } from '@backstage/core-app-api'; @@ -30,7 +30,7 @@ describe('Escalation', () => { it('Handles an empty response', async () => { mockPagerDutyApi.getOnCallByPolicyId = jest .fn() - .mockImplementationOnce(async () => []); + .mockImplementationOnce(async () => ({ oncalls: [] })); const { getByText, queryByTestId } = render( wrapInTestApp( @@ -48,17 +48,19 @@ describe('Escalation', () => { it('Render a list of users', async () => { mockPagerDutyApi.getOnCallByPolicyId = jest .fn() - .mockImplementationOnce(async () => [ - { - user: { - name: 'person1', - id: 'p1', - summary: 'person1', - email: 'person1@example.com', - html_url: 'http://a.com/id1', - } as User, - }, - ]); + .mockImplementationOnce(async () => ({ + oncalls: [ + { + user: { + name: 'person1', + id: 'p1', + summary: 'person1', + email: 'person1@example.com', + html_url: 'http://a.com/id1', + } as PagerDutyUser, + }, + ], + })); const { getByText, queryByTestId } = render( wrapInTestApp( diff --git a/plugins/pagerduty/src/components/Escalation/EscalationPolicy.tsx b/plugins/pagerduty/src/components/Escalation/EscalationPolicy.tsx index 704887be58..8602733ee3 100644 --- a/plugins/pagerduty/src/components/Escalation/EscalationPolicy.tsx +++ b/plugins/pagerduty/src/components/Escalation/EscalationPolicy.tsx @@ -37,7 +37,7 @@ export const EscalationPolicy = ({ policyId }: Props) => { loading, error, } = useAsync(async () => { - const oncalls = await api.getOnCallByPolicyId(policyId); + const { oncalls } = await api.getOnCallByPolicyId(policyId); const usersItem = oncalls .sort((a, b) => a.escalation_level - b.escalation_level) .map(oncall => oncall.user); diff --git a/plugins/pagerduty/src/components/Escalation/EscalationUser.tsx b/plugins/pagerduty/src/components/Escalation/EscalationUser.tsx index a460906d22..d418ad02c0 100644 --- a/plugins/pagerduty/src/components/Escalation/EscalationUser.tsx +++ b/plugins/pagerduty/src/components/Escalation/EscalationUser.tsx @@ -28,7 +28,7 @@ import { import Avatar from '@material-ui/core/Avatar'; import EmailIcon from '@material-ui/icons/Email'; import OpenInBrowserIcon from '@material-ui/icons/OpenInBrowser'; -import { User } from '../types'; +import { PagerDutyUser } from '../types'; const useStyles = makeStyles({ listItemPrimary: { @@ -37,7 +37,7 @@ const useStyles = makeStyles({ }); type Props = { - user: User; + user: PagerDutyUser; }; export const EscalationUser = ({ user }: Props) => { diff --git a/plugins/pagerduty/src/components/Incident/IncidentListItem.tsx b/plugins/pagerduty/src/components/Incident/IncidentListItem.tsx index 807685a83e..5945c45e83 100644 --- a/plugins/pagerduty/src/components/Incident/IncidentListItem.tsx +++ b/plugins/pagerduty/src/components/Incident/IncidentListItem.tsx @@ -29,7 +29,7 @@ import { import Done from '@material-ui/icons/Done'; import Warning from '@material-ui/icons/Warning'; import { DateTime, Duration } from 'luxon'; -import { Incident } from '../types'; +import { PagerDutyIncident } from '../types'; import OpenInBrowserIcon from '@material-ui/icons/OpenInBrowser'; import { BackstageTheme } from '@backstage/theme'; @@ -61,7 +61,7 @@ const useStyles = makeStyles(theme => ({ })); type Props = { - incident: Incident; + incident: PagerDutyIncident; }; export const IncidentListItem = ({ incident }: Props) => { diff --git a/plugins/pagerduty/src/components/Incident/Incidents.test.tsx b/plugins/pagerduty/src/components/Incident/Incidents.test.tsx index 74e7d82e8b..a83874a995 100644 --- a/plugins/pagerduty/src/components/Incident/Incidents.test.tsx +++ b/plugins/pagerduty/src/components/Incident/Incidents.test.tsx @@ -18,7 +18,7 @@ import { render, waitFor } from '@testing-library/react'; import { Incidents } from './Incidents'; import { TestApiRegistry, wrapInTestApp } from '@backstage/test-utils'; import { pagerDutyApiRef } from '../../api'; -import { Incident } from '../types'; +import { PagerDutyIncident } from '../types'; import { ApiProvider } from '@backstage/core-app-api'; const mockPagerDutyApi = { @@ -30,7 +30,7 @@ describe('Incidents', () => { it('Renders an empty state when there are no incidents', async () => { mockPagerDutyApi.getIncidentsByServiceId = jest .fn() - .mockImplementationOnce(async () => []); + .mockImplementationOnce(async () => ({ incidents: [] })); const { getByText, queryByTestId } = render( wrapInTestApp( @@ -44,9 +44,10 @@ describe('Incidents', () => { }); it('Renders all incidents', async () => { - mockPagerDutyApi.getIncidentsByServiceId = jest.fn().mockImplementationOnce( - async () => - [ + mockPagerDutyApi.getIncidentsByServiceId = jest + .fn() + .mockImplementationOnce(async () => ({ + incidents: [ { id: 'id1', status: 'triggered', @@ -82,8 +83,8 @@ describe('Incidents', () => { html_url: 'http://a.com/id2', serviceId: 'sId2', }, - ] as Incident[], - ); + ] as PagerDutyIncident[], + })); const { getByText, getAllByTitle, queryByTestId } = render( wrapInTestApp( diff --git a/plugins/pagerduty/src/components/Incident/Incidents.tsx b/plugins/pagerduty/src/components/Incident/Incidents.tsx index 06dd50bdaa..2e0e201c79 100644 --- a/plugins/pagerduty/src/components/Incident/Incidents.tsx +++ b/plugins/pagerduty/src/components/Incident/Incidents.tsx @@ -34,7 +34,12 @@ export const Incidents = ({ serviceId, refreshIncidents }: Props) => { const api = useApi(pagerDutyApiRef); const [{ value: incidents, loading, error }, getIncidents] = useAsyncFn( - async () => await api.getIncidentsByServiceId(serviceId), + async () => { + const { incidents: foundIncidents } = await api.getIncidentsByServiceId( + serviceId, + ); + return foundIncidents; + }, ); useEffect(() => { diff --git a/plugins/pagerduty/src/components/PagerDutyCard/index.test.tsx b/plugins/pagerduty/src/components/PagerDutyCard/index.test.tsx index e0a857ccc7..85202a9cfc 100644 --- a/plugins/pagerduty/src/components/PagerDutyCard/index.test.tsx +++ b/plugins/pagerduty/src/components/PagerDutyCard/index.test.tsx @@ -15,26 +15,17 @@ */ import React from 'react'; import { render, waitFor, fireEvent, act } from '@testing-library/react'; -import { PagerDutyCard } from '../PagerDutyCard'; +import { PagerDutyCard, isPluginApplicableToEntity } from '../PagerDutyCard'; import { Entity } from '@backstage/catalog-model'; import { EntityProvider } from '@backstage/plugin-catalog-react'; +import { NotFoundError } from '@backstage/errors'; import { TestApiRegistry, wrapInTestApp } from '@backstage/test-utils'; import { pagerDutyApiRef, UnauthorizedError, PagerDutyClient } from '../../api'; -import { Service, User } from '../types'; +import { PagerDutyService, PagerDutyUser } from '../types'; import { alertApiRef } from '@backstage/core-plugin-api'; import { ApiProvider } from '@backstage/core-app-api'; -const mockPagerDutyApi: Partial = { - getServiceByIntegrationKey: async () => [], - getOnCallByPolicyId: async () => [], - getIncidentsByServiceId: async () => [], -}; - -const apis = TestApiRegistry.from( - [pagerDutyApiRef, mockPagerDutyApi], - [alertApiRef, {}], -); const entity: Entity = { apiVersion: 'backstage.io/v1alpha1', kind: 'Component', @@ -46,7 +37,39 @@ const entity: Entity = { }, }; -const user: User = { +const entityWithoutAnnotations: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'pagerduty-test', + annotations: {}, + }, +}; + +const entityWithServiceId: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'pagerduty-test', + annotations: { + 'pagerduty.com/service-id': 'def456', + }, + }, +}; + +const entityWithAllAnnotations: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'pagerduty-test', + annotations: { + 'pagerduty.com/integration-key': 'abc123', + 'pagerduty.com/service-id': 'def456', + }, + }, +}; + +const user: PagerDutyUser = { name: 'person1', id: 'p1', summary: 'person1', @@ -54,8 +77,8 @@ const user: User = { html_url: 'http://a.com/id1', }; -const service: Service = { - id: 'abc', +const service: PagerDutyService = { + id: 'def456', name: 'pagerduty-name', html_url: 'www.example.com', escalation_policy: { @@ -63,14 +86,51 @@ const service: Service = { user: user, html_url: 'http://a.com/id1', }, - integrationKey: 'abcd', + integrationKey: 'abc123', }; +const mockPagerDutyApi: Partial = { + getServiceByEntity: async () => ({ service }), + getOnCallByPolicyId: async () => ({ oncalls: [] }), + getIncidentsByServiceId: async () => ({ incidents: [] }), +}; + +const apis = TestApiRegistry.from( + [pagerDutyApiRef, mockPagerDutyApi], + [alertApiRef, {}], +); + +describe('isPluginApplicableToEntity', () => { + describe('when entity has no annotations', () => { + it('returns false', () => { + expect(isPluginApplicableToEntity(entityWithoutAnnotations)).toBe(false); + }); + }); + + describe('when entity has the pagerduty.com/integration-key annotation', () => { + it('returns true', () => { + expect(isPluginApplicableToEntity(entity)).toBe(true); + }); + }); + + describe('when entity has the pagerduty.com/service-id annotation', () => { + it('returns true', () => { + expect(isPluginApplicableToEntity(entityWithServiceId)).toBe(true); + }); + }); + + describe('when entity has all annotations', () => { + it('returns true', () => { + expect(isPluginApplicableToEntity(entityWithAllAnnotations)).toBe(true); + }); + }); +}); + describe('PageDutyCard', () => { it('Render pagerduty', async () => { - mockPagerDutyApi.getServiceByIntegrationKey = jest + mockPagerDutyApi.getServiceByEntity = jest .fn() - .mockImplementationOnce(async () => [service]); + .mockImplementationOnce(async () => ({ service })); const { getByText, queryByTestId } = render( wrapInTestApp( @@ -89,7 +149,7 @@ describe('PageDutyCard', () => { }); it('Handles custom error for missing token', async () => { - mockPagerDutyApi.getServiceByIntegrationKey = jest + mockPagerDutyApi.getServiceByEntity = jest .fn() .mockRejectedValueOnce(new UnauthorizedError()); @@ -106,8 +166,26 @@ describe('PageDutyCard', () => { expect(getByText('Missing or invalid PagerDuty Token')).toBeInTheDocument(); }); + it('Handles custom NotFoundError', async () => { + mockPagerDutyApi.getServiceByEntity = jest + .fn() + .mockRejectedValueOnce(new NotFoundError()); + + const { getByText, queryByTestId } = render( + wrapInTestApp( + + + + + , + ), + ); + await waitFor(() => !queryByTestId('progress')); + expect(getByText('PagerDuty Service Not Found')).toBeInTheDocument(); + }); + it('handles general error', async () => { - mockPagerDutyApi.getServiceByIntegrationKey = jest + mockPagerDutyApi.getServiceByEntity = jest .fn() .mockRejectedValueOnce(new Error('An error occurred')); const { getByText, queryByTestId } = render( @@ -127,10 +205,11 @@ describe('PageDutyCard', () => { ), ).toBeInTheDocument(); }); + it('opens the dialog when trigger button is clicked', async () => { - mockPagerDutyApi.getServiceByIntegrationKey = jest + mockPagerDutyApi.getServiceByEntity = jest .fn() - .mockImplementationOnce(async () => [service]); + .mockImplementationOnce(async () => ({ service })); const { getByText, queryByTestId, getByRole } = render( wrapInTestApp( @@ -150,4 +229,131 @@ describe('PageDutyCard', () => { }); expect(getByRole('dialog')).toBeInTheDocument(); }); + + describe('when entity has the pagerduty.com/service-id annotation', () => { + it('Renders PagerDuty service information', async () => { + mockPagerDutyApi.getServiceByEntity = jest + .fn() + .mockImplementationOnce(async () => ({ service })); + + const { getByText, queryByTestId } = render( + wrapInTestApp( + + + + + , + ), + ); + await waitFor(() => !queryByTestId('progress')); + expect(getByText('Service Directory')).toBeInTheDocument(); + expect(getByText('Create Incident')).toBeInTheDocument(); + expect(getByText('Nice! No incidents found!')).toBeInTheDocument(); + expect(getByText('Empty escalation policy')).toBeInTheDocument(); + }); + + it('Handles custom error for missing token', async () => { + mockPagerDutyApi.getServiceByEntity = jest + .fn() + .mockRejectedValueOnce(new UnauthorizedError()); + + const { getByText, queryByTestId } = render( + wrapInTestApp( + + + + + , + ), + ); + await waitFor(() => !queryByTestId('progress')); + expect( + getByText('Missing or invalid PagerDuty Token'), + ).toBeInTheDocument(); + }); + + it('Handles custom NotFoundError', async () => { + mockPagerDutyApi.getServiceByEntity = jest + .fn() + .mockRejectedValueOnce(new NotFoundError()); + + const { getByText, queryByTestId } = render( + wrapInTestApp( + + + + + , + ), + ); + await waitFor(() => !queryByTestId('progress')); + expect(getByText('PagerDuty Service Not Found')).toBeInTheDocument(); + }); + + it('handles general error', async () => { + mockPagerDutyApi.getServiceByEntity = jest + .fn() + .mockRejectedValueOnce(new Error('An error occurred')); + const { getByText, queryByTestId } = render( + wrapInTestApp( + + + + + , + ), + ); + await waitFor(() => !queryByTestId('progress')); + + expect( + getByText( + 'Error encountered while fetching information. An error occurred', + ), + ).toBeInTheDocument(); + }); + + it('disables the Create Incident button', async () => { + mockPagerDutyApi.getServiceByEntity = jest + .fn() + .mockImplementationOnce(async () => ({ service })); + + const { queryByTestId, getByTitle } = render( + wrapInTestApp( + + + + + , + ), + ); + await waitFor(() => !queryByTestId('progress')); + expect( + getByTitle('Must provide an integration-key to create incidents') + .className, + ).toMatch('disabled'); + }); + }); + + describe('when entity has all annotations', () => { + it('queries by integration key', async () => { + mockPagerDutyApi.getServiceByEntity = jest + .fn() + .mockImplementationOnce(async () => ({ service })); + + const { getByText, queryByTestId } = render( + wrapInTestApp( + + + + + , + ), + ); + await waitFor(() => !queryByTestId('progress')); + expect(getByText('Service Directory')).toBeInTheDocument(); + expect(getByText('Create Incident')).toBeInTheDocument(); + expect(getByText('Nice! No incidents found!')).toBeInTheDocument(); + expect(getByText('Empty escalation policy')).toBeInTheDocument(); + }); + }); }); diff --git a/plugins/pagerduty/src/components/PagerDutyCard/index.tsx b/plugins/pagerduty/src/components/PagerDutyCard/index.tsx index bbade09e51..e8ba2c522c 100644 --- a/plugins/pagerduty/src/components/PagerDutyCard/index.tsx +++ b/plugins/pagerduty/src/components/PagerDutyCard/index.tsx @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import React, { useState, useCallback } from 'react'; +import React, { ReactNode, useState, useCallback } from 'react'; import { Entity } from '@backstage/catalog-model'; import { Card, CardHeader, Divider, CardContent } from '@material-ui/core'; import { Incidents } from '../Incident'; @@ -22,28 +22,39 @@ import useAsync from 'react-use/lib/useAsync'; import { Alert } from '@material-ui/lab'; import { pagerDutyApiRef, UnauthorizedError } from '../../api'; import AlarmAddIcon from '@material-ui/icons/AlarmAdd'; -import { MissingTokenError } from '../Errors/MissingTokenError'; +import { MissingTokenError, ServiceNotFoundError } from '../Errors'; import WebIcon from '@material-ui/icons/Web'; import DateRangeIcon from '@material-ui/icons/DateRange'; -import { usePagerdutyEntity } from '../../hooks'; -import { PAGERDUTY_INTEGRATION_KEY } from '../constants'; +import { PAGERDUTY_INTEGRATION_KEY, PAGERDUTY_SERVICE_ID } from '../constants'; import { TriggerDialog } from '../TriggerDialog'; import { ChangeEvents } from '../ChangeEvents'; import { useApi } from '@backstage/core-plugin-api'; +import { NotFoundError } from '@backstage/errors'; import { Progress, HeaderIconLinkRow, IconLinkVerticalProps, TabbedCard, CardTab, + InfoCard, } from '@backstage/core-components'; +import { useEntity } from '@backstage/plugin-catalog-react'; +import { getPagerDutyEntity } from '../pagerDutyEntity'; + +const BasicCard = ({ children }: { children: ReactNode }) => ( + {children} +); export const isPluginApplicableToEntity = (entity: Entity) => - Boolean(entity.metadata.annotations?.[PAGERDUTY_INTEGRATION_KEY]); + Boolean( + entity.metadata.annotations?.[PAGERDUTY_INTEGRATION_KEY] || + entity.metadata.annotations?.[PAGERDUTY_SERVICE_ID], + ); export const PagerDutyCard = () => { - const { integrationKey } = usePagerdutyEntity(); + const { entity } = useEntity(); + const pagerDutyEntity = getPagerDutyEntity(entity); const api = useApi(pagerDutyApiRef); const [refreshIncidents, setRefreshIncidents] = useState(false); const [refreshChangeEvents, setRefreshChangeEvents] = @@ -67,33 +78,44 @@ export const PagerDutyCard = () => { loading, error, } = useAsync(async () => { - const services = await api.getServiceByIntegrationKey( - integrationKey as string, - ); + const { service: foundService } = await api.getServiceByEntity(entity); return { - id: services[0].id, - name: services[0].name, - url: services[0].html_url, - policyId: services[0].escalation_policy.id, - policyLink: services[0].escalation_policy.html_url, + id: foundService.id, + name: foundService.name, + url: foundService.html_url, + policyId: foundService.escalation_policy.id, + policyLink: foundService.escalation_policy.html_url, }; }); - if (error instanceof UnauthorizedError) { - return ; - } - if (error) { - return ( - - Error encountered while fetching information. {error.message} - - ); + let errorNode: ReactNode; + + switch (error.constructor) { + case UnauthorizedError: + errorNode = ; + break; + case NotFoundError: + errorNode = ; + break; + default: + errorNode = ( + + Error encountered while fetching information. {error.message} + + ); + } + + return {errorNode}; } if (loading) { - return ; + return ( + + + + ); } const serviceLink: IconLinkVerticalProps = { @@ -102,11 +124,21 @@ export const PagerDutyCard = () => { icon: , }; + /** + * In order to create incidents using the REST API, a valid user email address must be present. + * There is no guarantee the current user entity has a valid email association, so instead just + * only allow triggering incidents when an integration key is present. + */ + const createIncidentDisabled = !pagerDutyEntity.integrationKey; const triggerLink: IconLinkVerticalProps = { label: 'Create Incident', onClick: showDialog, icon: , color: 'secondary', + disabled: createIncidentDisabled, + title: createIncidentDisabled + ? 'Must provide an integration-key to create incidents' + : '', }; const escalationPolicyLink: IconLinkVerticalProps = { @@ -145,12 +177,14 @@ export const PagerDutyCard = () => { - + {!createIncidentDisabled && ( + + )} ); }; diff --git a/plugins/pagerduty/src/components/TriggerDialog/TriggerDialog.tsx b/plugins/pagerduty/src/components/TriggerDialog/TriggerDialog.tsx index 171238cdd3..4dc4f04699 100644 --- a/plugins/pagerduty/src/components/TriggerDialog/TriggerDialog.tsx +++ b/plugins/pagerduty/src/components/TriggerDialog/TriggerDialog.tsx @@ -60,7 +60,7 @@ export const TriggerDialog = ({ defaultKind: 'User', defaultNamespace: DEFAULT_NAMESPACE, }); - await api.triggerAlarm({ + return await api.triggerAlarm({ integrationKey: integrationKey as string, source: window.location.toString(), description: descriptions, diff --git a/plugins/pagerduty/src/components/constants.ts b/plugins/pagerduty/src/components/constants.ts index bfe1aea298..01308590f1 100644 --- a/plugins/pagerduty/src/components/constants.ts +++ b/plugins/pagerduty/src/components/constants.ts @@ -14,3 +14,4 @@ * limitations under the License. */ export const PAGERDUTY_INTEGRATION_KEY = 'pagerduty.com/integration-key'; +export const PAGERDUTY_SERVICE_ID = 'pagerduty.com/service-id'; diff --git a/plugins/pagerduty/src/components/index.ts b/plugins/pagerduty/src/components/index.ts new file mode 100644 index 0000000000..17bb2c03dc --- /dev/null +++ b/plugins/pagerduty/src/components/index.ts @@ -0,0 +1,32 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export type { + PagerDutyChangeEvent, + PagerDutyIncident, + PagerDutyService, + PagerDutyOnCall, + PagerDutyAssignee, + PagerDutyUser, +} from './types'; + +export { + isPluginApplicableToEntity, + isPluginApplicableToEntity as isPagerDutyAvailable, + PagerDutyCard, +} from './PagerDutyCard'; + +export { TriggerButton } from './TriggerButton'; diff --git a/plugins/pagerduty/src/components/pagerDutyEntity.test.ts b/plugins/pagerduty/src/components/pagerDutyEntity.test.ts new file mode 100644 index 0000000000..cf551d3d6e --- /dev/null +++ b/plugins/pagerduty/src/components/pagerDutyEntity.test.ts @@ -0,0 +1,98 @@ +/* + * 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 { Entity } from '@backstage/catalog-model'; +import { getPagerDutyEntity } from './pagerDutyEntity'; + +const entity: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'pagerduty-test', + annotations: { + 'pagerduty.com/integration-key': 'abc123', + }, + }, +}; + +const entityWithoutAnnotations: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'pagerduty-test', + annotations: {}, + }, +}; + +const entityWithServiceId: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'pagerduty-test', + annotations: { + 'pagerduty.com/service-id': 'def456', + }, + }, +}; + +const entityWithAllAnnotations: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'pagerduty-test', + annotations: { + 'pagerduty.com/integration-key': 'abc123', + 'pagerduty.com/service-id': 'def456', + }, + }, +}; + +describe('getPagerDutyEntity', () => { + describe('when entity has no annotations', () => { + it('returns a PagerDutyEntity', () => { + expect(getPagerDutyEntity(entityWithoutAnnotations)).toEqual({ + name: 'pagerduty-test', + }); + }); + }); + + describe('when entity has integration key annotation', () => { + it('returns a PagerDutyEntity', () => { + expect(getPagerDutyEntity(entity)).toEqual({ + name: 'pagerduty-test', + integrationKey: 'abc123', + }); + }); + }); + + describe('when entity has service id annotation', () => { + it('returns a PagerDutyEntity', () => { + expect(getPagerDutyEntity(entityWithServiceId)).toEqual({ + name: 'pagerduty-test', + serviceId: 'def456', + }); + }); + }); + + describe('when entity has all annotations', () => { + it('returns a PagerDutyEntity', () => { + expect(getPagerDutyEntity(entityWithAllAnnotations)).toEqual({ + name: 'pagerduty-test', + integrationKey: 'abc123', + serviceId: 'def456', + }); + }); + }); +}); diff --git a/plugins/pagerduty/src/components/pagerDutyEntity.ts b/plugins/pagerduty/src/components/pagerDutyEntity.ts new file mode 100644 index 0000000000..950525881e --- /dev/null +++ b/plugins/pagerduty/src/components/pagerDutyEntity.ts @@ -0,0 +1,29 @@ +/* + * 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 { Entity } from '@backstage/catalog-model'; +import { PagerDutyEntity } from '../types'; +import { PAGERDUTY_INTEGRATION_KEY, PAGERDUTY_SERVICE_ID } from './constants'; + +export function getPagerDutyEntity(entity: Entity): PagerDutyEntity { + const { + [PAGERDUTY_INTEGRATION_KEY]: integrationKey, + [PAGERDUTY_SERVICE_ID]: serviceId, + } = entity.metadata.annotations || ({} as Record); + const name = entity.metadata.name; + + return { integrationKey, serviceId, name }; +} diff --git a/plugins/pagerduty/src/components/types.ts b/plugins/pagerduty/src/components/types.ts index d0d05d82d1..89cdffea16 100644 --- a/plugins/pagerduty/src/components/types.ts +++ b/plugins/pagerduty/src/components/types.ts @@ -14,11 +14,11 @@ * limitations under the License. */ -export type ChangeEvent = { +export type PagerDutyChangeEvent = { id: string; integration: [ { - service: Service; + service: PagerDutyService; }, ]; source: string; @@ -33,44 +33,44 @@ export type ChangeEvent = { timestamp: string; }; -export type Incident = { +export type PagerDutyIncident = { id: string; title: string; status: string; html_url: string; assignments: [ { - assignee: Assignee; + assignee: PagerDutyAssignee; }, ]; serviceId: string; created_at: string; }; -export type Service = { +export type PagerDutyService = { id: string; name: string; html_url: string; integrationKey: string; escalation_policy: { id: string; - user: User; + user: PagerDutyUser; html_url: string; }; }; -export type OnCall = { - user: User; +export type PagerDutyOnCall = { + user: PagerDutyUser; escalation_level: number; }; -export type Assignee = { +export type PagerDutyAssignee = { id: string; summary: string; html_url: string; }; -export type User = { +export type PagerDutyUser = { id: string; summary: string; email: string; diff --git a/plugins/pagerduty/src/hooks/index.ts b/plugins/pagerduty/src/hooks/index.ts index 40a34788f0..8950e443e2 100644 --- a/plugins/pagerduty/src/hooks/index.ts +++ b/plugins/pagerduty/src/hooks/index.ts @@ -15,14 +15,11 @@ */ import { useEntity } from '@backstage/plugin-catalog-react'; +import { PagerDutyEntity } from '../types'; +import { getPagerDutyEntity } from '../components/pagerDutyEntity'; -import { PAGERDUTY_INTEGRATION_KEY } from '../components/constants'; - -export function usePagerdutyEntity() { +export function usePagerdutyEntity(): PagerDutyEntity { const { entity } = useEntity(); - const integrationKey = - entity.metadata.annotations?.[PAGERDUTY_INTEGRATION_KEY]; - const name = entity.metadata.name; - return { integrationKey, name }; + return getPagerDutyEntity(entity); } diff --git a/plugins/pagerduty/src/index.ts b/plugins/pagerduty/src/index.ts index 8c970c01bb..2e0eddc9a8 100644 --- a/plugins/pagerduty/src/index.ts +++ b/plugins/pagerduty/src/index.ts @@ -25,14 +25,8 @@ export { pagerDutyPlugin as plugin, EntityPagerDutyCard, } from './plugin'; -export { - isPluginApplicableToEntity, - isPluginApplicableToEntity as isPagerDutyAvailable, - PagerDutyCard, -} from './components/PagerDutyCard'; -export { TriggerButton } from './components/TriggerButton'; -export { - PagerDutyClient, - pagerDutyApiRef, - UnauthorizedError, -} from './api/client'; + +export * from './components'; +export * from './api'; + +export type { PagerDutyEntity } from './types'; diff --git a/plugins/pagerduty/src/plugin.ts b/plugins/pagerduty/src/plugin.ts index d1c4e6d35b..d49de29416 100644 --- a/plugins/pagerduty/src/plugin.ts +++ b/plugins/pagerduty/src/plugin.ts @@ -19,6 +19,7 @@ import { createPlugin, createRouteRef, discoveryApiRef, + fetchApiRef, configApiRef, createComponentExtension, } from '@backstage/core-plugin-api'; @@ -32,9 +33,13 @@ export const pagerDutyPlugin = createPlugin({ apis: [ createApiFactory({ api: pagerDutyApiRef, - deps: { discoveryApi: discoveryApiRef, configApi: configApiRef }, - factory: ({ configApi, discoveryApi }) => - PagerDutyClient.fromConfig(configApi, discoveryApi), + deps: { + discoveryApi: discoveryApiRef, + configApi: configApiRef, + fetchApi: fetchApiRef, + }, + factory: ({ configApi, discoveryApi, fetchApi }) => + PagerDutyClient.fromConfig(configApi, { discoveryApi, fetchApi }), }), ], }); diff --git a/plugins/pagerduty/src/types.ts b/plugins/pagerduty/src/types.ts new file mode 100644 index 0000000000..07f7f2bbe3 --- /dev/null +++ b/plugins/pagerduty/src/types.ts @@ -0,0 +1,21 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export type PagerDutyEntity = { + integrationKey?: string; + serviceId?: string; + name: string; +}; diff --git a/plugins/periskop-backend/CHANGELOG.md b/plugins/periskop-backend/CHANGELOG.md index 195afe4545..3a4de21e28 100644 --- a/plugins/periskop-backend/CHANGELOG.md +++ b/plugins/periskop-backend/CHANGELOG.md @@ -1,5 +1,35 @@ # @backstage/plugin-periskop-backend +## 0.1.4 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/backend-common@0.14.0 + +## 0.1.4-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0-next.2 + +## 0.1.4-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/backend-common@0.13.6-next.1 + +## 0.1.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + ## 0.1.3 ### Patch Changes diff --git a/plugins/periskop-backend/package.json b/plugins/periskop-backend/package.json index fe60ec120c..a555124743 100644 --- a/plugins/periskop-backend/package.json +++ b/plugins/periskop-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-periskop-backend", - "version": "0.1.3", + "version": "0.1.4", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -24,7 +24,7 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/backend-common": "^0.13.3", + "@backstage/backend-common": "^0.14.0", "@backstage/config": "^1.0.1", "@types/express": "*", "cross-fetch": "^3.0.6", @@ -35,9 +35,9 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", + "@backstage/cli": "^0.17.2", "@types/supertest": "^2.0.8", - "msw": "^0.35.0", + "msw": "^0.42.0", "supertest": "^6.1.6" }, "files": [ diff --git a/plugins/periskop/CHANGELOG.md b/plugins/periskop/CHANGELOG.md index 33265d79c6..9c5887509b 100644 --- a/plugins/periskop/CHANGELOG.md +++ b/plugins/periskop/CHANGELOG.md @@ -1,5 +1,35 @@ # @backstage/plugin-periskop +## 0.1.4 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## 0.1.4-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## 0.1.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + ## 0.1.3 ### Patch Changes diff --git a/plugins/periskop/package.json b/plugins/periskop/package.json index 92648392a2..c7c6af850b 100644 --- a/plugins/periskop/package.json +++ b/plugins/periskop/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-periskop", - "version": "0.1.3", + "version": "0.1.4", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -25,11 +25,11 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-model": "^1.0.2", - "@backstage/core-components": "^0.9.4", - "@backstage/core-plugin-api": "^1.0.2", + "@backstage/catalog-model": "^1.0.3", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", "@backstage/errors": "^1.0.0", - "@backstage/plugin-catalog-react": "^1.1.0", + "@backstage/plugin-catalog-react": "^1.1.1", "@backstage/theme": "^0.2.15", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -42,10 +42,10 @@ "react-dom": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/core-app-api": "^1.0.2", - "@backstage/dev-utils": "^1.0.2", - "@backstage/test-utils": "^1.1.0", + "@backstage/cli": "^0.17.2", + "@backstage/core-app-api": "^1.0.3", + "@backstage/dev-utils": "^1.0.3", + "@backstage/test-utils": "^1.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", @@ -53,7 +53,7 @@ "@types/luxon": "^2.0.4", "@types/node": "^16.11.26", "cross-fetch": "^3.1.5", - "msw": "^0.35.0" + "msw": "^0.42.0" }, "files": [ "dist", diff --git a/plugins/permission-backend/CHANGELOG.md b/plugins/permission-backend/CHANGELOG.md index 4837102f27..f0e391d8d8 100644 --- a/plugins/permission-backend/CHANGELOG.md +++ b/plugins/permission-backend/CHANGELOG.md @@ -1,5 +1,45 @@ # @backstage/plugin-permission-backend +## 0.5.8 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-permission-node@0.6.2 + - @backstage/backend-common@0.14.0 + - @backstage/plugin-auth-node@0.2.2 + - @backstage/plugin-permission-common@0.6.2 + +## 0.5.8-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0-next.2 + - @backstage/plugin-auth-node@0.2.2-next.2 + - @backstage/plugin-permission-node@0.6.2-next.2 + +## 0.5.8-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/backend-common@0.13.6-next.1 + - @backstage/plugin-auth-node@0.2.2-next.1 + - @backstage/plugin-permission-common@0.6.2-next.0 + - @backstage/plugin-permission-node@0.6.2-next.1 + +## 0.5.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + - @backstage/plugin-auth-node@0.2.2-next.0 + - @backstage/plugin-permission-node@0.6.2-next.0 + ## 0.5.7 ### Patch Changes diff --git a/plugins/permission-backend/package.json b/plugins/permission-backend/package.json index b13d2a1eef..fd3ce984cc 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.7", + "version": "0.5.8", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -22,12 +22,12 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/backend-common": "^0.13.3", + "@backstage/backend-common": "^0.14.0", "@backstage/config": "^1.0.1", "@backstage/errors": "^1.0.0", - "@backstage/plugin-auth-node": "^0.2.1", - "@backstage/plugin-permission-common": "^0.6.1", - "@backstage/plugin-permission-node": "^0.6.1", + "@backstage/plugin-auth-node": "^0.2.2", + "@backstage/plugin-permission-common": "^0.6.2", + "@backstage/plugin-permission-node": "^0.6.2", "@types/express": "*", "dataloader": "^2.0.0", "express": "^4.17.1", @@ -39,11 +39,11 @@ "zod": "^3.11.6" }, "devDependencies": { - "@backstage/cli": "^0.17.1", + "@backstage/cli": "^0.17.2", "@types/lodash": "^4.14.151", "@types/supertest": "^2.0.8", "supertest": "^6.1.6", - "msw": "^0.35.0" + "msw": "^0.42.0" }, "files": [ "dist" diff --git a/plugins/permission-common/CHANGELOG.md b/plugins/permission-common/CHANGELOG.md index ea444dc61a..b578c1e8fb 100644 --- a/plugins/permission-common/CHANGELOG.md +++ b/plugins/permission-common/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-permission-common +## 0.6.2 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. + +## 0.6.2-next.0 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. + ## 0.6.1 ### Patch Changes diff --git a/plugins/permission-common/package.json b/plugins/permission-common/package.json index a3a54b8b48..82ab09c853 100644 --- a/plugins/permission-common/package.json +++ b/plugins/permission-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-permission-common", "description": "Isomorphic types and client for Backstage permissions and authorization", - "version": "0.6.1", + "version": "0.6.2", "main": "src/index.ts", "types": "src/index.ts", "publishConfig": { @@ -48,8 +48,8 @@ "zod": "^3.11.6" }, "devDependencies": { - "@backstage/cli": "^0.17.1", + "@backstage/cli": "^0.17.2", "@types/jest": "^26.0.7", - "msw": "^0.35.0" + "msw": "^0.42.0" } } diff --git a/plugins/permission-node/CHANGELOG.md b/plugins/permission-node/CHANGELOG.md index cee3143091..889b3ba374 100644 --- a/plugins/permission-node/CHANGELOG.md +++ b/plugins/permission-node/CHANGELOG.md @@ -1,5 +1,45 @@ # @backstage/plugin-permission-node +## 0.6.2 + +### Patch Changes + +- 58426f9c0f: Added a new endpoint for aggregating permission metadata from a plugin backend: `/.well-known/backstage/permissions/metadata` + + By default, the metadata endpoint will return information about the permission rules supported by the plugin. Plugin authors can also provide an optional `permissions` parameter to `createPermissionIntegrationRouter`. If provided, these `Permission` objects will be included in the metadata returned by this endpoint. The `permissions` parameter will eventually be required in a future breaking change. + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/backend-common@0.14.0 + - @backstage/plugin-auth-node@0.2.2 + - @backstage/plugin-permission-common@0.6.2 + +## 0.6.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0-next.2 + - @backstage/plugin-auth-node@0.2.2-next.2 + +## 0.6.2-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/backend-common@0.13.6-next.1 + - @backstage/plugin-auth-node@0.2.2-next.1 + - @backstage/plugin-permission-common@0.6.2-next.0 + +## 0.6.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + - @backstage/plugin-auth-node@0.2.2-next.0 + ## 0.6.1 ### Patch Changes diff --git a/plugins/permission-node/api-report.md b/plugins/permission-node/api-report.md index b2e4ff45b8..702d363137 100644 --- a/plugins/permission-node/api-report.md +++ b/plugins/permission-node/api-report.md @@ -112,6 +112,7 @@ export const createPermissionIntegrationRouter: < TResource, >(options: { resourceType: TResourceType; + permissions?: Permission[] | undefined; rules: PermissionRule, unknown[]>[]; getResources: (resourceRefs: string[]) => Promise<(TResource | undefined)[]>; }) => express.Router; diff --git a/plugins/permission-node/package.json b/plugins/permission-node/package.json index 4eba47e0e4..1281b4ecba 100644 --- a/plugins/permission-node/package.json +++ b/plugins/permission-node/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-permission-node", "description": "Common permission and authorization utilities for backend plugins", - "version": "0.6.1", + "version": "0.6.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -33,20 +33,20 @@ "start": "backstage-cli package start" }, "dependencies": { - "@backstage/backend-common": "^0.13.3", + "@backstage/backend-common": "^0.14.0", "@backstage/config": "^1.0.1", "@backstage/errors": "^1.0.0", - "@backstage/plugin-auth-node": "^0.2.1", - "@backstage/plugin-permission-common": "^0.6.1", + "@backstage/plugin-auth-node": "^0.2.2", + "@backstage/plugin-permission-common": "^0.6.2", "@types/express": "^4.17.6", "express": "^4.17.1", "express-promise-router": "^4.1.0", "zod": "^3.11.6" }, "devDependencies": { - "@backstage/cli": "^0.17.1", + "@backstage/cli": "^0.17.2", "@types/supertest": "^2.0.8", - "msw": "^0.35.0", + "msw": "^0.42.0", "supertest": "^6.1.3" }, "files": [ diff --git a/plugins/permission-node/src/integration/createPermissionIntegrationRouter.test.ts b/plugins/permission-node/src/integration/createPermissionIntegrationRouter.test.ts index 65c43c7f30..ac4005c487 100644 --- a/plugins/permission-node/src/integration/createPermissionIntegrationRouter.test.ts +++ b/plugins/permission-node/src/integration/createPermissionIntegrationRouter.test.ts @@ -14,7 +14,11 @@ * limitations under the License. */ -import { AuthorizeResult } from '@backstage/plugin-permission-common'; +import { + AuthorizeResult, + createPermission, + Permission, +} from '@backstage/plugin-permission-common'; import express, { Express, Router } from 'express'; import request, { Response } from 'supertest'; import { createPermissionIntegrationRouter } from './createPermissionIntegrationRouter'; @@ -26,22 +30,25 @@ const mockGetResources: jest.MockedFunction< resourceRefs.map(resourceRef => ({ id: resourceRef })), ); +const testPermission: Permission = createPermission({ + name: 'test.permission', + attributes: {}, +}); + const testRule1 = createPermissionRule({ name: 'test-rule-1', description: 'Test rule 1', resourceType: 'test-resource', - apply: jest.fn( - (_resource: any, _firstParam: string, _secondParam: number) => true, - ), - toQuery: jest.fn(), + apply: (_resource: any, _firstParam: string, _secondParam: number) => true, + toQuery: (_firstParam: string, _secondParam: number) => ({}), }); const testRule2 = createPermissionRule({ name: 'test-rule-2', description: 'Test rule 2', resourceType: 'test-resource', - apply: jest.fn((_resource: any, _firstParam: object) => false), - toQuery: jest.fn(), + apply: (_resource: any, _firstParam: object) => false, + toQuery: (_firstParam: object) => ({}), }); describe('createPermissionIntegrationRouter', () => { @@ -51,6 +58,7 @@ describe('createPermissionIntegrationRouter', () => { beforeAll(() => { router = createPermissionIntegrationRouter({ resourceType: 'test-resource', + permissions: [testPermission], getResources: mockGetResources, rules: [testRule1, testRule2], }); @@ -501,4 +509,31 @@ describe('createPermissionIntegrationRouter', () => { expect(response.error && response.error.text).toMatch(/invalid/i); }); }); + + describe('GET /.well-known/backstage/permissions/metadata', () => { + it('returns a list of permissions and rules used by a given backend', async () => { + const response = await request(app).get( + '/.well-known/backstage/permissions/metadata', + ); + + expect(response.status).toEqual(200); + expect(response.body).toEqual({ + permissions: [testPermission], + rules: [ + { + name: testRule1.name, + description: testRule1.description, + resourceType: testRule1.resourceType, + parameters: { count: 2 }, + }, + { + name: testRule2.name, + description: testRule2.description, + resourceType: testRule2.resourceType, + parameters: { count: 1 }, + }, + ], + }); + }); + }); }); diff --git a/plugins/permission-node/src/integration/createPermissionIntegrationRouter.ts b/plugins/permission-node/src/integration/createPermissionIntegrationRouter.ts index 4bdb4397dd..28abc2f48d 100644 --- a/plugins/permission-node/src/integration/createPermissionIntegrationRouter.ts +++ b/plugins/permission-node/src/integration/createPermissionIntegrationRouter.ts @@ -23,6 +23,7 @@ import { AuthorizeResult, DefinitivePolicyDecision, IdentifiedPermissionMessage, + Permission, PermissionCondition, PermissionCriteria, } from '@backstage/plugin-permission-common'; @@ -174,6 +175,7 @@ export const createPermissionIntegrationRouter = < TResource, >(options: { resourceType: TResourceType; + permissions?: Array; // Do not infer value of TResourceType from supplied rules. // instead only consider the resourceType parameter, and // consider any rules whose resource type does not match @@ -183,8 +185,22 @@ export const createPermissionIntegrationRouter = < resourceRefs: string[], ) => Promise>; }): express.Router => { - const { resourceType, rules, getResources } = options; + const { resourceType, permissions, rules, getResources } = options; const router = Router(); + router.use(express.json()); + + router.get('/.well-known/backstage/permissions/metadata', (_, res) => { + const serializableRules = rules.map(rule => ({ + name: rule.name, + description: rule.description, + resourceType: rule.resourceType, + parameters: { + count: rule.toQuery.length, + }, + })); + + return res.json({ permissions, rules: serializableRules }); + }); const getRule = createGetRule(rules); @@ -202,8 +218,6 @@ export const createPermissionIntegrationRouter = < } }; - router.use(express.json()); - router.post( '/.well-known/backstage/permissions/apply-conditions', async (req, res: Response) => { @@ -227,7 +241,7 @@ export const createPermissionIntegrationRouter = < return acc; }, {} as Record); - return res.status(200).json({ + return res.json({ items: body.items.map(request => ({ id: request.id, result: applyConditions( diff --git a/plugins/permission-react/CHANGELOG.md b/plugins/permission-react/CHANGELOG.md index 4e4dcb21c0..cd991549bd 100644 --- a/plugins/permission-react/CHANGELOG.md +++ b/plugins/permission-react/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-permission-react +## 0.4.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.0.3 + - @backstage/plugin-permission-common@0.6.2 + +## 0.4.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/plugin-permission-common@0.6.2-next.0 + ## 0.4.1 ### Patch Changes diff --git a/plugins/permission-react/package.json b/plugins/permission-react/package.json index a04506ed58..ffdb72d385 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.1", + "version": "0.4.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -32,8 +32,8 @@ }, "dependencies": { "@backstage/config": "^1.0.1", - "@backstage/core-plugin-api": "^1.0.2", - "@backstage/plugin-permission-common": "^0.6.1", + "@backstage/core-plugin-api": "^1.0.3", + "@backstage/plugin-permission-common": "^0.6.2", "cross-fetch": "^3.1.5", "react-router": "6.0.0-beta.0", "react-use": "^17.2.4", @@ -44,8 +44,8 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/test-utils": "^1.1.0", + "@backstage/cli": "^0.17.2", + "@backstage/test-utils": "^1.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@types/jest": "^26.0.7" diff --git a/plugins/proxy-backend/CHANGELOG.md b/plugins/proxy-backend/CHANGELOG.md index a83d570df4..b23cce864d 100644 --- a/plugins/proxy-backend/CHANGELOG.md +++ b/plugins/proxy-backend/CHANGELOG.md @@ -1,5 +1,26 @@ # @backstage/plugin-proxy-backend +## 0.2.27 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0 + +## 0.2.27-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0-next.2 + +## 0.2.27-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + ## 0.2.26 ### Patch Changes diff --git a/plugins/proxy-backend/package.json b/plugins/proxy-backend/package.json index 2e72f6e40a..a0f7668e3e 100644 --- a/plugins/proxy-backend/package.json +++ b/plugins/proxy-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-proxy-backend", "description": "A Backstage backend plugin that helps you set up proxy endpoints in the backend", - "version": "0.2.26", + "version": "0.2.27", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -32,7 +32,7 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/backend-common": "^0.13.3", + "@backstage/backend-common": "^0.14.0", "@backstage/config": "^1.0.1", "@types/express": "^4.17.6", "express": "^4.17.1", @@ -46,7 +46,7 @@ "yup": "^0.32.9" }, "devDependencies": { - "@backstage/cli": "^0.17.1", + "@backstage/cli": "^0.17.2", "@types/http-proxy-middleware": "^0.19.3", "@types/supertest": "^2.0.8", "@types/uuid": "^8.0.0", diff --git a/plugins/rollbar-backend/CHANGELOG.md b/plugins/rollbar-backend/CHANGELOG.md index 8f74a25eba..75e665c02d 100644 --- a/plugins/rollbar-backend/CHANGELOG.md +++ b/plugins/rollbar-backend/CHANGELOG.md @@ -1,5 +1,35 @@ # @backstage/plugin-rollbar-backend +## 0.1.30 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/backend-common@0.14.0 + +## 0.1.30-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0-next.2 + +## 0.1.30-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/backend-common@0.13.6-next.1 + +## 0.1.30-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + ## 0.1.29 ### Patch Changes diff --git a/plugins/rollbar-backend/package.json b/plugins/rollbar-backend/package.json index 7c22e1e0a6..53fba7bfe3 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.29", + "version": "0.1.30", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -34,7 +34,7 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/backend-common": "^0.13.3", + "@backstage/backend-common": "^0.14.0", "@backstage/config": "^1.0.1", "@types/express": "^4.17.6", "camelcase-keys": "^7.0.1", @@ -50,10 +50,10 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/backend-test-utils": "^0.1.24", - "@backstage/cli": "^0.17.1", + "@backstage/backend-test-utils": "^0.1.25", + "@backstage/cli": "^0.17.2", "@types/supertest": "^2.0.8", - "msw": "^0.36.3", + "msw": "^0.42.0", "supertest": "^6.1.3" }, "files": [ diff --git a/plugins/rollbar/CHANGELOG.md b/plugins/rollbar/CHANGELOG.md index f620ebe9bb..bb55d819a3 100644 --- a/plugins/rollbar/CHANGELOG.md +++ b/plugins/rollbar/CHANGELOG.md @@ -1,5 +1,35 @@ # @backstage/plugin-rollbar +## 0.4.6 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## 0.4.6-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## 0.4.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + ## 0.4.5 ### Patch Changes diff --git a/plugins/rollbar/package.json b/plugins/rollbar/package.json index c1a45ca31f..5633c9b09a 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.5", + "version": "0.4.6", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -35,10 +35,10 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-model": "^1.0.2", - "@backstage/core-components": "^0.9.4", - "@backstage/core-plugin-api": "^1.0.2", - "@backstage/plugin-catalog-react": "^1.1.0", + "@backstage/catalog-model": "^1.0.3", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", + "@backstage/plugin-catalog-react": "^1.1.1", "@backstage/theme": "^0.2.15", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -53,10 +53,10 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/core-app-api": "^1.0.2", - "@backstage/dev-utils": "^1.0.2", - "@backstage/test-utils": "^1.1.0", + "@backstage/cli": "^0.17.2", + "@backstage/core-app-api": "^1.0.3", + "@backstage/dev-utils": "^1.0.3", + "@backstage/test-utils": "^1.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/react-hooks": "^8.0.0", @@ -65,7 +65,7 @@ "@types/node": "^16.11.26", "@types/react": "^16.13.1 || ^17.0.0", "cross-fetch": "^3.1.5", - "msw": "^0.35.0" + "msw": "^0.42.0" }, "files": [ "dist", diff --git a/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md b/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md index 87c8ed07bb..698a746a2a 100644 --- a/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md @@ -1,5 +1,43 @@ # @backstage/plugin-scaffolder-backend-module-cookiecutter +## 0.2.8 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-scaffolder-backend@1.3.0 + - @backstage/backend-common@0.14.0 + - @backstage/integration@1.2.1 + +## 0.2.8-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-backend@1.3.0-next.2 + - @backstage/backend-common@0.14.0-next.2 + - @backstage/integration@1.2.1-next.2 + +## 0.2.8-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/backend-common@0.13.6-next.1 + - @backstage/integration@1.2.1-next.1 + - @backstage/plugin-scaffolder-backend@1.3.0-next.1 + +## 0.2.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-backend@1.3.0-next.0 + - @backstage/backend-common@0.13.6-next.0 + - @backstage/integration@1.2.1-next.0 + ## 0.2.7 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-cookiecutter/package.json b/plugins/scaffolder-backend-module-cookiecutter/package.json index 8dcef72d77..630f672231 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.7", + "version": "0.2.8", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -23,10 +23,10 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/backend-common": "^0.13.3", + "@backstage/backend-common": "^0.14.0", "@backstage/errors": "^1.0.0", - "@backstage/integration": "^1.2.0", - "@backstage/plugin-scaffolder-backend": "^1.2.0", + "@backstage/integration": "^1.2.1", + "@backstage/plugin-scaffolder-backend": "^1.3.0", "@backstage/config": "^1.0.1", "@backstage/types": "^1.0.0", "command-exists": "^1.2.9", @@ -35,13 +35,13 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", + "@backstage/cli": "^0.17.2", "@types/fs-extra": "^9.0.1", "@types/mock-fs": "^4.13.0", "@types/jest": "^26.0.7", "@types/command-exists": "^1.2.0", "mock-fs": "^5.1.0", - "msw": "^0.35.0" + "msw": "^0.42.0" }, "files": [ "dist" diff --git a/plugins/scaffolder-backend-module-rails/CHANGELOG.md b/plugins/scaffolder-backend-module-rails/CHANGELOG.md index 80c423941b..6a1f59ef7e 100644 --- a/plugins/scaffolder-backend-module-rails/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-rails/CHANGELOG.md @@ -1,5 +1,32 @@ # @backstage/plugin-scaffolder-backend-module-rails +## 0.4.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-backend@1.3.0 + - @backstage/backend-common@0.14.0 + - @backstage/integration@1.2.1 + +## 0.4.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-backend@1.3.0-next.2 + - @backstage/backend-common@0.14.0-next.2 + - @backstage/integration@1.2.1-next.2 + +## 0.4.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-backend@1.3.0-next.0 + - @backstage/backend-common@0.13.6-next.0 + - @backstage/integration@1.2.1-next.0 + ## 0.4.0 ### Minor Changes diff --git a/plugins/scaffolder-backend-module-rails/package.json b/plugins/scaffolder-backend-module-rails/package.json index ee8418f546..1f755a8f81 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.0", + "version": "0.4.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -24,17 +24,17 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/backend-common": "^0.13.3", - "@backstage/plugin-scaffolder-backend": "^1.2.0", + "@backstage/backend-common": "^0.14.0", + "@backstage/plugin-scaffolder-backend": "^1.3.0", "@backstage/config": "^1.0.1", "@backstage/errors": "^1.0.0", - "@backstage/integration": "^1.2.0", + "@backstage/integration": "^1.2.1", "@backstage/types": "^1.0.0", "command-exists": "^1.2.9", "fs-extra": "^10.0.1" }, "devDependencies": { - "@backstage/cli": "^0.17.1", + "@backstage/cli": "^0.17.2", "@types/jest": "^26.0.7", "@types/node": "^16.11.26", "@types/command-exists": "^1.2.0", diff --git a/plugins/scaffolder-backend-module-yeoman/CHANGELOG.md b/plugins/scaffolder-backend-module-yeoman/CHANGELOG.md index 32607029a7..a6da817ba6 100644 --- a/plugins/scaffolder-backend-module-yeoman/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-yeoman/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-scaffolder-backend-module-yeoman +## 0.2.6 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-backend@1.3.0 + +## 0.2.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-backend@1.3.0-next.0 + ## 0.2.5 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-yeoman/package.json b/plugins/scaffolder-backend-module-yeoman/package.json index 2b3f02cf9b..b876b5e87c 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.5", + "version": "0.2.6", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -24,13 +24,13 @@ }, "dependencies": { "@backstage/config": "^1.0.1", - "@backstage/plugin-scaffolder-backend": "^1.2.0", + "@backstage/plugin-scaffolder-backend": "^1.3.0", "@backstage/types": "^1.0.0", "winston": "^3.2.1", "yeoman-environment": "^3.9.1" }, "devDependencies": { - "@backstage/backend-common": "^0.13.3", + "@backstage/backend-common": "^0.14.0", "@types/jest": "^26.0.7" }, "files": [ diff --git a/plugins/scaffolder-backend/CHANGELOG.md b/plugins/scaffolder-backend/CHANGELOG.md index 98ebe6848e..598de4122f 100644 --- a/plugins/scaffolder-backend/CHANGELOG.md +++ b/plugins/scaffolder-backend/CHANGELOG.md @@ -1,5 +1,116 @@ # @backstage/plugin-scaffolder-backend +## 1.3.0 + +### Minor Changes + +- 35a26131b3: **DEPRECATION**: The `projectid` input parameters to the `publish:gitlab:merge-request`, it's no longer required as it can be decoded from the `repoUrl` input parameter. + **DEPRECATION**: The `projectid` output of the action in favour of `projectPath` +- 72dfcbc8bf: A new scaffolder action has been added: `gerrit:publish` +- ce0d8d7eb1: Fixed a bug in `publish:github` action that didn't permit to add users as collaborators. + This fix required changing the way parameters are passed to the action. + In order to add a team as collaborator, now you must use the `team` field instead of `username`. + In order to add a user as collaborator, you must use the `user` field. + + It's still possible to use the field `username` but is deprecated in favor of `team`. + + ```yaml + - id: publish + name: Publish + action: publish:github + input: + repoUrl: ... + collaborators: + - access: ... + team: my_team + - access: ... + user: my_username + ``` + +- 582003a059: - Added an optional `list` method on the `TaskBroker` and `TaskStore` interface to list tasks by an optional `userEntityRef` + - Implemented a `list` method on the `DatabaseTaskStore` class to list tasks by an optional `userEntityRef` + - Added a route under `/v2/tasks` to list tasks by a `userEntityRef` using the `createdBy` query parameter +- c042c5eaff: Add an option to not protect the default branch. +- f93af969cd: Added the ability to support running of templates that are not in the `default` namespace + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- 6901f6be4a: Adds more of an explanation when the `publish:github` scaffolder action fails to create a repository. +- Updated dependencies + - @backstage/plugin-catalog-backend@1.2.0 + - @backstage/backend-common@0.14.0 + - @backstage/integration@1.2.1 + - @backstage/catalog-client@1.0.3 + - @backstage/catalog-model@1.0.3 + - @backstage/plugin-scaffolder-common@1.1.1 + +## 1.3.0-next.2 + +### Minor Changes + +- ce0d8d7eb1: Fixed a bug in `publish:github` action that didn't permit to add users as collaborators. + This fix required changing the way parameters are passed to the action. + In order to add a team as collaborator, now you must use the `team` field instead of `username`. + In order to add a user as collaborator, you must use the `user` field. + + It's still possible to use the field `username` but is deprecated in favor of `team`. + + ```yaml + - id: publish + name: Publish + action: publish:github + input: + repoUrl: ... + collaborators: + - access: ... + team: my_team + - access: ... + user: my_username + ``` + +- 582003a059: - Added an optional `list` method on the `TaskBroker` and `TaskStore` interface to list tasks by an optional `userEntityRef` + - Implemented a `list` method on the `DatabaseTaskStore` class to list tasks by an optional `userEntityRef` + - Added a route under `/v2/tasks` to list tasks by a `userEntityRef` using the `createdBy` query parameter + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0-next.2 + - @backstage/integration@1.2.1-next.2 + - @backstage/plugin-catalog-backend@1.2.0-next.2 + +## 1.3.0-next.1 + +### Minor Changes + +- c042c5eaff: Add an option to not protect the default branch. + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/backend-common@0.13.6-next.1 + - @backstage/catalog-client@1.0.3-next.0 + - @backstage/integration@1.2.1-next.1 + - @backstage/plugin-catalog-backend@1.2.0-next.1 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-scaffolder-common@1.1.1-next.0 + +## 1.3.0-next.0 + +### Minor Changes + +- 72dfcbc8bf: A new scaffolder action has been added: `gerrit:publish` + +### Patch Changes + +- 6901f6be4a: Adds more of an explanation when the `publish:github` scaffolder action fails to create a repository. +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + - @backstage/integration@1.2.1-next.0 + - @backstage/plugin-catalog-backend@1.2.0-next.0 + ## 1.2.0 ### Minor Changes diff --git a/plugins/scaffolder-backend/api-report.md b/plugins/scaffolder-backend/api-report.md index 7cf26aa473..707b8e62ba 100644 --- a/plugins/scaffolder-backend/api-report.md +++ b/plugins/scaffolder-backend/api-report.md @@ -40,6 +40,7 @@ export type ActionContext = { output(name: string, value: JsonValue): void; createTemporaryDirectory(): Promise; templateInfo?: TemplateInfo; + isDryRun?: boolean; }; // @public @@ -247,6 +248,19 @@ export function createPublishFileAction(): TemplateAction<{ path: string; }>; +// @public +export function createPublishGerritAction(options: { + integrations: ScmIntegrationRegistry; + config: Config; +}): TemplateAction<{ + repoUrl: string; + description: string; + defaultBranch?: string | undefined; + gitCommitMessage?: string | undefined; + gitAuthorName?: string | undefined; + gitAuthorEmail?: string | undefined; +}>; + // @public export function createPublishGithubAction(options: { integrations: ScmIntegrationRegistry; @@ -257,6 +271,7 @@ export function createPublishGithubAction(options: { description?: string | undefined; access?: string | undefined; defaultBranch?: string | undefined; + protectDefaultBranch?: boolean | undefined; deleteBranchOnMerge?: boolean | undefined; gitCommitMessage?: string | undefined; gitAuthorName?: string | undefined; @@ -269,10 +284,20 @@ export function createPublishGithubAction(options: { requiredStatusCheckContexts?: string[] | undefined; repoVisibility?: 'internal' | 'private' | 'public' | undefined; collaborators?: - | { - username: string; - access: 'pull' | 'push' | 'admin' | 'maintain' | 'triage'; - }[] + | ( + | { + user: string; + access: 'pull' | 'push' | 'admin' | 'maintain' | 'triage'; + } + | { + team: string; + access: 'pull' | 'push' | 'admin' | 'maintain' | 'triage'; + } + | { + username: string; + access: 'pull' | 'push' | 'admin' | 'maintain' | 'triage'; + } + )[] | undefined; token?: string | undefined; topics?: string[] | undefined; @@ -313,13 +338,13 @@ export function createPublishGitlabAction(options: { export const createPublishGitlabMergeRequestAction: (options: { integrations: ScmIntegrationRegistry; }) => TemplateAction<{ - projectid: string; repoUrl: string; title: string; description: string; branchName: string; targetPath: string; token?: string | undefined; + projectid?: string | undefined; }>; // @public @@ -381,6 +406,10 @@ export class DatabaseTaskStore implements TaskStore { // (undocumented) heartbeatTask(taskId: string): Promise; // (undocumented) + list(options: { createdBy?: string }): Promise<{ + tasks: SerializedTask[]; + }>; + // (undocumented) listEvents({ taskId, after }: TaskStoreListEventsOptions): Promise<{ events: SerializedTaskEvent[]; }>; @@ -505,6 +534,10 @@ export interface TaskBroker { // (undocumented) get(taskId: string): Promise; // (undocumented) + list?(options?: { createdBy?: string }): Promise<{ + tasks: SerializedTask[]; + }>; + // (undocumented) vacuumTasks(options: { timeoutS: number }): Promise; } @@ -536,6 +569,8 @@ export interface TaskContext { // (undocumented) getWorkspaceName(): Promise; // (undocumented) + isDryRun?: boolean; + // (undocumented) secrets?: TaskSecrets; // (undocumented) spec: TaskSpec; @@ -602,6 +637,10 @@ export interface TaskStore { // (undocumented) heartbeatTask(taskId: string): Promise; // (undocumented) + list?(options: { createdBy?: string }): Promise<{ + tasks: SerializedTask[]; + }>; + // (undocumented) listEvents({ taskId, after }: TaskStoreListEventsOptions): Promise<{ events: SerializedTaskEvent[]; }>; @@ -651,6 +690,7 @@ export class TaskWorker { export type TemplateAction = { id: string; description?: string; + supportsDryRun?: boolean; schema?: { input?: Schema; output?: Schema; diff --git a/plugins/scaffolder-backend/package.json b/plugins/scaffolder-backend/package.json index 0ce300638c..9dfe00672e 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.2.0", + "version": "1.3.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -34,14 +34,14 @@ "build:assets": "node scripts/build-nunjucks.js" }, "dependencies": { - "@backstage/backend-common": "^0.13.3", - "@backstage/catalog-client": "^1.0.2", - "@backstage/catalog-model": "^1.0.2", + "@backstage/backend-common": "^0.14.0", + "@backstage/catalog-client": "^1.0.3", + "@backstage/catalog-model": "^1.0.3", "@backstage/config": "^1.0.1", "@backstage/errors": "^1.0.0", - "@backstage/integration": "^1.2.0", - "@backstage/plugin-catalog-backend": "^1.1.2", - "@backstage/plugin-scaffolder-common": "^1.1.0", + "@backstage/integration": "^1.2.1", + "@backstage/plugin-catalog-backend": "^1.2.0", + "@backstage/plugin-scaffolder-common": "^1.1.1", "@backstage/types": "^1.0.0", "@gitbeaker/core": "^35.6.0", "@gitbeaker/node": "^35.1.0", @@ -67,15 +67,17 @@ "nunjucks": "^3.2.3", "octokit": "^1.7.1", "octokit-plugin-create-pull-request": "^3.10.0", + "p-limit": "^3.1.0", "uuid": "^8.2.0", "winston": "^3.2.1", "yaml": "^1.10.0", "vm2": "^3.9.6", - "zen-observable": "^0.8.15" + "zen-observable": "^0.8.15", + "zod": "^3.11.6" }, "devDependencies": { - "@backstage/backend-test-utils": "^0.1.24", - "@backstage/cli": "^0.17.1", + "@backstage/backend-test-utils": "^0.1.25", + "@backstage/cli": "^0.17.2", "@types/command-exists": "^1.2.0", "@types/fs-extra": "^9.0.1", "@types/git-url-parse": "^9.0.0", @@ -86,7 +88,7 @@ "esbuild": "^0.14.1", "jest-when": "^3.1.0", "mock-fs": "^5.1.0", - "msw": "^0.35.0", + "msw": "^0.42.0", "supertest": "^6.1.3", "yaml": "^1.10.0" }, diff --git a/plugins/scaffolder-backend/src/lib/files/deserializeDirectoryContents.test.ts b/plugins/scaffolder-backend/src/lib/files/deserializeDirectoryContents.test.ts new file mode 100644 index 0000000000..0e7de7f88d --- /dev/null +++ b/plugins/scaffolder-backend/src/lib/files/deserializeDirectoryContents.test.ts @@ -0,0 +1,81 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import mockFs from 'mock-fs'; +import { deserializeDirectoryContents } from './deserializeDirectoryContents'; +import { serializeDirectoryContents } from './serializeDirectoryContents'; + +describe('deserializeDirectoryContents', () => { + beforeEach(() => { + mockFs({ + root: {}, + }); + }); + + afterEach(() => { + mockFs.restore(); + }); + + it('deserializes contents into a directory', async () => { + await deserializeDirectoryContents('root', [ + { + path: 'a.txt', + content: Buffer.from('a', 'utf8'), + }, + ]); + await expect(serializeDirectoryContents('root')).resolves.toEqual([ + { + path: 'a.txt', + content: Buffer.from('a', 'utf8'), + executable: false, + }, + ]); + }); + + it('deserializes contents into a deep directory structure', async () => { + await deserializeDirectoryContents('root', [ + { + path: 'a.txt', + content: Buffer.from('a', 'utf8'), + }, + { + path: 'a/b.txt', + content: Buffer.from('b', 'utf8'), + }, + { + path: 'a/b/c.txt', + content: Buffer.from('c', 'utf8'), + }, + ]); + await expect(serializeDirectoryContents('root')).resolves.toEqual([ + { + path: 'a.txt', + content: Buffer.from('a', 'utf8'), + executable: false, + }, + { + path: 'a/b.txt', + content: Buffer.from('b', 'utf8'), + executable: false, + }, + { + path: 'a/b/c.txt', + content: Buffer.from('c', 'utf8'), + executable: false, + }, + ]); + }); +}); diff --git a/plugins/scaffolder-backend/src/lib/files/deserializeDirectoryContents.ts b/plugins/scaffolder-backend/src/lib/files/deserializeDirectoryContents.ts new file mode 100644 index 0000000000..cb567eed9d --- /dev/null +++ b/plugins/scaffolder-backend/src/lib/files/deserializeDirectoryContents.ts @@ -0,0 +1,39 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import fs from 'fs-extra'; +import { dirname } from 'path'; +import { resolveSafeChildPath } from '@backstage/backend-common'; +import { SerializedFile } from './types'; + +/** + * Deserializes a list of serialized files into the target directory. + * + * This method uses `resolveSafeChildPath` to make sure that files are + * not written outside of the target directory. + * + * @internal + */ +export async function deserializeDirectoryContents( + targetPath: string, + files: SerializedFile[], +): Promise { + for (const file of files) { + const filePath = resolveSafeChildPath(targetPath, file.path); + await fs.ensureDir(dirname(filePath)); + await fs.writeFile(filePath, file.content); // Ignore file mode + } +} diff --git a/plugins/scaffolder-backend/src/lib/files/index.ts b/plugins/scaffolder-backend/src/lib/files/index.ts new file mode 100644 index 0000000000..ab6975532f --- /dev/null +++ b/plugins/scaffolder-backend/src/lib/files/index.ts @@ -0,0 +1,19 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { serializeDirectoryContents } from './serializeDirectoryContents'; +export { deserializeDirectoryContents } from './deserializeDirectoryContents'; +export type { SerializedFile } from './types'; diff --git a/plugins/scaffolder-backend/src/lib/files/serializeDirectoryContents.test.ts b/plugins/scaffolder-backend/src/lib/files/serializeDirectoryContents.test.ts new file mode 100644 index 0000000000..e1ea676ec5 --- /dev/null +++ b/plugins/scaffolder-backend/src/lib/files/serializeDirectoryContents.test.ts @@ -0,0 +1,179 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { serializeDirectoryContents } from './serializeDirectoryContents'; +import mockFs from 'mock-fs'; + +describe('serializeDirectoryContents', () => { + afterEach(() => { + mockFs.restore(); + }); + + it('should list files in this directory', async () => { + await expect(serializeDirectoryContents(__dirname)).resolves.toEqual( + expect.arrayContaining([ + { + path: 'index.ts', + executable: false, + content: expect.any(Buffer), + }, + { + path: 'types.ts', + executable: false, + content: expect.any(Buffer), + }, + { + path: 'serializeDirectoryContents.ts', + executable: false, + content: expect.any(Buffer), + }, + { + path: 'serializeDirectoryContents.test.ts', + executable: false, + content: expect.any(Buffer), + }, + ]), + ); + }); + + it('should list files in a mock directory', async () => { + mockFs({ + root: { + 'a.txt': 'a', + b: { + 'b1.txt': 'b1', + 'b2.txt': 'b2', + }, + c: { + c1: { + 'c11.txt': 'c11', + c11: { + 'c111.txt': 'c111', + }, + }, + }, + }, + }); + + await expect(serializeDirectoryContents('root')).resolves.toEqual([ + { + path: 'a.txt', + executable: false, + content: Buffer.from('a', 'utf8'), + }, + { + path: 'b/b1.txt', + executable: false, + content: Buffer.from('b1', 'utf8'), + }, + { + path: 'b/b2.txt', + executable: false, + content: Buffer.from('b2', 'utf8'), + }, + { + path: 'c/c1/c11.txt', + executable: false, + content: Buffer.from('c11', 'utf8'), + }, + { + path: 'c/c1/c11/c111.txt', + executable: false, + content: Buffer.from('c111', 'utf8'), + }, + ]); + }); + + it('should ignore gitignored files', async () => { + mockFs({ + root: { + '.gitignore': '*.txt', + 'a.txt': 'a', + 'a.log': 'a', + }, + }); + + await expect( + serializeDirectoryContents('root', { + gitignore: true, + }), + ).resolves.toEqual([ + { + path: '.gitignore', + executable: false, + content: Buffer.from('*.txt', 'utf8'), + }, + { + path: 'a.log', + executable: false, + content: Buffer.from('a', 'utf8'), + }, + ]); + }); + + it('should use custom glob patterns', async () => { + mockFs({ + root: { + '.a': 'a', + 'a.log': 'a', + 'a.txt': 'a', + b: { + '.b': 'b', + 'b.log': 'b', + 'b.txt': 'b', + }, + c: { + '.c': 'c', + 'c.log': 'c', + 'c.txt': 'c', + }, + }, + }); + + await expect( + serializeDirectoryContents('root', { + gitignore: true, + globPatterns: ['**/*.txt', '*/.?', '*/*.log', '!c/**/.*', '!b/*.log'], + }).then(files => files.sort((a, b) => a.path.localeCompare(b.path))), + ).resolves.toEqual([ + { + path: 'a.txt', + executable: false, + content: Buffer.from('a', 'utf8'), + }, + { + path: 'b/.b', + executable: false, + content: Buffer.from('b', 'utf8'), + }, + { + path: 'b/b.txt', + executable: false, + content: Buffer.from('b', 'utf8'), + }, + { + path: 'c/c.log', + executable: false, + content: Buffer.from('c', 'utf8'), + }, + { + path: 'c/c.txt', + executable: false, + content: Buffer.from('c', 'utf8'), + }, + ]); + }); +}); diff --git a/plugins/scaffolder-backend/src/lib/files/serializeDirectoryContents.ts b/plugins/scaffolder-backend/src/lib/files/serializeDirectoryContents.ts new file mode 100644 index 0000000000..608b9e71a5 --- /dev/null +++ b/plugins/scaffolder-backend/src/lib/files/serializeDirectoryContents.ts @@ -0,0 +1,62 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import fs from 'fs-extra'; +import globby from 'globby'; +import limiterFactory from 'p-limit'; +import { join as joinPath } from 'path'; +import { SerializedFile } from './types'; + +const DEFAULT_GLOB_PATTERNS = ['./**', '!.git']; + +export const isExecutable = (fileMode: number | undefined) => { + if (!fileMode) { + return false; + } + + const executeBitMask = 0o000111; + const res = fileMode & executeBitMask; + return res > 0; +}; + +export async function serializeDirectoryContents( + sourcePath: string, + options?: { + gitignore?: boolean; + globPatterns?: string[]; + }, +): Promise { + const paths = await globby(options?.globPatterns ?? DEFAULT_GLOB_PATTERNS, { + cwd: sourcePath, + dot: true, + gitignore: options?.gitignore, + followSymbolicLinks: false, + objectMode: true, + stats: true, + }); + + const limiter = limiterFactory(10); + + return Promise.all( + paths.map(async ({ path, stats }) => ({ + path, + content: await limiter(async () => + fs.readFile(joinPath(sourcePath, path)), + ), + executable: isExecutable(stats?.mode), + })), + ); +} diff --git a/plugins/scaffolder-backend/src/lib/files/types.ts b/plugins/scaffolder-backend/src/lib/files/types.ts new file mode 100644 index 0000000000..390d6791ee --- /dev/null +++ b/plugins/scaffolder-backend/src/lib/files/types.ts @@ -0,0 +1,21 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export interface SerializedFile { + path: string; + content: Buffer; + executable?: boolean; +} diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/catalog/register.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/catalog/register.ts index 1b30268d9d..4b9f883706 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/catalog/register.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/catalog/register.ts @@ -84,6 +84,18 @@ export function createCatalogRegisterAction(options: { }, ], }, + output: { + type: 'object', + required: ['catalogInfoUrl'], + properties: { + entityRef: { + type: 'string', + }, + catalogInfoUrl: { + type: 'string', + }, + }, + }, }, async handler(ctx) { const { input } = ctx; diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/catalog/write.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/catalog/write.ts index b55e521784..da758898dc 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/catalog/write.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/catalog/write.ts @@ -46,6 +46,7 @@ export function createCatalogWriteAction() { }, }, }, + supportsDryRun: true, async handler(ctx) { ctx.logStream.write(`Writing catalog-info.yaml`); const { filePath, entity } = ctx.input; diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/createBuiltinActions.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/createBuiltinActions.ts index 0d269019df..2ffbc09ce5 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/createBuiltinActions.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/createBuiltinActions.ts @@ -39,6 +39,7 @@ import { createPublishBitbucketAction, createPublishBitbucketCloudAction, createPublishBitbucketServerAction, + createPublishGerritAction, createPublishGithubAction, createPublishGithubPullRequestAction, createPublishGitlabAction, @@ -111,6 +112,10 @@ export const createBuiltinActions = ( reader, additionalTemplateFilters, }), + createPublishGerritAction({ + integrations, + config, + }), createPublishGithubAction({ integrations, config, diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/debug/log.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/debug/log.ts index 59d4c333c6..99d030b772 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/debug/log.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/debug/log.ts @@ -51,6 +51,7 @@ export function createDebugLogAction() { }, }, }, + supportsDryRun: true, async handler(ctx) { ctx.logger.info(JSON.stringify(ctx.input, null, 2)); diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plain.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plain.ts index 6dbe08f61b..9dc82068c0 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plain.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plain.ts @@ -54,6 +54,7 @@ export function createFetchPlainAction(options: { }, }, }, + supportsDryRun: true, async handler(ctx) { ctx.logger.info('Fetching plain content from remote URL'); diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.ts index 3d3d319c57..c6d6b86e7b 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.ts @@ -101,6 +101,7 @@ export function createFetchTemplateAction(options: { }, }, }, + supportsDryRun: true, async handler(ctx) { ctx.logger.info('Fetching template content from remote URL'); @@ -153,6 +154,7 @@ export function createFetchTemplateAction(options: { dot: true, onlyFiles: false, markDirectories: true, + followSymbolicLinks: false, }); const nonTemplatedEntries = new Set( @@ -164,6 +166,7 @@ export function createFetchTemplateAction(options: { dot: true, onlyFiles: false, markDirectories: true, + followSymbolicLinks: false, }), ), ) diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/filesystem/delete.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/filesystem/delete.ts index bf2953e75a..757e9c682e 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/filesystem/delete.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/filesystem/delete.ts @@ -42,6 +42,7 @@ export const createFilesystemDeleteAction = () => { }, }, }, + supportsDryRun: true, async handler(ctx) { if (!Array.isArray(ctx.input?.files)) { throw new InputError('files must be an Array'); diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/filesystem/rename.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/filesystem/rename.ts index 7c8dad7559..654ee7b150 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/filesystem/rename.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/filesystem/rename.ts @@ -66,6 +66,7 @@ export const createFilesystemRenameAction = () => { }, }, }, + supportsDryRun: true, async handler(ctx) { if (!Array.isArray(ctx.input?.files)) { throw new InputError('files must be an Array'); diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gerrit.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gerrit.test.ts new file mode 100644 index 0000000000..0b1ffe0de6 --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gerrit.test.ts @@ -0,0 +1,143 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +jest.mock('../helpers'); + +import { createPublishGerritAction } from './gerrit'; +import { rest } from 'msw'; +import { setupServer } from 'msw/node'; +import { setupRequestMockHandlers } from '@backstage/backend-test-utils'; +import { ScmIntegrations } from '@backstage/integration'; +import { ConfigReader } from '@backstage/config'; +import { getVoidLogger } from '@backstage/backend-common'; +import { PassThrough } from 'stream'; +import { initRepoAndPush } from '../helpers'; + +describe('publish:gerrit', () => { + const config = new ConfigReader({ + integrations: { + gerrit: [ + { + host: 'gerrithost.org', + username: 'gerrituser', + password: 'usertoken', + }, + ], + }, + }); + + const description = 'for the lols'; + const integrations = ScmIntegrations.fromConfig(config); + const action = createPublishGerritAction({ integrations, config }); + const mockContext = { + input: { + repoUrl: + 'gerrithost.org?owner=owner&workspace=parent&project=project&repo=repo', + description, + }, + workspacePath: 'lol', + logger: getVoidLogger(), + logStream: new PassThrough(), + output: jest.fn(), + createTemporaryDirectory: jest.fn(), + }; + const server = setupServer(); + setupRequestMockHandlers(server); + + beforeEach(() => { + jest.resetAllMocks(); + }); + + it('should throw an error when the repoUrl is not well formed', async () => { + await expect( + action.handler({ + ...mockContext, + input: { repoUrl: 'gerrithost.org?workspace=w&repo=repo', description }, + }), + ).rejects.toThrow(/missing owner/); + + await expect( + action.handler({ + ...mockContext, + input: { repoUrl: 'gerrithost.org?workspace=w&owner=o', description }, + }), + ).rejects.toThrow(/missing repo/); + }); + + it('should throw if there is no integration config provided', async () => { + await expect( + action.handler({ + ...mockContext, + input: { + repoUrl: 'missing.com?workspace=w&owner=o&repo=repo', + description, + }, + }), + ).rejects.toThrow(/No matching integration configuration/); + }); + + it('can correctly create a new project', async () => { + expect.assertions(5); + server.use( + rest.put('https://gerrithost.org/a/projects/repo', (req, res, ctx) => { + expect(req.headers.get('Authorization')).toBe( + 'Basic Z2Vycml0dXNlcjp1c2VydG9rZW4=', + ); + expect(req.body).toEqual({ + create_empty_commit: false, + owners: ['owner'], + description, + parent: 'workspace', + }); + return res( + ctx.status(201), + ctx.set('Content-Type', 'application/json'), + ctx.json({}), + ); + }), + ); + + await action.handler({ + ...mockContext, + input: { + ...mockContext.input, + repoUrl: 'gerrithost.org?workspace=workspace&owner=owner&repo=repo', + }, + }); + + expect(initRepoAndPush).toHaveBeenCalledWith({ + dir: mockContext.workspacePath, + remoteUrl: 'https://gerrithost.org/a/repo', + defaultBranch: 'master', + auth: { username: 'gerrituser', password: 'usertoken' }, + logger: mockContext.logger, + commitMessage: expect.stringContaining('initial commit\n\nChange-Id:'), + gitAuthorInfo: {}, + }); + + expect(mockContext.output).toHaveBeenCalledWith( + 'remoteUrl', + 'https://gerrithost.org/a/repo', + ); + expect(mockContext.output).toHaveBeenCalledWith( + 'repoContentsUrl', + 'https://gerrithost.org/repo/+/refs/heads/master', + ); + }); + afterEach(() => { + jest.resetAllMocks(); + }); +}); diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gerrit.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gerrit.ts new file mode 100644 index 0000000000..51355ec357 --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gerrit.ts @@ -0,0 +1,216 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import crypto from 'crypto'; +import { InputError } from '@backstage/errors'; +import { Config } from '@backstage/config'; +import { + GerritIntegrationConfig, + getGerritRequestOptions, + ScmIntegrationRegistry, +} from '@backstage/integration'; +import { createTemplateAction } from '../../createTemplateAction'; +import { getRepoSourceDirectory, parseRepoUrl } from './util'; +import fetch, { Response, RequestInit } from 'node-fetch'; +import { initRepoAndPush } from '../helpers'; + +const createGerritProject = async ( + config: GerritIntegrationConfig, + options: { + projectName: string; + parent: string; + owner: string; + description: string; + }, +): Promise => { + const { projectName, parent, owner, description } = options; + + const fetchOptions: RequestInit = { + method: 'PUT', + body: JSON.stringify({ + parent, + description, + owners: [owner], + create_empty_commit: false, + }), + headers: { + ...getGerritRequestOptions(config).headers, + 'Content-Type': 'application/json', + }, + }; + const response: Response = await fetch( + `${config.baseUrl}/a/projects/${encodeURIComponent(projectName)}`, + fetchOptions, + ); + if (response.status !== 201) { + throw new Error( + `Unable to create repository, ${response.status} ${ + response.statusText + }, ${await response.text()}`, + ); + } +}; + +const generateCommitMessage = ( + config: Config, + commitSubject?: string, +): string => { + const changeId = crypto.randomBytes(20).toString('hex'); + const msg = `${ + config.getOptionalString('scaffolder.defaultCommitMessage') || commitSubject + }\n\nChange-Id: I${changeId}`; + return msg; +}; + +/** + * Creates a new action that initializes a git repository of the content in the workspace + * and publishes it to a Gerrit instance. + * @public + */ +export function createPublishGerritAction(options: { + integrations: ScmIntegrationRegistry; + config: Config; +}) { + const { integrations, config } = options; + + return createTemplateAction<{ + repoUrl: string; + description: string; + defaultBranch?: string; + gitCommitMessage?: string; + gitAuthorName?: string; + gitAuthorEmail?: string; + }>({ + id: 'publish:gerrit', + description: + 'Initializes a git repository of the content in the workspace, and publishes it to Gerrit.', + schema: { + input: { + type: 'object', + required: ['repoUrl'], + properties: { + repoUrl: { + title: 'Repository Location', + type: 'string', + }, + description: { + title: 'Repository Description', + type: 'string', + }, + defaultBranch: { + title: 'Default Branch', + type: 'string', + description: `Sets the default branch on the repository. The default value is 'master'`, + }, + gitCommitMessage: { + title: 'Git Commit Message', + type: 'string', + description: `Sets the commit message on the repository. The default value is 'initial commit'`, + }, + gitAuthorName: { + title: 'Default Author Name', + type: 'string', + description: `Sets the default author name for the commit. The default value is 'Scaffolder'`, + }, + gitAuthorEmail: { + title: 'Default Author Email', + type: 'string', + description: `Sets the default author email for the commit.`, + }, + }, + }, + output: { + type: 'object', + properties: { + remoteUrl: { + title: 'A URL to the repository with the provider', + type: 'string', + }, + repoContentsUrl: { + title: 'A URL to the root of the repository', + type: 'string', + }, + }, + }, + }, + async handler(ctx) { + const { + repoUrl, + description, + defaultBranch = 'master', + gitAuthorName, + gitAuthorEmail, + gitCommitMessage = 'initial commit', + } = ctx.input; + const { repo, host, owner, workspace } = parseRepoUrl( + repoUrl, + integrations, + ); + + const integrationConfig = integrations.gerrit.byHost(host); + + if (!integrationConfig) { + throw new InputError( + `No matching integration configuration for host ${host}, please check your integrations config`, + ); + } + + if (!owner) { + throw new InputError( + `Invalid URL provider was included in the repo URL to create ${ctx.input.repoUrl}, missing owner`, + ); + } + if (!workspace) { + throw new InputError( + `Invalid URL provider was included in the repo URL to create ${ctx.input.repoUrl}, missing workspace`, + ); + } + + await createGerritProject(integrationConfig.config, { + description, + owner: owner, + projectName: repo, + parent: workspace, + }); + const auth = { + username: integrationConfig.config.username!, + password: integrationConfig.config.password!, + }; + const gitAuthorInfo = { + name: gitAuthorName + ? gitAuthorName + : config.getOptionalString('scaffolder.defaultAuthor.name'), + email: gitAuthorEmail + ? gitAuthorEmail + : config.getOptionalString('scaffolder.defaultAuthor.email'), + }; + + const remoteUrl = `${integrationConfig.config.cloneUrl}/a/${repo}`; + await initRepoAndPush({ + dir: getRepoSourceDirectory(ctx.workspacePath, undefined), + remoteUrl, + auth, + defaultBranch, + logger: ctx.logger, + commitMessage: generateCommitMessage(config, gitCommitMessage), + gitAuthorInfo, + }); + + const repoContentsUrl = `${integrationConfig.config.gitilesBaseUrl}/${repo}/+/refs/heads/${defaultBranch}`; + ctx.output('remoteUrl', remoteUrl); + ctx.output('repoContentsUrl', repoContentsUrl); + }, + }); +} diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.test.ts index f1a6c9bfe8..ff20d1014b 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.test.ts @@ -399,41 +399,35 @@ describe('publish:github', () => { collaborators: [ { access: 'pull', - username: 'robot-1', + user: 'robot-1', }, { access: 'push', - username: 'robot-2', + team: 'robot-2', }, ], }, }); const commonProperties = { - org: 'owner', owner: 'owner', repo: 'repo', }; - expect( - mockOctokit.rest.teams.addOrUpdateRepoPermissionsInOrg.mock.calls[1], - ).toEqual([ - { - ...commonProperties, - team_slug: 'robot-1', - permission: 'pull', - }, - ]); + expect(mockOctokit.rest.repos.addCollaborator).toHaveBeenCalledWith({ + ...commonProperties, + username: 'robot-1', + permission: 'pull', + }); expect( - mockOctokit.rest.teams.addOrUpdateRepoPermissionsInOrg.mock.calls[2], - ).toEqual([ - { - ...commonProperties, - team_slug: 'robot-2', - permission: 'push', - }, - ]); + mockOctokit.rest.teams.addOrUpdateRepoPermissionsInOrg, + ).toHaveBeenCalledWith({ + ...commonProperties, + org: 'owner', + team_slug: 'robot-2', + permission: 'push', + }); }); it('should ignore failures when adding multiple collaborators', async () => { @@ -465,11 +459,11 @@ describe('publish:github', () => { collaborators: [ { access: 'pull', - username: 'robot-1', + team: 'robot-1', }, { access: 'push', - username: 'robot-2', + team: 'robot-2', }, ], }, @@ -727,4 +721,26 @@ describe('publish:github', () => { requiredStatusCheckContexts: [], }); }); + + it('should not call enableBranchProtectionOnDefaultRepoBranch with protectDefaultBranch disabled', async () => { + mockOctokit.rest.users.getByUsername.mockResolvedValue({ + data: { type: 'User' }, + }); + + mockOctokit.rest.repos.createForAuthenticatedUser.mockResolvedValue({ + data: { + name: 'repository', + }, + }); + + await action.handler({ + ...mockContext, + input: { + ...mockContext.input, + protectDefaultBranch: false, + }, + }); + + expect(enableBranchProtectionOnDefaultRepoBranch).not.toHaveBeenCalled(); + }); }); diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.ts index 1f9125d962..d9a7112fc9 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.ts @@ -46,6 +46,7 @@ export function createPublishGithubAction(options: { description?: string; access?: string; defaultBranch?: string; + protectDefaultBranch?: boolean; deleteBranchOnMerge?: boolean; gitCommitMessage?: string; gitAuthorName?: string; @@ -57,10 +58,21 @@ export function createPublishGithubAction(options: { requireCodeOwnerReviews?: boolean; requiredStatusCheckContexts?: string[]; repoVisibility?: 'private' | 'internal' | 'public'; - collaborators?: Array<{ - username: string; - access: 'pull' | 'push' | 'admin' | 'maintain' | 'triage'; - }>; + collaborators?: Array< + | { + user: string; + access: 'pull' | 'push' | 'admin' | 'maintain' | 'triage'; + } + | { + team: string; + access: 'pull' | 'push' | 'admin' | 'maintain' | 'triage'; + } + | { + /** @deprecated This field is deprecated in favor of team */ + username: string; + access: 'pull' | 'push' | 'admin' | 'maintain' | 'triage'; + } + >; token?: string; topics?: string[]; }>({ @@ -111,6 +123,11 @@ export function createPublishGithubAction(options: { type: 'string', description: `Sets the default branch on the repository. The default value is 'master'`, }, + protectDefaultBranch: { + title: 'Protect Default Branch', + type: 'boolean', + description: `Protect the default branch after creating the repository. The default value is 'true'`, + }, deleteBranchOnMerge: { title: 'Delete Branch On Merge', type: 'boolean', @@ -154,22 +171,39 @@ export function createPublishGithubAction(options: { }, collaborators: { title: 'Collaborators', - description: 'Provide additional users with permissions', + description: 'Provide additional users or teams with permissions', type: 'array', items: { type: 'object', - required: ['username', 'access'], + additionalProperties: false, + required: ['access'], properties: { access: { type: 'string', description: 'The type of access for the user', enum: ['push', 'pull', 'admin', 'maintain', 'triage'], }, + user: { + type: 'string', + description: + 'The name of the user that will be added as a collaborator', + }, username: { type: 'string', - description: 'The username or group', + description: + 'Deprecated. Use the `team` or `user` field instead.', + }, + team: { + type: 'string', + description: + 'The name of the team that will be added as a collaborator', }, }, + oneOf: [ + { required: ['user'] }, + { required: ['username'] }, + { required: ['team'] }, + ], }, }, token: { @@ -209,6 +243,7 @@ export function createPublishGithubAction(options: { requiredStatusCheckContexts = [], repoVisibility = 'private', defaultBranch = 'master', + protectDefaultBranch = true, deleteBranchOnMerge = false, gitCommitMessage = 'initial commit', gitAuthorName, @@ -299,22 +334,40 @@ export function createPublishGithubAction(options: { } if (collaborators) { - for (const { - access: permission, - username: team_slug, - } of collaborators) { + for (const collaborator of collaborators) { try { - await client.rest.teams.addOrUpdateRepoPermissionsInOrg({ - org: owner, - team_slug, - owner, - repo, - permission, - }); + if ('user' in collaborator) { + await client.rest.repos.addCollaborator({ + owner, + repo, + username: collaborator.user, + permission: collaborator.access, + }); + } else if ('username' in collaborator) { + ctx.logger.warn( + 'The field `username` is deprecated in favor of `team` and will be removed in the future.', + ); + await client.rest.teams.addOrUpdateRepoPermissionsInOrg({ + org: owner, + team_slug: collaborator.username, + owner, + repo, + permission: collaborator.access, + }); + } else if ('team' in collaborator) { + await client.rest.teams.addOrUpdateRepoPermissionsInOrg({ + org: owner, + team_slug: collaborator.team, + owner, + repo, + permission: collaborator.access, + }); + } } catch (e) { assertError(e); + const name = extractCollaboratorName(collaborator); ctx.logger.warn( - `Skipping ${permission} access for ${team_slug}, ${e.message}`, + `Skipping ${collaborator.access} access for ${name}, ${e.message}`, ); } } @@ -360,21 +413,23 @@ export function createPublishGithubAction(options: { gitAuthorInfo, }); - try { - await enableBranchProtectionOnDefaultRepoBranch({ - owner, - client, - repoName: newRepo.name, - logger: ctx.logger, - defaultBranch, - requireCodeOwnerReviews, - requiredStatusCheckContexts, - }); - } catch (e) { - assertError(e); - ctx.logger.warn( - `Skipping: default branch protection on '${newRepo.name}', ${e.message}`, - ); + if (protectDefaultBranch) { + try { + await enableBranchProtectionOnDefaultRepoBranch({ + owner, + client, + repoName: newRepo.name, + logger: ctx.logger, + defaultBranch, + requireCodeOwnerReviews, + requiredStatusCheckContexts, + }); + } catch (e) { + assertError(e); + ctx.logger.warn( + `Skipping: default branch protection on '${newRepo.name}', ${e.message}`, + ); + } } ctx.output('remoteUrl', remoteUrl); @@ -382,3 +437,11 @@ export function createPublishGithubAction(options: { }, }); } + +function extractCollaboratorName( + collaborator: { user: string } | { team: string } | { username: string }, +) { + if ('username' in collaborator) return collaborator.username; + if ('user' in collaborator) return collaborator.user; + return collaborator.team; +} diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/githubPullRequest.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/githubPullRequest.ts index 3bf90d1e30..f8f9b22d61 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/githubPullRequest.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/githubPullRequest.ts @@ -14,21 +14,19 @@ * limitations under the License. */ -import fs from 'fs-extra'; -import { parseRepoUrl, isExecutable } from './util'; - +import path from 'path'; +import { parseRepoUrl } from './util'; import { GithubCredentialsProvider, ScmIntegrationRegistry, } from '@backstage/integration'; -import { zipObject } from 'lodash'; import { createTemplateAction } from '../../createTemplateAction'; import { Octokit } from 'octokit'; import { InputError, CustomErrorBase } from '@backstage/errors'; import { createPullRequest } from 'octokit-plugin-create-pull-request'; -import globby from 'globby'; import { resolveSafeChildPath } from '@backstage/backend-common'; import { getOctokitOptions } from '../github/helpers'; +import { serializeDirectoryContents } from '../../../../lib/files'; export type Encoding = 'utf-8' | 'base64'; @@ -219,56 +217,39 @@ export const createPublishGithubPullRequestAction = ({ ? resolveSafeChildPath(ctx.workspacePath, sourcePath) : ctx.workspacePath; - const localFilePaths = await globby(['./**', './**/.*', '!.git'], { - cwd: fileRoot, + const directoryContents = await serializeDirectoryContents(fileRoot, { gitignore: true, - dot: true, }); - - const fileContents = await Promise.all( - localFilePaths.map(filePath => { - const absPath = resolveSafeChildPath(fileRoot, filePath); - const base64EncodedContent = fs - .readFileSync(absPath) - .toString('base64'); - const fileStat = fs.statSync(absPath); - // See the properties of tree items - // in https://docs.github.com/en/rest/reference/git#trees - const githubTreeItemMode = isExecutable(fileStat.mode) - ? '100755' - : '100644'; - // Always use base64 encoding to avoid doubling a binary file in size - // due to interpreting a binary file as utf-8 and sending github - // the utf-8 encoded content. - // - // For example, the original gradle-wrapper.jar is 57.8k in https://github.com/kennethzfeng/pull-request-test/pull/5/files. - // Its size could be doubled to 98.3K (See https://github.com/kennethzfeng/pull-request-test/pull/4/files) - const encoding: Encoding = 'base64'; - return { - encoding: encoding, - content: base64EncodedContent, - mode: githubTreeItemMode, - }; - }), + const files = Object.fromEntries( + directoryContents.map(file => [ + targetPath ? path.posix.join(targetPath, file.path) : file.path, + { + // See the properties of tree items + // in https://docs.github.com/en/rest/reference/git#trees + mode: file.executable ? '100755' : '100644', + // Always use base64 encoding to avoid doubling a binary file in size + // due to interpreting a binary file as utf-8 and sending github + // the utf-8 encoded content. + // + // For example, the original gradle-wrapper.jar is 57.8k in https://github.com/kennethzfeng/pull-request-test/pull/5/files. + // Its size could be doubled to 98.3K (See https://github.com/kennethzfeng/pull-request-test/pull/4/files) + encoding: 'base64' as const, + content: file.content.toString('base64'), + }, + ]), ); - const repoFilePaths = localFilePaths.map(repoFilePath => { - return targetPath ? `${targetPath}/${repoFilePath}` : repoFilePath; - }); - - const changes = [ - { - files: zipObject(repoFilePaths, fileContents), - commit: title, - }, - ]; - try { const response = await client.createPullRequest({ owner, repo, title, - changes, + changes: [ + { + files, + commit: title, + }, + ], body: description, head: branchName, draft, diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlabMergeRequest.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlabMergeRequest.ts index 63cfeaa8b3..f717a7a0c6 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlabMergeRequest.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlabMergeRequest.ts @@ -14,15 +14,14 @@ * limitations under the License. */ import { createTemplateAction } from '../../createTemplateAction'; -import { readFile } from 'fs-extra'; import { Gitlab } from '@gitbeaker/node'; -import globby from 'globby'; import { Types } from '@gitbeaker/core'; - +import path from 'path'; import { ScmIntegrationRegistry } from '@backstage/integration'; import { InputError } from '@backstage/errors'; import { parseRepoUrl } from './util'; import { resolveSafeChildPath } from '@backstage/backend-common'; +import { serializeDirectoryContents } from '../../../../lib/files'; /** * Create a new action that creates a gitlab merge request. @@ -35,18 +34,19 @@ export const createPublishGitlabMergeRequestAction = (options: { const { integrations } = options; return createTemplateAction<{ - projectid: string; repoUrl: string; title: string; description: string; branchName: string; targetPath: string; token?: string; + /** @deprecated Use projectPath instead */ + projectid?: string; }>({ id: 'publish:gitlab:merge-request', schema: { input: { - required: ['projectid', 'repoUrl', 'targetPath', 'branchName'], + required: ['repoUrl', 'targetPath', 'branchName'], type: 'object', properties: { repoUrl: { @@ -54,6 +54,7 @@ export const createPublishGitlabMergeRequestAction = (options: { title: 'Repository Location', description: `Accepts the format 'gitlab.com/group_name/project_name' where 'project_name' is the repository name and 'group_name' is a group or username`, }, + /** @deprecated Use projectPath instead */ projectid: { type: 'string', title: 'projectid', @@ -93,6 +94,10 @@ export const createPublishGitlabMergeRequestAction = (options: { title: 'Gitlab Project id/Name(slug)', type: 'string', }, + projectPath: { + title: 'Gitlab Project path', + type: 'string', + }, mergeRequestURL: { title: 'MergeRequest(MR) URL', type: 'string', @@ -103,10 +108,16 @@ export const createPublishGitlabMergeRequestAction = (options: { }, async handler(ctx) { const repoUrl = ctx.input.repoUrl; - const { host } = parseRepoUrl(repoUrl, integrations); - const integrationConfig = integrations.gitlab.byHost(host); + const { host, owner, repo } = parseRepoUrl(repoUrl, integrations); + const projectPath = `${owner}/${repo}`; - const actions: Types.CommitAction[] = []; + if (ctx.input.projectid) { + const deprecationWarning = `Property "projectid" is deprecated and no longer to needed to create a MR`; + ctx.logger.warn(deprecationWarning); + console.warn(deprecationWarning); + } + + const integrationConfig = integrations.gitlab.byHost(host); const destinationBranch = ctx.input.branchName; @@ -128,36 +139,28 @@ export const createPublishGitlabMergeRequestAction = (options: { [tokenType]: token, }); - const fileRoot = ctx.workspacePath; - const localFilePaths = await globby([`${ctx.input.targetPath}/**`], { - cwd: fileRoot, - gitignore: true, - dot: true, - }); - - const fileContents = await Promise.all( - localFilePaths.map(p => readFile(resolveSafeChildPath(fileRoot, p))), + const targetPath = resolveSafeChildPath( + ctx.workspacePath, + ctx.input.targetPath, ); - - const repoFilePaths = localFilePaths.map(repoFilePath => { - return repoFilePath; + const fileContents = await serializeDirectoryContents(targetPath, { + gitignore: true, }); - for (let i = 0; i < repoFilePaths.length; i++) { - actions.push({ - action: 'create', - filePath: repoFilePaths[i], - content: fileContents[i].toString(), - }); - } - - const projects = await api.Projects.show(ctx.input.projectid); + const actions: Types.CommitAction[] = fileContents.map(file => ({ + action: 'create', + filePath: path.posix.join(ctx.input.targetPath, file.path), + encoding: 'base64', + content: file.content.toString('base64'), + execute_filemode: file.executable, + })); + const projects = await api.Projects.show(projectPath); const { default_branch: defaultBranch } = projects; try { await api.Branches.create( - ctx.input.projectid, + projectPath, destinationBranch, String(defaultBranch), ); @@ -167,7 +170,7 @@ export const createPublishGitlabMergeRequestAction = (options: { try { await api.Commits.create( - ctx.input.projectid, + projectPath, destinationBranch, ctx.input.title, actions, @@ -180,7 +183,7 @@ export const createPublishGitlabMergeRequestAction = (options: { try { const mergeRequestUrl = await api.MergeRequests.create( - ctx.input.projectid, + projectPath, destinationBranch, String(defaultBranch), ctx.input.title, @@ -188,7 +191,9 @@ export const createPublishGitlabMergeRequestAction = (options: { ).then((mergeRequest: { web_url: string }) => { return mergeRequest.web_url; }); - ctx.output('projectid', ctx.input.projectid); + /** @deprecated */ + ctx.output('projectid', projectPath); + ctx.output('projectPath', projectPath); ctx.output('mergeRequestUrl', mergeRequestUrl); } catch (e) { throw new InputError(`Merge request creation failed${e}`); diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/index.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/index.ts index 02266b6a06..11ecd2b127 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/index.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/index.ts @@ -19,6 +19,7 @@ export { createPublishBitbucketAction } from './bitbucket'; export { createPublishBitbucketCloudAction } from './bitbucketCloud'; export { createPublishBitbucketServerAction } from './bitbucketServer'; export { createPublishFileAction } from './file'; +export { createPublishGerritAction } from './gerrit'; export { createPublishGithubAction } from './github'; export { createPublishGithubPullRequestAction } from './githubPullRequest'; export type { diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/types.ts b/plugins/scaffolder-backend/src/scaffolder/actions/types.ts index 9a4ae8b2bf..ff15300b4a 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/types.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/types.ts @@ -39,12 +39,19 @@ export type ActionContext = { createTemporaryDirectory(): Promise; templateInfo?: TemplateInfo; + + /** + * Whether this action invocation is a dry-run or not. + * This will only ever be true if the actions as marked as supporting dry-runs. + */ + isDryRun?: boolean; }; /** @public */ export type TemplateAction = { id: string; description?: string; + supportsDryRun?: boolean; schema?: { input?: Schema; output?: Schema; diff --git a/plugins/scaffolder-backend/src/scaffolder/dryrun/DecoratedActionsRegistry.ts b/plugins/scaffolder-backend/src/scaffolder/dryrun/DecoratedActionsRegistry.ts new file mode 100644 index 0000000000..6f11d3b6e8 --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/dryrun/DecoratedActionsRegistry.ts @@ -0,0 +1,39 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { JsonObject } from '@backstage/types'; +import { TemplateAction, TemplateActionRegistry } from '../actions'; + +/** @internal */ +export class DecoratedActionsRegistry extends TemplateActionRegistry { + constructor( + private readonly innerRegistry: TemplateActionRegistry, + extraActions: Array>, + ) { + super(); + for (const action of extraActions) { + this.register(action); + } + } + + get(actionId: string): TemplateAction { + try { + return super.get(actionId); + } catch { + return this.innerRegistry.get(actionId); + } + } +} diff --git a/plugins/scaffolder-backend/src/scaffolder/dryrun/createDryRunner.ts b/plugins/scaffolder-backend/src/scaffolder/dryrun/createDryRunner.ts new file mode 100644 index 0000000000..f2c3c13509 --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/dryrun/createDryRunner.ts @@ -0,0 +1,146 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ScmIntegrations } from '@backstage/integration'; +import { TaskSpec } from '@backstage/plugin-scaffolder-common'; +import { JsonObject } from '@backstage/types'; +import { v4 as uuid } from 'uuid'; +import { pathToFileURL } from 'url'; +import { Logger } from 'winston'; +import { + deserializeDirectoryContents, + SerializedFile, + serializeDirectoryContents, +} from '../../lib/files'; +import { TemplateFilter } from '../../lib/templating'; +import { createTemplateAction, TemplateActionRegistry } from '../actions'; +import { NunjucksWorkflowRunner } from '../tasks/NunjucksWorkflowRunner'; +import { TaskSecrets } from '../tasks/types'; +import { DecoratedActionsRegistry } from './DecoratedActionsRegistry'; +import fs from 'fs-extra'; +import { resolveSafeChildPath } from '@backstage/backend-common'; + +interface DryRunInput { + spec: TaskSpec; + secrets?: TaskSecrets; + directoryContents: SerializedFile[]; +} + +interface DryRunResult { + log: Array<{ body: JsonObject }>; + directoryContents: SerializedFile[]; + output: JsonObject; +} + +/** @internal */ +export type TemplateTesterCreateOptions = { + logger: Logger; + integrations: ScmIntegrations; + actionRegistry: TemplateActionRegistry; + workingDirectory: string; + additionalTemplateFilters?: Record; +}; + +/** + * Executes a dry-run of the provided template. + * + * The provided content will be extracted into a temporary directory + * which is then use as the base for any relative file fetch paths. + * + * @internal + */ +export function createDryRunner(options: TemplateTesterCreateOptions) { + return async function dryRun(input: DryRunInput): Promise { + let contentPromise; + + const workflowRunner = new NunjucksWorkflowRunner({ + ...options, + actionRegistry: new DecoratedActionsRegistry(options.actionRegistry, [ + createTemplateAction({ + id: 'dry-run:extract', + supportsDryRun: true, + async handler(ctx) { + contentPromise = serializeDirectoryContents(ctx.workspacePath); + await contentPromise.catch(() => {}); + }, + }), + ]), + }); + + const dryRunId = uuid(); + const log = new Array<{ body: JsonObject }>(); + const contentsPath = resolveSafeChildPath( + options.workingDirectory, + `dry-run-content-${dryRunId}`, + ); + + try { + await deserializeDirectoryContents(contentsPath, input.directoryContents); + + const result = await workflowRunner.execute({ + spec: { + ...input.spec, + steps: [ + ...input.spec.steps, + { + id: dryRunId, + name: 'dry-run:extract', + action: 'dry-run:extract', + }, + ], + templateInfo: { + entityRef: 'template:default/dry-run', + baseUrl: pathToFileURL( + resolveSafeChildPath(contentsPath, 'template.yaml'), + ).toString(), + }, + }, + secrets: input.secrets, + // No need to update this at the end of the run, so just hard-code it + done: false, + isDryRun: true, + getWorkspaceName: async () => `dry-run-${dryRunId}`, + async emitLog(message: string, logMetadata?: JsonObject) { + if (logMetadata?.stepId === dryRunId) { + return; + } + log.push({ + body: { + ...logMetadata, + message, + }, + }); + }, + async complete() { + throw new Error('Not implemented'); + }, + }); + + if (!contentPromise) { + throw new Error('Content extraction step was skipped'); + } + const directoryContents = await contentPromise; + + return { + log, + directoryContents, + output: result.output, + }; + } finally { + await fs.remove(contentsPath); + } + }; +} diff --git a/plugins/scaffolder-backend/src/scaffolder/dryrun/index.ts b/plugins/scaffolder-backend/src/scaffolder/dryrun/index.ts new file mode 100644 index 0000000000..9ae38e6df2 --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/dryrun/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { createDryRunner } from './createDryRunner'; diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.ts index d0a0fec0a6..d62e59744f 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.ts @@ -64,6 +64,14 @@ export type DatabaseTaskStoreOptions = { database: Knex; }; +const parseSqlDateToIsoString = (input: T): T | string => { + if (typeof input === 'string') { + return DateTime.fromSQL(input, { zone: 'UTC' }).toISO(); + } + + return input; +}; + /** * DatabaseTaskStore * @@ -85,6 +93,31 @@ export class DatabaseTaskStore implements TaskStore { this.db = options.database; } + async list(options: { + createdBy?: string; + }): Promise<{ tasks: SerializedTask[] }> { + const queryBuilder = this.db('tasks'); + + if (options.createdBy) { + queryBuilder.where({ + created_by: options.createdBy, + }); + } + + const results = await queryBuilder.orderBy('created_at', 'desc').select(); + + const tasks = results.map(result => ({ + id: result.id, + spec: JSON.parse(result.spec), + status: result.status, + createdBy: result.created_by ?? undefined, + lastHeartbeatAt: parseSqlDateToIsoString(result.last_heartbeat_at), + createdAt: parseSqlDateToIsoString(result.created_at), + })); + + return { tasks }; + } + async getTask(taskId: string): Promise { const [result] = await this.db('tasks') .where({ id: taskId }) @@ -99,8 +132,8 @@ export class DatabaseTaskStore implements TaskStore { id: result.id, spec, status: result.status, - lastHeartbeatAt: result.last_heartbeat_at, - createdAt: result.created_at, + lastHeartbeatAt: parseSqlDateToIsoString(result.last_heartbeat_at), + createdAt: parseSqlDateToIsoString(result.created_at), createdBy: result.created_by ?? undefined, secrets, }; @@ -292,10 +325,7 @@ export class DatabaseTaskStore implements TaskStore { taskId, body, type: event.event_type, - createdAt: - typeof event.created_at === 'string' - ? DateTime.fromSQL(event.created_at, { zone: 'UTC' }).toISO() - : event.created_at, + createdAt: parseSqlDateToIsoString(event.created_at), }; } catch (error) { throw new Error( diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.ts index c9d0714392..97e37fd3eb 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.ts @@ -23,7 +23,7 @@ import nunjucks from 'nunjucks'; import { JsonObject, JsonValue } from '@backstage/types'; import { InputError } from '@backstage/errors'; import { PassThrough } from 'stream'; -import { isTruthy } from './helper'; +import { generateExampleOutput, isTruthy } from './helper'; import { validate as validateJsonSchema } from 'jsonschema'; import { parseRepoUrl } from '../actions/builtin/publish/util'; import { TemplateActionRegistry } from '../actions'; @@ -236,6 +236,27 @@ export class NunjucksWorkflowRunner implements WorkflowRunner { const action = this.options.actionRegistry.get(step.action); const { taskLogger, streamLogger } = createStepLogger({ task, step }); + if (task.isDryRun && !action.supportsDryRun) { + task.emitLog( + `Skipping because ${action.id} does not support dry-run`, + { + stepId: step.id, + status: 'skipped', + }, + ); + const outputSchema = action.schema?.output; + if (outputSchema) { + context.steps[step.id] = { + output: generateExampleOutput(outputSchema) as { + [name in string]: JsonValue; + }, + }; + } else { + context.steps[step.id] = { output: {} }; + } + continue; + } + // Secrets are only passed when templating the input to actions for security reasons const input = (step.input && diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/StorageTaskBroker.test.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/StorageTaskBroker.test.ts index bb375e5a0f..2350072806 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/StorageTaskBroker.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/StorageTaskBroker.test.ts @@ -202,4 +202,31 @@ describe('StorageTaskBroker', () => { expect(task.done).toBe(true); }); + + it('should list all tasks', async () => { + const broker = new StorageTaskBroker(storage, logger); + const { taskId } = await broker.dispatch({ spec: {} as TaskSpec }); + + const promise = broker.list(); + await expect(promise).resolves.toEqual({ + tasks: expect.arrayContaining([ + expect.objectContaining({ + id: taskId, + }), + ]), + }); + }); + + it('should list only tasks createdBy a specific user', async () => { + const broker = new StorageTaskBroker(storage, logger); + const { taskId } = await broker.dispatch({ + spec: {} as TaskSpec, + createdBy: 'user:default/foo', + }); + + const task = await storage.getTask(taskId); + + const promise = broker.list({ createdBy: 'user:default/foo' }); + await expect(promise).resolves.toEqual({ tasks: [task] }); + }); }); diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/StorageTaskBroker.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/StorageTaskBroker.ts index 17f07f702d..9e60bc7727 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/StorageTaskBroker.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/StorageTaskBroker.ts @@ -150,6 +150,18 @@ export class StorageTaskBroker implements TaskBroker { private readonly storage: TaskStore, private readonly logger: Logger, ) {} + + async list(options?: { + createdBy?: string; + }): Promise<{ tasks: SerializedTask[] }> { + if (!this.storage.list) { + throw new Error( + 'TaskStore does not implement the list method. Please implement the list method to be able to list tasks', + ); + } + return await this.storage.list({ createdBy: options?.createdBy }); + } + private deferredDispatch = defer(); /** diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/helper.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/helper.ts index 2058ce917f..76951afde4 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/helper.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/helper.ts @@ -15,6 +15,7 @@ */ import { isArray } from 'lodash'; +import { Schema } from 'jsonschema'; /** * Returns true if the input is not `false`, `undefined`, `null`, `""`, `0`, or @@ -24,3 +25,31 @@ import { isArray } from 'lodash'; export function isTruthy(value: any): boolean { return isArray(value) ? value.length > 0 : !!value; } + +export function generateExampleOutput(schema: Schema): unknown { + const { examples } = schema as { examples?: unknown }; + if (examples && Array.isArray(examples)) { + return examples[0]; + } + if (schema.type === 'object') { + return Object.fromEntries( + Object.entries(schema.properties ?? {}).map(([key, value]) => [ + key, + generateExampleOutput(value), + ]), + ); + } else if (schema.type === 'array') { + const [firstSchema] = [schema.items]?.flat(); + if (firstSchema) { + return [generateExampleOutput(firstSchema)]; + } + return []; + } else if (schema.type === 'string') { + return ''; + } else if (schema.type === 'number') { + return 0; + } else if (schema.type === 'boolean') { + return false; + } + return ''; +} diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/types.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/types.ts index a3a9ef84d1..4b63a0cdd3 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/types.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/types.ts @@ -111,6 +111,7 @@ export interface TaskContext { secrets?: TaskSecrets; createdBy?: string; done: boolean; + isDryRun?: boolean; emitLog(message: string, logMetadata?: JsonObject): Promise; complete(result: TaskCompletionState, metadata?: JsonObject): Promise; getWorkspaceName(): Promise; @@ -132,6 +133,7 @@ export interface TaskBroker { after: number | undefined; }): Observable<{ events: SerializedTaskEvent[] }>; get(taskId: string): Promise; + list?(options?: { createdBy?: string }): Promise<{ tasks: SerializedTask[] }>; } /** @@ -192,6 +194,7 @@ export interface TaskStore { listStaleTasks(options: { timeoutS: number }): Promise<{ tasks: { taskId: string }[]; }>; + list?(options: { createdBy?: string }): Promise<{ tasks: SerializedTask[] }>; emitLogEvent({ taskId, body }: TaskStoreEmitOptions): Promise; listEvents({ diff --git a/plugins/scaffolder-backend/src/service/helpers.ts b/plugins/scaffolder-backend/src/service/helpers.ts index 23200d6aac..3412fec2d3 100644 --- a/plugins/scaffolder-backend/src/service/helpers.ts +++ b/plugins/scaffolder-backend/src/service/helpers.ts @@ -21,7 +21,6 @@ import { parseLocationRef, ANNOTATION_SOURCE_LOCATION, CompoundEntityRef, - DEFAULT_NAMESPACE, stringifyEntityRef, } from '@backstage/catalog-model'; import { Config } from '@backstage/config'; @@ -95,11 +94,6 @@ export async function findTemplate(options: { }): Promise { const { entityRef, token, catalogApi } = options; - if (entityRef.namespace.toLocaleLowerCase('en-US') !== DEFAULT_NAMESPACE) { - throw new InputError( - `Invalid namespace, only '${DEFAULT_NAMESPACE}' namespace is supported`, - ); - } if (entityRef.kind.toLocaleLowerCase('en-US') !== 'template') { throw new InputError(`Invalid kind, only 'Template' kind is supported`); } diff --git a/plugins/scaffolder-backend/src/service/router.test.ts b/plugins/scaffolder-backend/src/service/router.test.ts index 14c543249e..aea33f1158 100644 --- a/plugins/scaffolder-backend/src/service/router.test.ts +++ b/plugins/scaffolder-backend/src/service/router.test.ts @@ -131,6 +131,7 @@ describe('createRouter', () => { jest.spyOn(taskBroker, 'dispatch'); jest.spyOn(taskBroker, 'get'); + jest.spyOn(taskBroker, 'list'); jest.spyOn(taskBroker, 'event$'); const router = await createRouter({ @@ -216,23 +217,18 @@ describe('createRouter', () => { const mockToken = 'blob.eyJzdWIiOiJ1c2VyOmRlZmF1bHQvZ3Vlc3QiLCJuYW1lIjoiSm9obiBEb2UifQ.blob'; - console.log( - JSON.stringify( - await request(app) - .post('/v2/tasks') - .set('Authorization', `Bearer ${mockToken}`) - .send({ - templateRef: stringifyEntityRef({ - kind: 'template', - name: 'create-react-app-template', - }), - values: { - required: 'required-value', - }, - }), - ), - ); - + await request(app) + .post('/v2/tasks') + .set('Authorization', `Bearer ${mockToken}`) + .send({ + templateRef: stringifyEntityRef({ + kind: 'template', + name: 'create-react-app-template', + }), + values: { + required: 'required-value', + }, + }); expect(broker).toHaveBeenCalledWith( expect.objectContaining({ createdBy: 'user:default/guest', @@ -294,6 +290,77 @@ describe('createRouter', () => { }); }); + describe('GET /v2/tasks', () => { + it('return all tasks', async () => { + ( + taskBroker.list as jest.Mocked>['list'] + ).mockResolvedValue({ + tasks: [ + { + id: 'a-random-id', + spec: {} as any, + status: 'completed', + createdAt: '', + createdBy: '', + }, + ], + }); + + const response = await request(app).get(`/v2/tasks`); + expect(taskBroker.list).toBeCalledWith({ + createdBy: undefined, + }); + expect(response.status).toEqual(200); + expect(response.body).toStrictEqual({ + tasks: [ + { + id: 'a-random-id', + spec: {} as any, + status: 'completed', + createdAt: '', + createdBy: '', + }, + ], + }); + }); + + it('return filtered tasks', async () => { + ( + taskBroker.list as jest.Mocked>['list'] + ).mockResolvedValue({ + tasks: [ + { + id: 'a-random-id', + spec: {} as any, + status: 'completed', + createdAt: '', + createdBy: 'user:default/foo', + }, + ], + }); + + const response = await request(app).get( + `/v2/tasks?createdBy=user:default/foo`, + ); + expect(taskBroker.list).toBeCalledWith({ + createdBy: 'user:default/foo', + }); + + expect(response.status).toEqual(200); + expect(response.body).toStrictEqual({ + tasks: [ + { + id: 'a-random-id', + spec: {} as any, + status: 'completed', + createdAt: '', + createdBy: 'user:default/foo', + }, + ], + }); + }); + }); + describe('GET /v2/tasks/:taskId', () => { it('does not divulge secrets', async () => { (taskBroker.get as jest.Mocked['get']).mockResolvedValue({ @@ -302,6 +369,7 @@ describe('createRouter', () => { status: 'completed', createdAt: '', secrets: { backstageToken: 'secret' }, + createdBy: '', }); const response = await request(app).get(`/v2/tasks/a-random-id`); diff --git a/plugins/scaffolder-backend/src/service/router.ts b/plugins/scaffolder-backend/src/service/router.ts index 5453dcd234..4919d2e5f9 100644 --- a/plugins/scaffolder-backend/src/service/router.ts +++ b/plugins/scaffolder-backend/src/service/router.ts @@ -22,18 +22,20 @@ import { UserEntity, } from '@backstage/catalog-model'; import { Entity } from '@backstage/catalog-model'; -import { Config } from '@backstage/config'; +import { Config, JsonObject } from '@backstage/config'; import { InputError, NotFoundError } from '@backstage/errors'; import { ScmIntegrations } from '@backstage/integration'; import { TemplateEntityV1beta3, TaskSpec, + templateEntityV1beta3Validator, } from '@backstage/plugin-scaffolder-common'; import express from 'express'; import Router from 'express-promise-router'; import { validate } from 'jsonschema'; import { Logger } from 'winston'; import { TemplateFilter } from '../lib'; +import { z } from 'zod'; import { createBuiltinActions, DatabaseTaskStore, @@ -42,6 +44,7 @@ import { TemplateAction, TemplateActionRegistry, } from '../scaffolder'; +import { createDryRunner } from '../scaffolder/dryrun'; import { StorageTaskBroker } from '../scaffolder/tasks/StorageTaskBroker'; import { getEntityBaseUrl, getWorkingDirectory, findTemplate } from './helpers'; @@ -129,6 +132,14 @@ export async function createRouter( actionsToRegister.forEach(action => actionRegistry.register(action)); workers.forEach(worker => worker.start()); + const dryRunner = createDryRunner({ + actionRegistry, + integrations, + logger, + workingDirectory, + additionalTemplateFilters, + }); + router .get( '/v2/templates/:namespace/:kind/:name/parameter-schema', @@ -241,6 +252,28 @@ export async function createRouter( res.status(201).json({ id: result.taskId }); }) + .get('/v2/tasks', async (req, res) => { + const [userEntityRef] = [req.query.createdBy].flat(); + + if ( + typeof userEntityRef !== 'string' && + typeof userEntityRef !== 'undefined' + ) { + throw new InputError('createdBy query parameter must be a string'); + } + + if (!taskBroker.list) { + throw new Error( + 'TaskBroker does not support listing tasks, please implement the list method on the TaskBroker.', + ); + } + + const tasks = await taskBroker.list({ + createdBy: userEntityRef, + }); + + res.status(200).json(tasks); + }) .get('/v2/tasks/:taskId', async (req, res) => { const { taskId } = req.params; const task = await taskBroker.get(taskId); @@ -324,6 +357,67 @@ export async function createRouter( subscription.unsubscribe(); clearTimeout(timeout); }); + }) + .post('/v2/dry-run', async (req, res) => { + const bodySchema = z.object({ + template: z.unknown(), + values: z.record(z.unknown()), + secrets: z.record(z.string()).optional(), + directoryContents: z.array( + z.object({ path: z.string(), base64Content: z.string() }), + ), + }); + const body = await bodySchema.parseAsync(req.body).catch(e => { + throw new InputError(`Malformed request: ${e}`); + }); + + const template = body.template as TemplateEntityV1beta3; + if (!(await templateEntityV1beta3Validator.check(template))) { + throw new InputError('Input template is not a template'); + } + + const { token } = parseBearerToken(req.headers.authorization); + + for (const parameters of [template.spec.parameters ?? []].flat()) { + const result = validate(body.values, parameters); + if (!result.valid) { + res.status(400).json({ errors: result.errors }); + return; + } + } + + const steps = template.spec.steps.map((step, index) => ({ + ...step, + id: step.id ?? `step-${index + 1}`, + name: step.name ?? step.action, + })); + + const result = await dryRunner({ + spec: { + apiVersion: template.apiVersion, + steps, + output: template.spec.output ?? {}, + parameters: body.values as JsonObject, + }, + directoryContents: (body.directoryContents ?? []).map(file => ({ + path: file.path, + content: Buffer.from(file.base64Content, 'base64'), + })), + secrets: { + ...body.secrets, + ...(token && { backstageToken: token }), + }, + }); + + res.status(200).json({ + ...result, + steps, + directoryContents: result.directoryContents.map(file => ({ + path: file.path, + executable: file.executable, + base64Content: file.content.toString('base64'), + })), + }); }); const app = express(); diff --git a/plugins/scaffolder-common/CHANGELOG.md b/plugins/scaffolder-common/CHANGELOG.md index bde6c24a1a..27a1abc7d2 100644 --- a/plugins/scaffolder-common/CHANGELOG.md +++ b/plugins/scaffolder-common/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-scaffolder-common +## 1.1.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.0.3 + +## 1.1.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.0.3-next.0 + ## 1.1.0 ### Minor Changes diff --git a/plugins/scaffolder-common/package.json b/plugins/scaffolder-common/package.json index b0816d7796..beeec6430d 100644 --- a/plugins/scaffolder-common/package.json +++ b/plugins/scaffolder-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-scaffolder-common", "description": "Common functionalities for the scaffolder, to be shared between scaffolder and scaffolder-backend plugin", - "version": "1.1.0", + "version": "1.1.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -39,10 +39,10 @@ "url": "https://github.com/backstage/backstage/issues" }, "dependencies": { - "@backstage/catalog-model": "^1.0.2", + "@backstage/catalog-model": "^1.0.3", "@backstage/types": "^1.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1" + "@backstage/cli": "^0.17.2" } } diff --git a/plugins/scaffolder/CHANGELOG.md b/plugins/scaffolder/CHANGELOG.md index 72f9ba67a3..617fe95457 100644 --- a/plugins/scaffolder/CHANGELOG.md +++ b/plugins/scaffolder/CHANGELOG.md @@ -1,5 +1,116 @@ # @backstage/plugin-scaffolder +## 1.3.0 + +### Minor Changes + +- dc39366bdb: - Added a new page under `/create/tasks` to show tasks that have been run by the Scaffolder. + - Ability to filter these tasks by the signed in user, and all tasks. + - Added optional method to the `ScaffolderApi` interface called `listTasks` to get tasks with an required `filterByOwnership` parameter. +- 86a4a0f72d: Get data of other fields in Form from a custom field in template Scaffolder. + following: + + ```tsx + const CustomFieldExtensionComponent = (props: FieldExtensionComponentProps) => { + const { formData } = props.formContext; + ... + }; + + const CustomFieldExtension = scaffolderPlugin.provide( + createScaffolderFieldExtension({ + name: ..., + component: CustomFieldExtensionComponent, + validation: ... + }) + ); + ``` + +- 72dfcbc8bf: Gerrit Integration: Implemented a `RepoUrlPicker` for Gerrit. +- f93af969cd: Added the ability to support running of templates that are not in the `default` namespace + +### Patch Changes + +- ac0c7e45ee: Fixes review mask in `MultistepJsonForm` to work as documented. `show: true` no longer needed when mask is set. +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- fd505f40c0: Handle binary files and files that are too large during dry-run content upload. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/plugin-catalog-common@1.0.3 + - @backstage/core-components@0.9.5 + - @backstage/integration@1.2.1 + - @backstage/catalog-client@1.0.3 + - @backstage/core-plugin-api@1.0.3 + - @backstage/integration-react@1.1.1 + - @backstage/catalog-model@1.0.3 + - @backstage/plugin-permission-react@0.4.2 + - @backstage/plugin-scaffolder-common@1.1.1 + +## 1.3.0-next.2 + +### Minor Changes + +- dc39366bdb: - Added a new page under `/create/tasks` to show tasks that have been run by the Scaffolder. + - Ability to filter these tasks by the signed in user, and all tasks. + - Added optional method to the `ScaffolderApi` interface called `listTasks` to get tasks with an required `filterByOwnership` parameter. + +### Patch Changes + +- ac0c7e45ee: Fixes review mask in `MultistepJsonForm` to work as documented. `show: true` no longer needed when mask is set. +- fd505f40c0: Handle binary files and files that are too large during dry-run content upload. +- Updated dependencies + - @backstage/plugin-catalog-common@1.0.3-next.1 + - @backstage/core-components@0.9.5-next.2 + - @backstage/integration@1.2.1-next.2 + +## 1.3.0-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/catalog-client@1.0.3-next.0 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/integration-react@1.1.1-next.1 + - @backstage/integration@1.2.1-next.1 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + - @backstage/plugin-permission-react@0.4.2-next.0 + - @backstage/plugin-catalog-common@1.0.3-next.0 + - @backstage/plugin-scaffolder-common@1.1.1-next.0 + +## 1.3.0-next.0 + +### Minor Changes + +- 86a4a0f72d: Get data of other fields in Form from a custom field in template Scaffolder. + following: + + ```tsx + const CustomFieldExtensionComponent = (props: FieldExtensionComponentProps) => { + const { formData } = props.formContext; + ... + }; + + const CustomFieldExtension = scaffolderPlugin.provide( + createScaffolderFieldExtension({ + name: ..., + component: CustomFieldExtensionComponent, + validation: ... + }) + ); + ``` + +- 72dfcbc8bf: Gerrit Integration: Implemented a `RepoUrlPicker` for Gerrit. + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + - @backstage/integration@1.2.1-next.0 + - @backstage/integration-react@1.1.1-next.0 + ## 1.2.0 ### Minor Changes diff --git a/plugins/scaffolder/api-report.md b/plugins/scaffolder/api-report.md index 20945d08a6..9ffca426ac 100644 --- a/plugins/scaffolder/api-report.md +++ b/plugins/scaffolder/api-report.md @@ -16,6 +16,7 @@ import { ExternalRouteRef } from '@backstage/core-plugin-api'; import { FetchApi } from '@backstage/core-plugin-api'; import { FieldProps } from '@rjsf/core'; import { FieldValidation } from '@rjsf/core'; +import { IdentityApi } from '@backstage/core-plugin-api'; import { JsonObject } from '@backstage/types'; import { JSONSchema7 } from 'json-schema'; import { JsonValue } from '@backstage/types'; @@ -25,6 +26,7 @@ import { default as React_2 } from 'react'; import { RouteRef } from '@backstage/core-plugin-api'; import { ScmIntegrationRegistry } from '@backstage/integration'; import { TaskSpec } from '@backstage/plugin-scaffolder-common'; +import { TaskStep } from '@backstage/plugin-scaffolder-common'; import { TemplateEntityV1beta3 } from '@backstage/plugin-scaffolder-common'; // @public @@ -194,6 +196,7 @@ export interface RepoUrlPickerUiOptions { requestUserCredentials?: { secretsKey: string; additionalScopes?: { + gerrit?: string[]; github?: string[]; gitlab?: string[]; bitbucket?: string[]; @@ -225,6 +228,8 @@ export type RouterProps = { // @public export interface ScaffolderApi { + // (undocumented) + dryRun?(options: ScaffolderDryRunOptions): Promise; // (undocumented) getIntegrationsList( options: ScaffolderGetIntegrationsListOptions, @@ -236,6 +241,14 @@ export interface ScaffolderApi { templateRef: string, ): Promise; listActions(): Promise; + // (undocumented) + listTasks?({ + filterByOwnership, + }: { + filterByOwnership: 'owned' | 'all'; + }): Promise<{ + tasks: ScaffolderTask[]; + }>; scaffold( options: ScaffolderScaffoldOptions, ): Promise; @@ -251,10 +264,13 @@ export class ScaffolderClient implements ScaffolderApi { constructor(options: { discoveryApi: DiscoveryApi; fetchApi: FetchApi; + identityApi?: IdentityApi; scmIntegrationsApi: ScmIntegrationRegistry; useLongPollingLogs?: boolean; }); // (undocumented) + dryRun(options: ScaffolderDryRunOptions): Promise; + // (undocumented) getIntegrationsList( options: ScaffolderGetIntegrationsListOptions, ): Promise; @@ -266,6 +282,10 @@ export class ScaffolderClient implements ScaffolderApi { ): Promise; // (undocumented) listActions(): Promise; + // (undocumented) + listTasks(options: { filterByOwnership: 'owned' | 'all' }): Promise<{ + tasks: ScaffolderTask[]; + }>; scaffold( options: ScaffolderScaffoldOptions, ): Promise; @@ -273,6 +293,37 @@ export class ScaffolderClient implements ScaffolderApi { streamLogs(options: ScaffolderStreamLogsOptions): Observable; } +// @public (undocumented) +export interface ScaffolderDryRunOptions { + // (undocumented) + directoryContents: { + path: string; + base64Content: string; + }[]; + // (undocumented) + secrets?: Record; + // (undocumented) + template: JsonValue; + // (undocumented) + values: JsonObject; +} + +// @public (undocumented) +export interface ScaffolderDryRunResponse { + // (undocumented) + directoryContents: Array<{ + path: string; + base64Content: string; + executable: boolean; + }>; + // (undocumented) + log: Array>; + // (undocumented) + output: ScaffolderTaskOutput; + // (undocumented) + steps: TaskStep[]; +} + // @public export const ScaffolderFieldExtensions: React_2.ComponentType; diff --git a/plugins/scaffolder/dev/index.tsx b/plugins/scaffolder/dev/index.tsx index 03632f434d..83496c719d 100644 --- a/plugins/scaffolder/dev/index.tsx +++ b/plugins/scaffolder/dev/index.tsx @@ -25,7 +25,11 @@ import { import React from 'react'; import { scaffolderApiRef, ScaffolderClient } from '../src'; import { ScaffolderPage } from '../src/plugin'; -import { discoveryApiRef, fetchApiRef } from '@backstage/core-plugin-api'; +import { + discoveryApiRef, + fetchApiRef, + identityApiRef, +} from '@backstage/core-plugin-api'; import { CatalogEntityPage } from '@backstage/plugin-catalog'; createDevApp() @@ -49,9 +53,15 @@ createDevApp() discoveryApi: discoveryApiRef, fetchApi: fetchApiRef, scmIntegrationsApi: scmIntegrationsApiRef, + identityApi: identityApiRef, }, - factory: ({ discoveryApi, fetchApi, scmIntegrationsApi }) => - new ScaffolderClient({ discoveryApi, fetchApi, scmIntegrationsApi }), + factory: ({ discoveryApi, fetchApi, scmIntegrationsApi, identityApi }) => + new ScaffolderClient({ + discoveryApi, + fetchApi, + scmIntegrationsApi, + identityApi, + }), }) .addPage({ path: '/create', diff --git a/plugins/scaffolder/package.json b/plugins/scaffolder/package.json index 67548584ad..fd8161646c 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.2.0", + "version": "1.3.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -35,26 +35,27 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-client": "^1.0.2", - "@backstage/catalog-model": "^1.0.2", + "@backstage/catalog-client": "^1.0.3", + "@backstage/catalog-model": "^1.0.3", "@backstage/config": "^1.0.1", - "@backstage/core-components": "^0.9.4", - "@backstage/core-plugin-api": "^1.0.2", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", "@backstage/errors": "^1.0.0", - "@backstage/integration": "^1.2.0", - "@backstage/integration-react": "^1.1.0", - "@backstage/plugin-catalog-common": "^1.0.2", - "@backstage/plugin-catalog-react": "^1.1.0", - "@backstage/plugin-permission-react": "^0.4.1", - "@backstage/plugin-scaffolder-common": "^1.1.0", + "@backstage/integration": "^1.2.1", + "@backstage/integration-react": "^1.1.1", + "@backstage/plugin-catalog-common": "^1.0.3", + "@backstage/plugin-catalog-react": "^1.1.1", + "@backstage/plugin-permission-react": "^0.4.2", + "@backstage/plugin-scaffolder-common": "^1.1.1", "@backstage/theme": "^0.2.15", "@backstage/types": "^1.0.0", - "@codemirror/legacy-modes": "^0.20.0", "@codemirror/language": "^0.20.0", + "@codemirror/legacy-modes": "^0.20.0", "@codemirror/view": "^0.20.2", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", + "@react-hookz/web": "^13.0.0", "@rjsf/core": "^3.2.1", "@rjsf/material-ui": "^3.2.1", "@types/json-schema": "^7.0.9", @@ -79,11 +80,11 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/core-app-api": "^1.0.2", - "@backstage/dev-utils": "^1.0.2", - "@backstage/plugin-catalog": "^1.2.0", - "@backstage/test-utils": "^1.1.0", + "@backstage/cli": "^0.17.2", + "@backstage/core-app-api": "^1.0.3", + "@backstage/dev-utils": "^1.0.3", + "@backstage/plugin-catalog": "^1.3.0", + "@backstage/test-utils": "^1.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/react-hooks": "^8.0.0", @@ -93,7 +94,7 @@ "@types/node": "^16.11.26", "cross-fetch": "^3.1.5", "event-source-polyfill": "1.0.25", - "msw": "^0.35.0" + "msw": "^0.42.0" }, "files": [ "dist", diff --git a/plugins/scaffolder/src/api.test.ts b/plugins/scaffolder/src/api.test.ts index 4e47c7f239..583258c069 100644 --- a/plugins/scaffolder/src/api.test.ts +++ b/plugins/scaffolder/src/api.test.ts @@ -33,6 +33,13 @@ describe('api', () => { const discoveryApi = { getBaseUrl: async () => mockBaseUrl }; const fetchApi = new MockFetchApi(); + const identityApi = { + getBackstageIdentity: jest.fn(), + getProfileInfo: jest.fn(), + getCredentials: jest.fn(), + signOut: jest.fn(), + }; + const scmIntegrationsApi = ScmIntegrations.fromConfig( new ConfigReader({ integrations: { @@ -51,7 +58,11 @@ describe('api', () => { scmIntegrationsApi, discoveryApi, fetchApi, + identityApi, }); + + jest.restoreAllMocks(); + identityApi.getBackstageIdentity.mockReturnValue({}); }); it('should return default and custom integrations', async () => { @@ -129,6 +140,7 @@ describe('api', () => { scmIntegrationsApi, discoveryApi, fetchApi, + identityApi, useLongPollingLogs: true, }); }); @@ -305,4 +317,78 @@ describe('api', () => { }); }); }); + + describe('listTasks', () => { + it('should list all tasks', async () => { + server.use( + rest.get(`${mockBaseUrl}/v2/tasks`, (req, res, ctx) => { + const createdBy = req.url.searchParams.get('createdBy'); + + if (createdBy) { + return res( + ctx.json([ + { + createdBy, + }, + ]), + ); + } + + return res( + ctx.json([ + { + createdBy: null, + }, + { + createdBy: null, + }, + ]), + ); + }), + ); + + const result = await apiClient.listTasks({ filterByOwnership: 'all' }); + expect(result).toHaveLength(2); + }); + it('should list task using the current user as owner', async () => { + server.use( + rest.get(`${mockBaseUrl}/v2/tasks`, (req, res, ctx) => { + const createdBy = req.url.searchParams.get('createdBy'); + + if (createdBy) { + return res( + ctx.json({ + tasks: [ + { + createdBy, + }, + ], + }), + ); + } + + return res( + ctx.json({ + tasks: [ + { + createdBy: null, + }, + { + createdBy: null, + }, + ], + }), + ); + }), + ); + + identityApi.getBackstageIdentity.mockResolvedValueOnce({ + userEntityRef: 'user:default/foo', + }); + + const result = await apiClient.listTasks({ filterByOwnership: 'owned' }); + expect(identityApi.getBackstageIdentity).toBeCalled(); + expect(result.tasks).toHaveLength(1); + }); + }); }); diff --git a/plugins/scaffolder/src/api.ts b/plugins/scaffolder/src/api.ts index 6fc456e1fe..64141dd83d 100644 --- a/plugins/scaffolder/src/api.ts +++ b/plugins/scaffolder/src/api.ts @@ -19,6 +19,7 @@ import { createApiRef, DiscoveryApi, FetchApi, + IdentityApi, } from '@backstage/core-plugin-api'; import { ResponseError } from '@backstage/errors'; import { ScmIntegrationRegistry } from '@backstage/integration'; @@ -36,7 +37,10 @@ import { ScaffolderGetIntegrationsListOptions, ScaffolderGetIntegrationsListResponse, ScaffolderTask, + ScaffolderDryRunOptions, + ScaffolderDryRunResponse, } from './types'; +import queryString from 'qs'; /** * Utility API reference for the {@link ScaffolderApi}. @@ -56,11 +60,13 @@ export class ScaffolderClient implements ScaffolderApi { private readonly discoveryApi: DiscoveryApi; private readonly scmIntegrationsApi: ScmIntegrationRegistry; private readonly fetchApi: FetchApi; + private readonly identityApi?: IdentityApi; private readonly useLongPollingLogs: boolean; constructor(options: { discoveryApi: DiscoveryApi; fetchApi: FetchApi; + identityApi?: IdentityApi; scmIntegrationsApi: ScmIntegrationRegistry; useLongPollingLogs?: boolean; }) { @@ -68,6 +74,30 @@ export class ScaffolderClient implements ScaffolderApi { this.fetchApi = options.fetchApi ?? { fetch }; this.scmIntegrationsApi = options.scmIntegrationsApi; this.useLongPollingLogs = options.useLongPollingLogs ?? false; + this.identityApi = options.identityApi; + } + + async listTasks(options: { + filterByOwnership: 'owned' | 'all'; + }): Promise<{ tasks: ScaffolderTask[] }> { + if (!this.identityApi) { + throw new Error( + 'IdentityApi is not available in the ScaffolderClient, please pass through the IdentityApi to the ScaffolderClient constructor in order to use the listTasks method', + ); + } + const baseUrl = await this.discoveryApi.getBaseUrl('scaffolder'); + const { userEntityRef } = await this.identityApi.getBackstageIdentity(); + + const query = queryString.stringify( + options.filterByOwnership === 'owned' ? { createdBy: userEntityRef } : {}, + ); + + const response = await this.fetchApi.fetch(`${baseUrl}/v2/tasks?${query}`); + if (!response.ok) { + throw await ResponseError.fromResponse(response); + } + + return await response.json(); } async getIntegrationsList( @@ -84,6 +114,7 @@ export class ScaffolderClient implements ScaffolderApi { ), ...this.scmIntegrationsApi.bitbucketCloud.list(), ...this.scmIntegrationsApi.bitbucketServer.list(), + ...this.scmIntegrationsApi.gerrit.list(), ...this.scmIntegrationsApi.github.list(), ...this.scmIntegrationsApi.gitlab.list(), ] @@ -171,6 +202,30 @@ export class ScaffolderClient implements ScaffolderApi { return this.streamLogsEventStream(options); } + async dryRun( + options: ScaffolderDryRunOptions, + ): Promise { + const baseUrl = await this.discoveryApi.getBaseUrl('scaffolder'); + const response = await this.fetchApi.fetch(`${baseUrl}/v2/dry-run`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + template: options.template, + values: options.values, + secrets: options.secrets, + directoryContents: options.directoryContents, + }), + }); + + if (!response.ok) { + throw await ResponseError.fromResponse(response); + } + + return response.json(); + } + private streamLogsEventStream({ taskId, after, diff --git a/plugins/scaffolder/src/components/ActionsPage/ActionsPage.test.tsx b/plugins/scaffolder/src/components/ActionsPage/ActionsPage.test.tsx index aff6681037..40683f01ba 100644 --- a/plugins/scaffolder/src/components/ActionsPage/ActionsPage.test.tsx +++ b/plugins/scaffolder/src/components/ActionsPage/ActionsPage.test.tsx @@ -28,6 +28,7 @@ const scaffolderApiMock: jest.Mocked = { getTask: jest.fn(), streamLogs: jest.fn(), listActions: jest.fn(), + listTasks: jest.fn(), }; const apis = TestApiRegistry.from([scaffolderApiRef, scaffolderApiMock]); diff --git a/plugins/scaffolder/src/components/FileBrowser/FileBrowser.test.tsx b/plugins/scaffolder/src/components/FileBrowser/FileBrowser.test.tsx new file mode 100644 index 0000000000..67d0034ba3 --- /dev/null +++ b/plugins/scaffolder/src/components/FileBrowser/FileBrowser.test.tsx @@ -0,0 +1,95 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { FileEntry, parseFileEntires } from './FileBrowser'; + +function dir(path: string, ...children: FileEntry[]): FileEntry { + return { + type: 'directory', + path: path, + name: path.split('/').pop()!, + children: children, + }; +} + +function file(path: string): FileEntry { + return { + type: 'file', + path: path, + name: path.split('/').pop()!, + }; +} + +describe('parseFileEntires', () => { + it('parses an empty list', () => { + expect(parseFileEntires([])).toEqual([]); + }); + + it('parses a single file', () => { + expect(parseFileEntires(['a.txt'])).toEqual([file('a.txt')]); + expect(parseFileEntires(['a/b.txt'])).toEqual([dir('a', file('a/b.txt'))]); + expect(parseFileEntires(['a/b/c.txt'])).toEqual([ + dir('a', dir('a/b', file('a/b/c.txt'))), + ]); + }); + + it('parses multiple files', () => { + expect(parseFileEntires(['a.txt', 'b.txt'])).toEqual([ + file('a.txt'), + file('b.txt'), + ]); + expect(parseFileEntires(['a.txt', 'a/b.txt'])).toEqual([ + dir('a', file('a/b.txt')), + file('a.txt'), + ]); + expect(parseFileEntires(['a.txt', 'a/b.txt', 'a/c.txt'])).toEqual([ + dir('a', file('a/b.txt'), file('a/c.txt')), + file('a.txt'), + ]); + expect(parseFileEntires(['a.txt', 'a/b/c.txt', 'a/b/d.txt'])).toEqual([ + dir('a', dir('a/b', file('a/b/c.txt'), file('a/b/d.txt'))), + file('a.txt'), + ]); + }); + + it('throws an error on invalid filenames', () => { + expect(() => parseFileEntires([''])).toThrow(`Invalid path part: ''`); + expect(() => parseFileEntires(['/'])).toThrow(`Invalid path part: ''`); + expect(() => parseFileEntires(['a/'])).toThrow(`Invalid path part: ''`); + expect(() => parseFileEntires(['/a.txt'])).toThrow(`Invalid path part: ''`); + expect(() => parseFileEntires(['a//a.txt'])).toThrow( + `Invalid path part: ''`, + ); + }); + + it('throws an error on conflicting directory and filenames', () => { + expect(() => parseFileEntires(['a', 'a'])).toThrow( + `Duplicate filename at 'a'`, + ); + expect(() => parseFileEntires(['a', 'a/b'])).toThrow( + `Duplicate filename at 'a'`, + ); + expect(() => parseFileEntires(['a/b', 'a'])).toThrow( + `Duplicate filename at 'a'`, + ); + expect(() => parseFileEntires(['a/b', 'a/b/c'])).toThrow( + `Duplicate filename at 'a/b'`, + ); + expect(() => parseFileEntires(['a/b/c', 'a/b/c'])).toThrow( + `Duplicate filename at 'a/b/c'`, + ); + }); +}); diff --git a/plugins/scaffolder/src/components/FileBrowser/FileBrowser.tsx b/plugins/scaffolder/src/components/FileBrowser/FileBrowser.tsx new file mode 100644 index 0000000000..1273b8ad09 --- /dev/null +++ b/plugins/scaffolder/src/components/FileBrowser/FileBrowser.tsx @@ -0,0 +1,145 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React, { useMemo } from 'react'; +import { makeStyles } from '@material-ui/core/styles'; +import TreeView from '@material-ui/lab/TreeView'; +import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; +import ChevronRightIcon from '@material-ui/icons/ChevronRight'; +import TreeItem from '@material-ui/lab/TreeItem'; + +const useStyles = makeStyles({ + root: { + whiteSpace: 'nowrap', + overflowY: 'auto', + }, +}); + +export type FileEntry = + | { + type: 'file'; + name: string; + path: string; + } + | { + type: 'directory'; + name: string; + path: string; + children: FileEntry[]; + }; + +export function parseFileEntires(paths: string[]): FileEntry[] { + const root: FileEntry = { + type: 'directory', + name: '', + path: '', + children: [], + }; + + for (const path of paths.slice().sort()) { + const parts = path.split('/'); + + let current = root; + for (let i = 0; i < parts.length; i++) { + const part = parts[i]; + if (part === '') { + throw new Error(`Invalid path part: ''`); + } + + const entryPath = parts.slice(0, i + 1).join('/'); + + const existing = current.children.find(child => child.name === part); + if (existing?.type === 'file') { + throw new Error(`Duplicate filename at '${entryPath}'`); + } else if (existing) { + current = existing; + } else { + if (i < parts.length - 1) { + const newEntry: FileEntry = { + type: 'directory', + name: part, + path: entryPath, + children: [], + }; + const firstFileIndex = current.children.findIndex( + child => child.type === 'file', + ); + current.children.splice(firstFileIndex, 0, newEntry); + current = newEntry; + } else { + current.children.push({ + type: 'file', + name: part, + path: entryPath, + }); + } + } + } + } + + return root.children; +} + +function FileTreeItem({ entry }: { entry: FileEntry }) { + if (entry.type === 'file') { + return ; + } + + return ( + + {entry.children.map(child => ( + + ))} + + ); +} + +interface FileBrowserProps { + /** A list of all filepaths to show, directories are separated with a `/` */ + filePaths: string[]; + /** The currently selected file */ + selected?: string; + /** Callback for when a file is selected */ + onSelect?(filePath: string): void; +} + +/** A simple file browser that allows you to select individual files */ +export function FileBrowser(props: FileBrowserProps) { + const classes = useStyles(); + + const fileTree = useMemo( + () => parseFileEntires(props.filePaths), + [props.filePaths], + ); + + return ( + } + defaultExpandIcon={} + onNodeSelect={(_e: unknown, nodeId: string) => { + if (props.onSelect && props.filePaths.includes(nodeId)) { + props.onSelect(nodeId); + } + }} + > + {fileTree.map(entry => ( + + ))} + + ); +} diff --git a/packages/search-common/src/index.ts b/plugins/scaffolder/src/components/FileBrowser/index.ts similarity index 86% rename from packages/search-common/src/index.ts rename to plugins/scaffolder/src/components/FileBrowser/index.ts index 8e21c5f936..5d18109a9c 100644 --- a/packages/search-common/src/index.ts +++ b/plugins/scaffolder/src/components/FileBrowser/index.ts @@ -1,5 +1,5 @@ /* - * Copyright 2021 The Backstage Authors + * Copyright 2022 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,4 +14,4 @@ * limitations under the License. */ -export * from '@backstage/plugin-search-common'; +export { FileBrowser } from './FileBrowser'; diff --git a/plugins/scaffolder/src/components/ListTasksPage/ListTaskPage.test.tsx b/plugins/scaffolder/src/components/ListTasksPage/ListTaskPage.test.tsx new file mode 100644 index 0000000000..6dc26abd68 --- /dev/null +++ b/plugins/scaffolder/src/components/ListTasksPage/ListTaskPage.test.tsx @@ -0,0 +1,249 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Entity } from '@backstage/catalog-model'; +import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; +import { + CatalogApi, + catalogApiRef, + entityRouteRef, +} from '@backstage/plugin-catalog-react'; +import React from 'react'; +import { identityApiRef } from '@backstage/core-plugin-api'; +import { ListTasksPage } from './ListTasksPage'; +import { ScaffolderApi } from '../../types'; +import { scaffolderApiRef } from '../../api'; +import { rootRouteRef } from '../../routes'; +import { act, fireEvent } from '@testing-library/react'; + +describe('', () => { + const catalogApi: jest.Mocked = { + getEntityByRef: jest.fn(), + } as any; + + const identityApi = { + getBackstageIdentity: jest.fn(), + getProfileInfo: jest.fn(), + getCredentials: jest.fn(), + signOut: jest.fn(), + }; + + const scaffolderApiMock: jest.Mocked> = { + scaffold: jest.fn(), + getTemplateParameterSchema: jest.fn(), + listTasks: jest.fn(), + } as any; + + it('should render the page', async () => { + const entity: Entity = { + apiVersion: 'v1', + kind: 'service', + metadata: { + name: 'test', + }, + spec: { + profile: { + displayName: 'BackUser', + }, + }, + }; + catalogApi.getEntityByRef.mockResolvedValue(entity); + + scaffolderApiMock.listTasks.mockResolvedValue({ tasks: [] }); + + const { getByText } = await renderInTestApp( + + + , + { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': entityRouteRef, + '/root': rootRouteRef, + }, + }, + ); + + expect(getByText('List template tasks')).toBeInTheDocument(); + expect(getByText('All tasks that have been started')).toBeInTheDocument(); + expect(getByText('Tasks')).toBeInTheDocument(); + }); + + it('should render the task I am owner', async () => { + const entity: Entity = { + apiVersion: 'v1', + kind: 'User', + metadata: { + name: 'foo', + }, + spec: { + profile: { + displayName: 'BackUser', + }, + }, + }; + catalogApi.getEntityByRef.mockResolvedValue(entity); + scaffolderApiMock.listTasks.mockResolvedValue({ + tasks: [ + { + id: 'a-random-id', + spec: { + user: { ref: 'user:default/foo' }, + templateInfo: { + entityRef: 'template:default/test', + }, + } as any, + status: 'completed', + createdAt: '', + lastHeartbeatAt: '', + }, + ], + }); + + scaffolderApiMock.getTemplateParameterSchema.mockResolvedValue({ + title: 'One Template', + steps: [], + }); + + const { getByText, findByText } = await renderInTestApp( + + + , + { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': entityRouteRef, + '/root': rootRouteRef, + }, + }, + ); + + expect(scaffolderApiMock.listTasks).toBeCalledWith({ + filterByOwnership: 'owned', + }); + expect(getByText('List template tasks')).toBeInTheDocument(); + expect(getByText('All tasks that have been started')).toBeInTheDocument(); + expect(getByText('Tasks')).toBeInTheDocument(); + expect(await findByText('One Template')).toBeInTheDocument(); + expect(await findByText('BackUser')).toBeInTheDocument(); + }); + + it('should render all tasks', async () => { + const entity: Entity = { + apiVersion: 'v1', + kind: 'User', + metadata: { + name: 'foo', + }, + spec: { + profile: { + displayName: 'BackUser', + }, + }, + }; + catalogApi.getEntityByRef + .mockResolvedValue(entity) + .mockResolvedValue(entity) + .mockResolvedValue({ + ...entity, + spec: { + profile: { + displayName: 'OtherUser', + }, + }, + }); + + scaffolderApiMock.listTasks + .mockResolvedValue({ + tasks: [ + { + id: 'a-random-id', + spec: { + user: { ref: 'user:default/foo' }, + templateInfo: { + entityRef: 'template:default/mock', + }, + } as any, + status: 'completed', + createdAt: '', + lastHeartbeatAt: '', + }, + ], + }) + .mockResolvedValue({ + tasks: [ + { + id: 'b-random-id', + spec: { + templateInfo: { + entityRef: 'template:default/mock', + }, + user: { + ref: 'user:default/boo', + }, + } as any, + status: 'completed', + createdAt: '', + lastHeartbeatAt: '', + }, + ], + }); + + scaffolderApiMock.getTemplateParameterSchema.mockResolvedValue({ + title: 'One Template', + steps: [], + }); + + const { getByText, findByText } = await renderInTestApp( + + + , + { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': entityRouteRef, + '/root': rootRouteRef, + }, + }, + ); + + await act(async () => { + const allButton = await getByText('All'); + fireEvent.click(allButton); + }); + + expect(scaffolderApiMock.listTasks).toBeCalledWith({ + filterByOwnership: 'all', + }); + expect(await findByText('One Template')).toBeInTheDocument(); + expect(await findByText('OtherUser')).toBeInTheDocument(); + }); +}); diff --git a/plugins/scaffolder/src/components/ListTasksPage/ListTasksPage.tsx b/plugins/scaffolder/src/components/ListTasksPage/ListTasksPage.tsx new file mode 100644 index 0000000000..0b00879d43 --- /dev/null +++ b/plugins/scaffolder/src/components/ListTasksPage/ListTasksPage.tsx @@ -0,0 +1,152 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { + Content, + EmptyState, + ErrorPanel, + Header, + Lifecycle, + Link, + Page, + Progress, + Table, +} from '@backstage/core-components'; +import { useApi, useRouteRef } from '@backstage/core-plugin-api'; +import { CatalogFilterLayout } from '@backstage/plugin-catalog-react'; +import useAsync from 'react-use/lib/useAsync'; +import React, { useState } from 'react'; +import { scaffolderApiRef } from '../../api'; +import { rootRouteRef } from '../../routes'; +import { ScaffolderTask } from '../../types'; +import { OwnerListPicker } from './OwnerListPicker'; +import { + CreatedAtColumn, + OwnerEntityColumn, + TaskStatusColumn, + TemplateTitleColumn, +} from './columns'; + +export interface MyTaskPageProps { + initiallySelectedFilter?: 'owned' | 'all'; +} + +const ListTaskPageContent = (props: MyTaskPageProps) => { + const { initiallySelectedFilter = 'owned' } = props; + + const scaffolderApi = useApi(scaffolderApiRef); + const rootLink = useRouteRef(rootRouteRef); + + const [ownerFilter, setOwnerFilter] = useState(initiallySelectedFilter); + const { value, loading, error } = useAsync(() => { + if (scaffolderApi.listTasks) { + return scaffolderApi.listTasks?.({ filterByOwnership: ownerFilter }); + } + + // eslint-disable-next-line no-console + console.warn( + 'listTasks is not implemented in the scaffolderApi, please make sure to implement this method.', + ); + + return Promise.resolve({ tasks: [] }); + }, [scaffolderApi, ownerFilter]); + + if (loading) { + return ; + } + + if (error) { + return ( + <> + + + + ); + } + + return ( + + + setOwnerFilter(id)} + /> + + + + data={value?.tasks ?? []} + title="Tasks" + columns={[ + { + title: 'Task ID', + field: 'id', + render: row => ( + {row.id} + ), + }, + { + title: 'Template', + render: row => ( + + ), + }, + { + title: 'Created', + field: 'createdAt', + render: row => , + }, + { + title: 'Owner', + field: 'createdBy', + render: row => ( + + ), + }, + { + title: 'Status', + field: 'status', + render: row => , + }, + ]} + /> + + + ); +}; + +export const ListTasksPage = (props: MyTaskPageProps) => { + return ( + +
+ List template tasks + + } + subtitle="All tasks that have been started" + /> + + + + + ); +}; diff --git a/plugins/scaffolder/src/components/ListTasksPage/OwnerListPicker.test.tsx b/plugins/scaffolder/src/components/ListTasksPage/OwnerListPicker.test.tsx new file mode 100644 index 0000000000..bf0ae6d946 --- /dev/null +++ b/plugins/scaffolder/src/components/ListTasksPage/OwnerListPicker.test.tsx @@ -0,0 +1,47 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { renderInTestApp } from '@backstage/test-utils'; + +import React from 'react'; +import { OwnerListPicker } from './OwnerListPicker'; +import { fireEvent } from '@testing-library/react'; + +describe('', () => { + it('should render the tasks owner filter', async () => { + const props = { + filter: 'owned', + onSelectOwner: jest.fn(), + }; + + const { getByText } = await renderInTestApp(); + + expect(await getByText('Owned')).toBeDefined(); + expect(await getByText('All')).toBeDefined(); + }); + + it('should call the function on select other item', async () => { + const props = { + filter: 'owned', + onSelectOwner: jest.fn(), + }; + + const { getByText } = await renderInTestApp(); + + fireEvent.click(await getByText('All')); + expect(props.onSelectOwner).toBeCalledWith('all'); + }); +}); diff --git a/plugins/scaffolder/src/components/ListTasksPage/OwnerListPicker.tsx b/plugins/scaffolder/src/components/ListTasksPage/OwnerListPicker.tsx new file mode 100644 index 0000000000..a9e3917922 --- /dev/null +++ b/plugins/scaffolder/src/components/ListTasksPage/OwnerListPicker.tsx @@ -0,0 +1,133 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { IconComponent } from '@backstage/core-plugin-api'; +import { + Card, + List, + ListItemIcon, + ListItemText, + makeStyles, + MenuItem, + Theme, + Typography, +} from '@material-ui/core'; +import SettingsIcon from '@material-ui/icons/Settings'; +import React, { Fragment } from 'react'; + +import AllIcon from '@material-ui/icons/FontDownload'; + +const useStyles = makeStyles( + theme => ({ + root: { + backgroundColor: 'rgba(0, 0, 0, .11)', + boxShadow: 'none', + margin: theme.spacing(1, 0, 1, 0), + }, + title: { + margin: theme.spacing(1, 0, 0, 1), + textTransform: 'uppercase', + fontSize: 12, + fontWeight: 'bold', + }, + listIcon: { + minWidth: 30, + color: theme.palette.text.primary, + }, + menuItem: { + minHeight: theme.spacing(6), + }, + groupWrapper: { + margin: theme.spacing(1, 1, 2, 1), + }, + }), + { + name: 'ScaffolderReactOwnerListPicker', + }, +); + +export type ButtonGroup = { + name: string; + items: { + id: 'owned' | 'starred' | 'all'; + label: string; + icon?: IconComponent; + }[]; +}; + +function getFilterGroups(): ButtonGroup[] { + return [ + { + name: 'Task Owner', + items: [ + { + id: 'owned', + label: 'Owned', + icon: SettingsIcon, + }, + { + id: 'all', + label: 'All', + icon: AllIcon, + }, + ], + }, + ]; +} + +export const OwnerListPicker = (props: { + filter: string; + onSelectOwner: (id: 'owned' | 'all') => void; +}) => { + const { filter, onSelectOwner } = props; + const classes = useStyles(); + + const filterGroups = getFilterGroups(); + return ( + + {filterGroups.map(group => ( + + + {group.name} + + + + {group.items.map(item => ( + onSelectOwner(item.id as 'owned' | 'all')} + selected={item.id === filter} + className={classes.menuItem} + data-testid={`owner-picker-${item.id}`} + > + {item.icon && ( + + + + )} + + {item.label} + + + ))} + + + + ))} + + ); +}; diff --git a/plugins/scaffolder/src/components/ListTasksPage/columns/CreatedAtColumn.test.tsx b/plugins/scaffolder/src/components/ListTasksPage/columns/CreatedAtColumn.test.tsx new file mode 100644 index 0000000000..a30c1d370b --- /dev/null +++ b/plugins/scaffolder/src/components/ListTasksPage/columns/CreatedAtColumn.test.tsx @@ -0,0 +1,34 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { renderInTestApp } from '@backstage/test-utils'; + +import React from 'react'; +import { CreatedAtColumn } from './CreatedAtColumn'; +import { DateTime } from 'luxon'; + +describe('', () => { + it('should render the column with the time', async () => { + const props = { + createdAt: DateTime.now().toISO(), + }; + + const { getByText } = await renderInTestApp(); + + const text = getByText('0 seconds ago'); + expect(text).toBeDefined(); + }); +}); diff --git a/plugins/scaffolder/src/components/ListTasksPage/columns/CreatedAtColumn.tsx b/plugins/scaffolder/src/components/ListTasksPage/columns/CreatedAtColumn.tsx new file mode 100644 index 0000000000..8e48d4a561 --- /dev/null +++ b/plugins/scaffolder/src/components/ListTasksPage/columns/CreatedAtColumn.tsx @@ -0,0 +1,27 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { DateTime, Interval } from 'luxon'; +import humanizeDuration from 'humanize-duration'; +import React from 'react'; + +export const CreatedAtColumn = ({ createdAt }: { createdAt: string }) => { + const createdAtTime = DateTime.fromISO(createdAt); + const formatted = Interval.fromDateTimes(createdAtTime, DateTime.local()) + .toDuration() + .valueOf(); + + return

{humanizeDuration(formatted, { round: true })} ago

; +}; diff --git a/plugins/scaffolder/src/components/ListTasksPage/columns/OwnerEntityColumn.test.tsx b/plugins/scaffolder/src/components/ListTasksPage/columns/OwnerEntityColumn.test.tsx new file mode 100644 index 0000000000..84b86599d5 --- /dev/null +++ b/plugins/scaffolder/src/components/ListTasksPage/columns/OwnerEntityColumn.test.tsx @@ -0,0 +1,82 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Entity } from '@backstage/catalog-model'; +import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; +import { + CatalogApi, + catalogApiRef, + entityRouteRef, +} from '@backstage/plugin-catalog-react'; +import React from 'react'; +import { OwnerEntityColumn } from './OwnerEntityColumn'; +import { identityApiRef } from '@backstage/core-plugin-api'; + +describe('', () => { + const catalogApi: jest.Mocked = { + getEntityByRef: jest.fn(), + } as any; + + const identityApi = { + getBackstageIdentity: jest.fn(), + getProfileInfo: jest.fn(), + getCredentials: jest.fn(), + signOut: jest.fn(), + }; + + it('should render the column with the user', async () => { + const props = { + entityRef: 'user:default/foo', + }; + + const entity: Entity = { + apiVersion: 'v1', + kind: 'User', + metadata: { + name: 'test', + }, + spec: { + profile: { + displayName: 'BackUser', + }, + }, + }; + catalogApi.getEntityByRef.mockResolvedValue(entity); + + const { getByText, getByRole } = await renderInTestApp( + + + , + { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': entityRouteRef, + }, + }, + ); + + expect(getByRole('link')).toHaveAttribute( + 'href', + '/catalog/default/user/foo', + ); + const text = getByText('BackUser'); + expect(text).toBeDefined(); + }); +}); diff --git a/plugins/scaffolder/src/components/ListTasksPage/columns/OwnerEntityColumn.tsx b/plugins/scaffolder/src/components/ListTasksPage/columns/OwnerEntityColumn.tsx new file mode 100644 index 0000000000..0c83c7cbc2 --- /dev/null +++ b/plugins/scaffolder/src/components/ListTasksPage/columns/OwnerEntityColumn.tsx @@ -0,0 +1,49 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { useApi } from '@backstage/core-plugin-api'; +import React from 'react'; + +import useAsync from 'react-use/lib/useAsync'; + +import { catalogApiRef, EntityRefLink } from '@backstage/plugin-catalog-react'; +import { parseEntityRef, UserEntity } from '@backstage/catalog-model'; + +export const OwnerEntityColumn = ({ entityRef }: { entityRef?: string }) => { + const catalogApi = useApi(catalogApiRef); + + const { value, loading, error } = useAsync( + () => catalogApi.getEntityByRef(entityRef || ''), + [catalogApi, entityRef], + ); + + if (!entityRef) { + return

Unknown

; + } + + if (loading || error) { + return null; + } + + return ( + + ); +}; diff --git a/plugins/scaffolder/src/components/ListTasksPage/columns/TaskStatusColumn.test.tsx b/plugins/scaffolder/src/components/ListTasksPage/columns/TaskStatusColumn.test.tsx new file mode 100644 index 0000000000..5eabcf72ff --- /dev/null +++ b/plugins/scaffolder/src/components/ListTasksPage/columns/TaskStatusColumn.test.tsx @@ -0,0 +1,38 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { renderInTestApp } from '@backstage/test-utils'; + +import React from 'react'; +import { TaskStatusColumn } from './TaskStatusColumn'; + +describe('', () => { + it.each(['processing', 'error', 'completed'])( + 'should render the column with the status %s', + async status => { + const props = { + status: status, + }; + + const { getByText } = await renderInTestApp( + , + ); + + const text = getByText(status); + expect(text).toBeDefined(); + }, + ); +}); diff --git a/plugins/scaffolder/src/components/ListTasksPage/columns/TaskStatusColumn.tsx b/plugins/scaffolder/src/components/ListTasksPage/columns/TaskStatusColumn.tsx new file mode 100644 index 0000000000..995e505c36 --- /dev/null +++ b/plugins/scaffolder/src/components/ListTasksPage/columns/TaskStatusColumn.tsx @@ -0,0 +1,33 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { + StatusError, + StatusOK, + StatusPending, +} from '@backstage/core-components'; +import React from 'react'; + +export const TaskStatusColumn = ({ status }: { status: string }) => { + switch (status) { + case 'processing': + return {status}; + case 'completed': + return {status}; + case 'error': + default: + return {status}; + } +}; diff --git a/plugins/scaffolder/src/components/ListTasksPage/columns/TemplateTitleColumn.test.tsx b/plugins/scaffolder/src/components/ListTasksPage/columns/TemplateTitleColumn.test.tsx new file mode 100644 index 0000000000..3dc20312aa --- /dev/null +++ b/plugins/scaffolder/src/components/ListTasksPage/columns/TemplateTitleColumn.test.tsx @@ -0,0 +1,50 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; + +import React from 'react'; +import { TemplateTitleColumn } from './TemplateTitleColumn'; +import { scaffolderApiRef } from '../../../api'; +import { ScaffolderApi } from '../../../types'; +import { entityRouteRef } from '@backstage/plugin-catalog-react'; + +describe('', () => { + const scaffolderApiMock: jest.Mocked = { + scaffold: jest.fn(), + getTemplateParameterSchema: jest.fn(), + } as any; + + it('should render the column with the template name', async () => { + const props = { + entityRef: 'template:default/one-template', + }; + scaffolderApiMock.getTemplateParameterSchema.mockResolvedValue({ + title: 'One Template', + steps: [], + }); + + const { getByText } = await renderInTestApp( + + + , + { mountedRoutes: { '/test': entityRouteRef } }, + ); + + const text = getByText('One Template'); + expect(text).toBeDefined(); + }); +}); diff --git a/plugins/scaffolder/src/components/ListTasksPage/columns/TemplateTitleColumn.tsx b/plugins/scaffolder/src/components/ListTasksPage/columns/TemplateTitleColumn.tsx new file mode 100644 index 0000000000..0561b40392 --- /dev/null +++ b/plugins/scaffolder/src/components/ListTasksPage/columns/TemplateTitleColumn.tsx @@ -0,0 +1,37 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { useApi } from '@backstage/core-plugin-api'; +import React from 'react'; +import { scaffolderApiRef } from '../../../api'; +import useAsync from 'react-use/lib/useAsync'; +import { parseEntityRef } from '@backstage/catalog-model'; +import { EntityRefLink } from '@backstage/plugin-catalog-react'; + +export const TemplateTitleColumn = ({ entityRef }: { entityRef?: string }) => { + const scaffolder = useApi(scaffolderApiRef); + const { value, loading, error } = useAsync( + () => scaffolder.getTemplateParameterSchema(entityRef || ''), + [scaffolder, entityRef], + ); + + if (loading || error || !entityRef) { + return null; + } + + return ( + + ); +}; diff --git a/plugins/scaffolder/src/components/ListTasksPage/columns/index.ts b/plugins/scaffolder/src/components/ListTasksPage/columns/index.ts new file mode 100644 index 0000000000..f6f356a3da --- /dev/null +++ b/plugins/scaffolder/src/components/ListTasksPage/columns/index.ts @@ -0,0 +1,19 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export { CreatedAtColumn } from './CreatedAtColumn'; +export { OwnerEntityColumn } from './OwnerEntityColumn'; +export { TaskStatusColumn } from './TaskStatusColumn'; +export { TemplateTitleColumn } from './TemplateTitleColumn'; diff --git a/plugins/scaffolder/src/components/ListTasksPage/index.tsx b/plugins/scaffolder/src/components/ListTasksPage/index.tsx new file mode 100644 index 0000000000..56fc7d3d28 --- /dev/null +++ b/plugins/scaffolder/src/components/ListTasksPage/index.tsx @@ -0,0 +1,16 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export { ListTasksPage } from './ListTasksPage'; diff --git a/plugins/scaffolder/src/components/MultistepJsonForm/MultistepJsonForm.test.tsx b/plugins/scaffolder/src/components/MultistepJsonForm/MultistepJsonForm.test.tsx index 5f7091832f..21540a4895 100644 --- a/plugins/scaffolder/src/components/MultistepJsonForm/MultistepJsonForm.test.tsx +++ b/plugins/scaffolder/src/components/MultistepJsonForm/MultistepJsonForm.test.tsx @@ -41,7 +41,6 @@ describe('MultistepJsonForm', () => { type: 'string', 'ui:backstage': { review: { - show: true, mask: '******', }, }, diff --git a/plugins/scaffolder/src/components/MultistepJsonForm/MultistepJsonForm.tsx b/plugins/scaffolder/src/components/MultistepJsonForm/MultistepJsonForm.tsx index fb1de5f46d..43e12ae85f 100644 --- a/plugins/scaffolder/src/components/MultistepJsonForm/MultistepJsonForm.tsx +++ b/plugins/scaffolder/src/components/MultistepJsonForm/MultistepJsonForm.tsx @@ -54,6 +54,7 @@ type Props = { onFinish?: () => Promise; widgets?: FormProps['widgets']; fields?: FormProps['fields']; + finishButtonLabel?: string; }; export function getUiSchemasFromSteps(steps: Step[]): UiSchema[] { @@ -94,12 +95,12 @@ export function getReviewData(formData: Record, steps: Step[]) { } const review = uiSchema['ui:backstage'].review as JsonObject; - if (!review.show) { + if (review.mask) { + reviewData[key] = review.mask; continue; } - if (review.mask) { - reviewData[key] = review.mask; + if (!review.show) { continue; } reviewData[key] = formData[key]; @@ -110,7 +111,15 @@ export function getReviewData(formData: Record, steps: Step[]) { } export const MultistepJsonForm = (props: Props) => { - const { formData, onChange, onReset, onFinish, fields, widgets } = props; + const { + formData, + onChange, + onReset, + onFinish, + fields, + widgets, + finishButtonLabel, + } = props; const [activeStep, setActiveStep] = useState(0); const [disableButtons, setDisableButtons] = useState(false); const errorApi = useApi(errorApiRef); @@ -171,8 +180,9 @@ export const MultistepJsonForm = (props: Props) => { try { await onFinish(); } catch (err) { - setDisableButtons(false); errorApi.post(err); + } finally { + setDisableButtons(false); } }; @@ -239,7 +249,7 @@ export const MultistepJsonForm = (props: Props) => { onClick={handleCreate} disabled={!onFinish || disableButtons} > - Create + {finishButtonLabel ?? 'Create'} diff --git a/plugins/scaffolder/src/components/Router.tsx b/plugins/scaffolder/src/components/Router.tsx index 774bc61638..ac35c4dcd7 100644 --- a/plugins/scaffolder/src/components/Router.tsx +++ b/plugins/scaffolder/src/components/Router.tsx @@ -14,8 +14,8 @@ * limitations under the License. */ -import React, { ComponentType } from 'react'; -import { Routes, Route, useOutlet, Navigate } from 'react-router'; +import React, { ComponentType, useEffect } from 'react'; +import { Routes, Route, useOutlet, Navigate, useParams } from 'react-router'; import { Entity } from '@backstage/catalog-model'; import { TemplateEntityV1beta3 } from '@backstage/plugin-scaffolder-common'; import { ScaffolderPage } from './ScaffolderPage'; @@ -31,13 +31,16 @@ import { FIELD_EXTENSION_KEY, DEFAULT_SCAFFOLDER_FIELD_EXTENSIONS, } from '../extensions'; -import { useElementFilter } from '@backstage/core-plugin-api'; +import { useElementFilter, useRouteRef } from '@backstage/core-plugin-api'; import { actionsRouteRef, editRouteRef, + legacySelectedTemplateRouteRef, + scaffolderListTaskRouteRef, scaffolderTaskRouteRef, selectedTemplateRouteRef, } from '../routes'; +import { ListTasksPage } from './ListTasksPage'; /** * The props for the entrypoint `ScaffolderPage` component the plugin. @@ -98,6 +101,22 @@ export const Router = (props: RouterProps) => { ), ), ]; + /** + * This component can be deleted once the older routes have been deprecated. + */ + const RedirectingComponent = () => { + const { templateName } = useParams(); + const newLink = useRouteRef(selectedTemplateRouteRef); + useEffect( + () => + // eslint-disable-next-line no-console + console.warn( + 'The route /template/:templateName is deprecated, please use the new /template/:namespace/:templateName route instead', + ), + [], + ); + return ; + }; return ( @@ -110,6 +129,9 @@ export const Router = (props: RouterProps) => { /> } /> + + + { } /> + } + /> } /> } /> (); - const pageLink = useRouteRef(rootRouteRef); + const editLink = useRouteRef(editRouteRef); + const actionsLink = useRouteRef(actionsRouteRef); + const tasksLink = useRouteRef(scaffolderListTaskRouteRef); + const navigate = useNavigate(); const showEditor = props.editor !== false; const showActions = props.actions !== false; + const showTasks = props.tasks !== false; if (!showEditor && !showActions) { return null; @@ -85,7 +95,7 @@ export function ScaffolderPageContextMenu( > {showEditor && ( - navigate(`${pageLink()}/edit`)}> + navigate(editLink())}> @@ -93,13 +103,21 @@ export function ScaffolderPageContextMenu( )} {showActions && ( - navigate(`${pageLink()}/actions`)}> + navigate(actionsLink())}> )} + {showTasks && ( + navigate(tasksLink())}> + + + + + + )} diff --git a/plugins/scaffolder/src/components/TaskPage/TaskPage.tsx b/plugins/scaffolder/src/components/TaskPage/TaskPage.tsx index 27d89cb6a4..7073157f25 100644 --- a/plugins/scaffolder/src/components/TaskPage/TaskPage.tsx +++ b/plugins/scaffolder/src/components/TaskPage/TaskPage.tsx @@ -161,16 +161,16 @@ function TaskStepIconComponent(props: StepIconProps) { } export const TaskStatusStepper = memo( - ({ - steps, - currentStepId, - onUserStepChange, - }: { + (props: { steps: TaskStep[]; currentStepId: string | undefined; onUserStepChange: (id: string) => void; + classes?: { + root?: string; + }; }) => { - const classes = useStyles(); + const { steps, currentStepId, onUserStepChange } = props; + const classes = useStyles(props); return (
@@ -299,12 +299,12 @@ export const TaskPage = ({ loadingText }: TaskPageProps) => { const formData = taskStream.task!.spec.parameters; - const { name } = parseEntityRef( + const { name, namespace } = parseEntityRef( taskStream.task!.spec.templateInfo?.entityRef, ); navigate( - `${templateRoute({ templateName: name })}?${qs.stringify({ + `${templateRoute({ templateName: name, namespace })}?${qs.stringify({ formData: JSON.stringify(formData), })}`, ); diff --git a/plugins/scaffolder/src/components/TaskPage/TaskPageLinks.tsx b/plugins/scaffolder/src/components/TaskPage/TaskPageLinks.tsx index cb77689b6e..78d6a452ea 100644 --- a/plugins/scaffolder/src/components/TaskPage/TaskPageLinks.tsx +++ b/plugins/scaffolder/src/components/TaskPage/TaskPageLinks.tsx @@ -41,7 +41,10 @@ export const TaskPageLinks = ({ output }: TaskPageLinksProps) => { .filter(({ url, entityRef }) => url || entityRef) .map(({ url, entityRef, title, icon }) => { if (entityRef) { - const entityName = parseEntityRef(entityRef); + const entityName = parseEntityRef(entityRef, { + defaultKind: '', + defaultNamespace: '', + }); const target = entityRoute(entityName); return { title, icon, url: target }; } diff --git a/plugins/scaffolder/src/components/TemplateCard/TemplateCard.tsx b/plugins/scaffolder/src/components/TemplateCard/TemplateCard.tsx index 6f7e375cce..df46a119f2 100644 --- a/plugins/scaffolder/src/components/TemplateCard/TemplateCard.tsx +++ b/plugins/scaffolder/src/components/TemplateCard/TemplateCard.tsx @@ -13,7 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Entity, RELATION_OWNED_BY } from '@backstage/catalog-model'; +import { + Entity, + parseEntityRef, + RELATION_OWNED_BY, + stringifyEntityRef, +} from '@backstage/catalog-model'; import { TemplateEntityV1beta3 } from '@backstage/plugin-scaffolder-common'; import { ScmIntegrationIcon, @@ -162,7 +167,8 @@ export const TemplateCard = ({ template, deprecated }: TemplateCardProps) => { : 'other'; const theme = backstageTheme.getPageTheme({ themeId }); const classes = useStyles({ backgroundImage: theme.backgroundImage }); - const href = templateRoute({ templateName: templateProps.name }); + const { name, namespace } = parseEntityRef(stringifyEntityRef(template)); + const href = templateRoute({ templateName: name, namespace }); const scmIntegrationsApi = useApi(scmIntegrationsApiRef); const sourceLocation = getEntitySourceLocation(template, scmIntegrationsApi); diff --git a/plugins/scaffolder/src/components/TemplateEditorPage/DirectoryEditorContext.tsx b/plugins/scaffolder/src/components/TemplateEditorPage/DirectoryEditorContext.tsx new file mode 100644 index 0000000000..0edf390bfe --- /dev/null +++ b/plugins/scaffolder/src/components/TemplateEditorPage/DirectoryEditorContext.tsx @@ -0,0 +1,243 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ErrorPanel, Progress } from '@backstage/core-components'; +import { useAsync, useRerender } from '@react-hookz/web'; +import React, { createContext, ReactNode, useContext, useEffect } from 'react'; +import { + TemplateDirectoryAccess, + TemplateFileAccess, +} from '../../lib/filesystem'; + +const MAX_SIZE = 1024 * 1024; +const MAX_SIZE_MESSAGE = 'This file is too large to be displayed'; + +interface DirectoryEditorFile { + /** The path of the file relative to the root directory */ + path: string; + /** The staged content of the file */ + content: string; + /** Whether the staged content matches what is on disk */ + dirty: boolean; + + /** Update the staged content of the file without saving */ + updateContent(content: string): void; + /** Save the staged content of the file to disk */ + save(): Promise; + /** Reload the staged content of the file from disk */ + reload(): Promise; +} + +interface DirectoryEditor { + /** A list of all files in the edited directory */ + files: Array; + + /** The currently selected file */ + selectedFile: DirectoryEditorFile | undefined; + /** Switch the selected file */ + setSelectedFile(path: string | undefined): void; + + /** Save all files to disk */ + save(): Promise; + /** Reload all files from disk */ + reload(): Promise; + + subscribe(listener: () => void): () => void; +} + +class DirectoryEditorFileManager implements DirectoryEditorFile { + readonly #access: TemplateFileAccess; + readonly #signalUpdate: () => void; + + #content?: string; + #savedContent?: string; + + constructor(access: TemplateFileAccess, signalUpdate: () => void) { + this.#access = access; + this.#signalUpdate = signalUpdate; + } + + get path() { + return this.#access.path; + } + + get content() { + return this.#content ?? MAX_SIZE_MESSAGE; + } + + updateContent(content: string): void { + if (this.#content === undefined) { + return; + } + this.#content = content; + this.#signalUpdate(); + } + + get dirty() { + return this.#content !== this.#savedContent; + } + + async save(): Promise { + if (this.#content !== undefined) { + await this.#access.save(this.#content); + this.#savedContent = this.#content; + this.#signalUpdate(); + } + } + + async reload(): Promise { + const file = await this.#access.file(); + if (file.size > MAX_SIZE) { + if (this.#content !== undefined) { + this.#content = undefined; + this.#savedContent = undefined; + this.#signalUpdate(); + } + return; + } + + const content = await file.text(); + if (this.#content !== content) { + this.#content = content; + this.#savedContent = content; + this.#signalUpdate(); + } + } +} + +class DirectoryEditorManager implements DirectoryEditor { + readonly #access: TemplateDirectoryAccess; + readonly #listeners = new Set<() => void>(); + + #files: DirectoryEditorFile[] = []; + #selectedFile: DirectoryEditorFile | undefined; + + constructor(access: TemplateDirectoryAccess) { + this.#access = access; + } + + get files() { + return this.#files; + } + + get selectedFile() { + return this.#selectedFile; + } + + setSelectedFile = (path: string | undefined): void => { + const prev = this.#selectedFile; + const next = this.#files.find(file => file.path === path); + if (prev !== next) { + this.#selectedFile = next; + this.#signalUpdate(); + } + }; + + get dirty() { + return this.#files.some(file => file.dirty); + } + + async save(): Promise { + await Promise.all(this.#files.map(file => file.save())); + } + + async reload(): Promise { + const selectedPath = this.#selectedFile?.path; + + const files = await this.#access.listFiles(); + const fileManagers = await Promise.all( + files.map(async file => { + const manager = new DirectoryEditorFileManager( + file, + this.#signalUpdate, + ); + await manager.reload(); + return manager; + }), + ); + this.#files.length = 0; + this.#files.push(...fileManagers); + + this.setSelectedFile(selectedPath); + this.#signalUpdate(); + } + + subscribe(listener: () => void): () => void { + this.#listeners.add(listener); + return () => { + this.#listeners.delete(listener); + }; + } + + #signalUpdate = () => { + this.#listeners.forEach(listener => listener()); + }; +} + +const DirectoryEditorContext = createContext( + undefined, +); + +export function useDirectoryEditor(): DirectoryEditor { + const value = useContext(DirectoryEditorContext); + const rerender = useRerender(); + + useEffect(() => value?.subscribe(rerender), [value, rerender]); + + if (!value) { + throw new Error('must be used within a DirectoryEditorProvider'); + } + return value; +} + +interface DirectoryEditorProviderProps { + directory: TemplateDirectoryAccess; + children?: ReactNode; +} + +export function DirectoryEditorProvider(props: DirectoryEditorProviderProps) { + const { directory } = props; + + const [{ result, error }, { execute }] = useAsync( + async (dir: TemplateDirectoryAccess) => { + const manager = new DirectoryEditorManager(dir); + await manager.reload(); + + const firstYaml = manager.files.find(file => file.path.match(/\.ya?ml$/)); + if (firstYaml) { + manager.setSelectedFile(firstYaml.path); + } + + return manager; + }, + ); + + useEffect(() => { + execute(directory); + }, [execute, directory]); + + if (error) { + return ; + } else if (!result) { + return ; + } + + return ( + + {props.children} + + ); +} diff --git a/plugins/scaffolder/src/components/TemplateEditorPage/DryRunContext.test.tsx b/plugins/scaffolder/src/components/TemplateEditorPage/DryRunContext.test.tsx new file mode 100644 index 0000000000..184118914a --- /dev/null +++ b/plugins/scaffolder/src/components/TemplateEditorPage/DryRunContext.test.tsx @@ -0,0 +1,44 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { base64EncodeContent } from './DryRunContext'; + +// eslint-disable-next-line no-restricted-imports +import { TextEncoder } from 'util'; + +window.TextEncoder = TextEncoder; + +describe('base64EncodeContent', () => { + it('encodes text files', () => { + expect(base64EncodeContent('abc')).toBe('YWJj'); + expect(base64EncodeContent('abc'.repeat(1000000))).toBe( + btoa(''), + ); + }); + + it('encodes binary files', () => { + expect(base64EncodeContent('\x00\x01\x02')).toBe('AAEC'); + expect(base64EncodeContent('๐Ÿ˜…')).toBe('8J+YhQ=='); + // Triggers chunking + expect(base64EncodeContent('๐Ÿ˜…'.repeat(18000))).toBe( + '8J+YhfCfmIXwn5iF8J+YhfCfmIXwn5iF'.repeat(3000), + ); + // Triggers size check + expect(base64EncodeContent('๐Ÿ˜…'.repeat(1000000))).toBe( + btoa(''), + ); + }); +}); diff --git a/plugins/scaffolder/src/components/TemplateEditorPage/DryRunContext.tsx b/plugins/scaffolder/src/components/TemplateEditorPage/DryRunContext.tsx new file mode 100644 index 0000000000..0a5bfeefd5 --- /dev/null +++ b/plugins/scaffolder/src/components/TemplateEditorPage/DryRunContext.tsx @@ -0,0 +1,177 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import yaml from 'yaml'; +import { useApi } from '@backstage/core-plugin-api'; +import { JsonObject } from '@backstage/types'; +import React, { + createContext, + ReactNode, + useCallback, + useContext, + useMemo, + useRef, + useState, +} from 'react'; +import { scaffolderApiRef } from '../../api'; +import { ScaffolderDryRunResponse } from '../../types'; + +const MAX_CONTENT_SIZE = 256 * 1024; +const CHUNK_SIZE = 0x8000; + +interface DryRunOptions { + templateContent: string; + values: JsonObject; + files: Array<{ path: string; content: string }>; +} + +interface DryRunResult extends ScaffolderDryRunResponse { + id: number; +} + +interface DryRun { + results: DryRunResult[]; + selectedResult: DryRunResult | undefined; + + selectResult(id: number): void; + deleteResult(id: number): void; + execute(options: DryRunOptions): Promise; +} + +const DryRunContext = createContext(undefined); + +interface DryRunProviderProps { + children: ReactNode; +} + +export function base64EncodeContent(content: string): string { + if (content.length > MAX_CONTENT_SIZE) { + return btoa(''); + } + + try { + return btoa(content); + } catch { + const decoder = new TextEncoder(); + const buffer = decoder.encode(content); + + const chunks = new Array(); + for (let offset = 0; offset < buffer.length; offset += CHUNK_SIZE) { + chunks.push( + String.fromCharCode(...buffer.slice(offset, offset + CHUNK_SIZE)), + ); + } + return btoa(chunks.join('')); + } +} + +export function DryRunProvider(props: DryRunProviderProps) { + const scaffolderApi = useApi(scaffolderApiRef); + + const [state, setState] = useState< + Pick + >({ + results: [], + selectedResult: undefined, + }); + const idRef = useRef(1); + + const selectResult = useCallback((id: number) => { + setState(prevState => { + const result = prevState.results.find(r => r.id === id); + if (result === prevState.selectedResult) { + return prevState; + } + return { + results: prevState.results, + selectedResult: result, + }; + }); + }, []); + + const deleteResult = useCallback((id: number) => { + setState(prevState => { + const index = prevState.results.findIndex(r => r.id === id); + if (index === -1) { + return prevState; + } + const newResults = prevState.results.slice(); + const [deleted] = newResults.splice(index, 1); + return { + results: newResults, + selectedResult: + prevState.selectedResult?.id === deleted.id + ? newResults[0] + : prevState.selectedResult, + }; + }); + }, []); + + const execute = useCallback( + async (options: DryRunOptions) => { + if (!scaffolderApi.dryRun) { + throw new Error('Scaffolder API does not support dry-run'); + } + + const parsed = yaml.parse(options.templateContent); + + const response = await scaffolderApi.dryRun({ + template: parsed, + values: options.values, + secrets: {}, + directoryContents: options.files.map(file => ({ + path: file.path, + base64Content: base64EncodeContent(file.content), + })), + }); + + const result = { + ...response, + id: idRef.current++, + }; + + setState(prevState => ({ + results: [...prevState.results, result], + selectedResult: prevState.selectedResult ?? result, + })); + }, + [scaffolderApi], + ); + + const dryRun = useMemo( + () => ({ + ...state, + selectResult, + deleteResult, + execute, + }), + [state, selectResult, deleteResult, execute], + ); + + return ( + + {props.children} + + ); +} + +export function useDryRun(): DryRun { + const value = useContext(DryRunContext); + if (!value) { + throw new Error('must be used within a DryRunProvider'); + } + return value; +} diff --git a/plugins/scaffolder/src/components/TemplateEditorPage/DryRunResults/DryRunResults.test.tsx b/plugins/scaffolder/src/components/TemplateEditorPage/DryRunResults/DryRunResults.test.tsx new file mode 100644 index 0000000000..bc3354cc46 --- /dev/null +++ b/plugins/scaffolder/src/components/TemplateEditorPage/DryRunResults/DryRunResults.test.tsx @@ -0,0 +1,118 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; +import { act, screen, waitFor } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import React, { useEffect } from 'react'; +import { scaffolderApiRef } from '../../../api'; +import { DryRunProvider, useDryRun } from '../DryRunContext'; +import { DryRunResults } from './DryRunResults'; + +function DryRunRemote({ + execute, + remove, +}: { + execute?: boolean; + remove?: boolean; +}) { + const dryRun = useDryRun(); + + useEffect(() => { + if (execute) { + dryRun.execute({ + templateContent: '', + values: {}, + files: [], + }); + } + if (remove) { + if (dryRun.selectedResult) { + dryRun.deleteResult(dryRun.selectedResult.id); + } + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [execute, remove]); + + return null; +} + +const mockScaffolderApi = { + dryRun: async () => ({ + directoryContents: [], + log: [], + output: {}, + steps: [], + }), +}; + +describe('DryRunResults', () => { + it('renders without exploding', async () => { + await renderInTestApp( + + + + + , + ); + expect(screen.getByText('Dry-run results')).toBeInTheDocument(); + }); + + it('expands when dry-run result is added and toggles on click, and disappears when results are gone', async () => { + const { rerender } = await renderInTestApp( + + + + + + , + ); + + expect(screen.getByText('Files')).not.toBeVisible(); + + await act(async () => { + rerender( + + + + + + , + ); + }); + + expect(screen.getByText('Files')).toBeVisible(); + + await userEvent.click(screen.getByText('Dry-run results')); + await waitFor(() => expect(screen.getByText('Files')).not.toBeVisible()); + + await userEvent.click(screen.getByText('Dry-run results')); + expect(screen.getByText('Files')).toBeVisible(); + + await act(async () => { + rerender( + + + + + + , + ); + }); + + await waitFor(() => expect(screen.getByText('Files')).not.toBeVisible()); + }); +}); diff --git a/plugins/scaffolder/src/components/TemplateEditorPage/DryRunResults/DryRunResults.tsx b/plugins/scaffolder/src/components/TemplateEditorPage/DryRunResults/DryRunResults.tsx new file mode 100644 index 0000000000..ae9374438f --- /dev/null +++ b/plugins/scaffolder/src/components/TemplateEditorPage/DryRunResults/DryRunResults.tsx @@ -0,0 +1,91 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { BackstageTheme } from '@backstage/theme'; +import Accordion from '@material-ui/core/Accordion'; +import AccordionDetails from '@material-ui/core/AccordionDetails'; +import AccordionSummary from '@material-ui/core/AccordionSummary'; +import Divider from '@material-ui/core/Divider'; +import { makeStyles } from '@material-ui/core/styles'; +import Typography from '@material-ui/core/Typography'; +import ExpandMoreIcon from '@material-ui/icons/ExpandLess'; +import { usePrevious } from '@react-hookz/web'; +import React, { useEffect, useState } from 'react'; +import { useDryRun } from '../DryRunContext'; +import { DryRunResultsList } from './DryRunResultsList'; +import { DryRunResultsView } from './DryRunResultsView'; + +const useStyles = makeStyles((theme: BackstageTheme) => ({ + header: { + height: 48, + minHeight: 0, + '&.Mui-expanded': { + height: 48, + minHeight: 0, + }, + }, + content: { + display: 'grid', + background: theme.palette.background.default, + gridTemplateColumns: '180px auto 1fr', + gridTemplateRows: '1fr', + padding: 0, + height: 400, + }, +})); + +export function DryRunResults() { + const classes = useStyles(); + const dryRun = useDryRun(); + const [expanded, setExpanded] = useState(false); + const [hidden, setHidden] = useState(true); + + const resultsLength = dryRun.results.length; + const prevResultsLength = usePrevious(resultsLength); + useEffect(() => { + if (prevResultsLength === 0 && resultsLength === 1) { + setHidden(false); + setExpanded(true); + } else if (prevResultsLength === 1 && resultsLength === 0) { + setExpanded(false); + } + }, [prevResultsLength, resultsLength]); + + return ( + <> + + + ); +} diff --git a/plugins/scaffolder/src/components/TemplateEditorPage/DryRunResults/DryRunResultsList.test.tsx b/plugins/scaffolder/src/components/TemplateEditorPage/DryRunResults/DryRunResultsList.test.tsx new file mode 100644 index 0000000000..65eb59c84a --- /dev/null +++ b/plugins/scaffolder/src/components/TemplateEditorPage/DryRunResults/DryRunResultsList.test.tsx @@ -0,0 +1,95 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; +import { act, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import React, { useEffect } from 'react'; +import { scaffolderApiRef } from '../../../api'; +import { DryRunProvider, useDryRun } from '../DryRunContext'; +import { DryRunResultsList } from './DryRunResultsList'; + +function DryRunRemote({ execute }: { execute?: number }) { + const dryRun = useDryRun(); + + useEffect(() => { + if (execute) { + dryRun.execute({ + templateContent: '', + values: {}, + files: [], + }); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [execute]); + + return null; +} + +const mockScaffolderApi = { + dryRun: async () => ({ + directoryContents: [], + log: [], + output: {}, + steps: [], + }), +}; + +describe('DryRunResultsList', () => { + it('renders without exploding', async () => { + const rendered = await renderInTestApp( + + + + + , + ); + expect(rendered.baseElement.querySelector('ul')).toBeEmptyDOMElement(); + }); + + it('adds new result items and deletes them', async () => { + const { rerender } = await renderInTestApp( + + + + + + , + ); + + expect(screen.queryByText('Result 1')).toBeInTheDocument(); + expect(screen.queryByText('Result 2')).not.toBeInTheDocument(); + + await act(async () => { + rerender( + + + + + + , + ); + }); + + expect(screen.queryByText('Result 1')).toBeInTheDocument(); + expect(screen.queryByText('Result 2')).toBeInTheDocument(); + + await userEvent.click(screen.getAllByLabelText('delete')[0]); + + expect(screen.queryByText('Result 1')).not.toBeInTheDocument(); + expect(screen.queryByText('Result 2')).toBeInTheDocument(); + }); +}); diff --git a/plugins/scaffolder/src/components/TemplateEditorPage/DryRunResults/DryRunResultsList.tsx b/plugins/scaffolder/src/components/TemplateEditorPage/DryRunResults/DryRunResultsList.tsx new file mode 100644 index 0000000000..462c3b58fd --- /dev/null +++ b/plugins/scaffolder/src/components/TemplateEditorPage/DryRunResults/DryRunResultsList.tsx @@ -0,0 +1,83 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { BackstageTheme } from '@backstage/theme'; +import IconButton from '@material-ui/core/IconButton'; +import List from '@material-ui/core/List'; +import ListItem from '@material-ui/core/ListItem'; +import ListItemIcon from '@material-ui/core/ListItemIcon'; +import ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction'; +import ListItemText from '@material-ui/core/ListItemText'; +import { makeStyles } from '@material-ui/core/styles'; +import CancelIcon from '@material-ui/icons/Cancel'; +import CheckIcon from '@material-ui/icons/Check'; +import DeleteIcon from '@material-ui/icons/Delete'; +import React from 'react'; +import { useDryRun } from '../DryRunContext'; + +const useStyles = makeStyles((theme: BackstageTheme) => ({ + root: { + overflowY: 'auto', + background: theme.palette.background.default, + }, + iconSuccess: { + minWidth: 0, + marginRight: theme.spacing(1), + color: theme.palette.status.ok, + }, + iconFailure: { + minWidth: 0, + marginRight: theme.spacing(1), + color: theme.palette.status.error, + }, +})); + +export function DryRunResultsList() { + const classes = useStyles(); + const dryRun = useDryRun(); + + return ( + + {dryRun.results.map(result => { + const failed = result.log.some(l => l.body.status === 'failed'); + return ( + dryRun.selectResult(result.id)} + > + + {failed ? : } + + + + dryRun.deleteResult(result.id)} + > + + + + + ); + })} + + ); +} diff --git a/plugins/scaffolder/src/components/TemplateEditorPage/DryRunResults/DryRunResultsSplitView.tsx b/plugins/scaffolder/src/components/TemplateEditorPage/DryRunResults/DryRunResultsSplitView.tsx new file mode 100644 index 0000000000..b554a1c0c0 --- /dev/null +++ b/plugins/scaffolder/src/components/TemplateEditorPage/DryRunResults/DryRunResultsSplitView.tsx @@ -0,0 +1,55 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { makeStyles } from '@material-ui/core/styles'; +import Divider from '@material-ui/core/Divider'; +import React, { Children, ReactNode } from 'react'; +import classNames from 'classnames'; + +const useStyles = makeStyles(theme => ({ + root: { + display: 'grid', + gridTemplateColumns: '280px auto 3fr', + gridTemplateRows: '1fr', + }, + child: { + overflowY: 'auto', + height: '100%', + minHeight: 0, + }, + firstChild: { + background: theme.palette.background.paper, + }, +})); + +export function DryRunResultsSplitView(props: { children: ReactNode }) { + const classes = useStyles(); + const childArray = Children.toArray(props.children); + + if (childArray.length !== 2) { + throw new Error('must have exactly 2 children'); + } + + return ( +
+
+ {childArray[0]} +
+ +
{childArray[1]}
+
+ ); +} diff --git a/plugins/scaffolder/src/components/TemplateEditorPage/DryRunResults/DryRunResultsView.test.tsx b/plugins/scaffolder/src/components/TemplateEditorPage/DryRunResults/DryRunResultsView.test.tsx new file mode 100644 index 0000000000..bc6b5d8f6f --- /dev/null +++ b/plugins/scaffolder/src/components/TemplateEditorPage/DryRunResults/DryRunResultsView.test.tsx @@ -0,0 +1,99 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { entityRouteRef } from '@backstage/plugin-catalog-react'; +import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; +import { screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import React, { ReactNode, useEffect } from 'react'; +import { scaffolderApiRef } from '../../../api'; +import { DryRunProvider, useDryRun } from '../DryRunContext'; +import { DryRunResultsView } from './DryRunResultsView'; + +// The inside needs mocking to render in jsdom +jest.mock('react-virtualized-auto-sizer', () => ({ + __esModule: true, + default: (props: { + children: (size: { width: number; height: number }) => ReactNode; + }) => <>{props.children({ width: 400, height: 200 })}, +})); + +function DryRunRemote() { + const dryRun = useDryRun(); + + useEffect(() => { + dryRun.execute({ + templateContent: '', + values: {}, + files: [], + }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + return null; +} + +describe('DryRunResultsView', () => { + it('renders a result', async () => { + await renderInTestApp( + ({ + directoryContents: [ + { + path: 'foo.txt', + base64Content: btoa('Foo Content'), + executable: false, + }, + ], + log: [{ body: { message: 'Foo Message', stepId: 'foo' } }], + output: { + links: [{ title: 'Foo Link', url: 'http://example.com' }], + }, + steps: [{ id: 'foo', action: 'foo', name: 'Foo' }], + }), + }, + ], + ]} + > + + + + + , + { + mountedRoutes: { + '/catalog/:kind/:namespace/:name': entityRouteRef, + }, + }, + ); + + expect(screen.getByText('foo.txt')).toBeInTheDocument(); + expect(screen.getByText('Foo Content')).toBeInTheDocument(); + + expect(screen.queryByText('Foo Message')).not.toBeInTheDocument(); + expect(screen.queryByText('Foo Link')).not.toBeInTheDocument(); + + await userEvent.click(screen.getByText('Log')); + expect(screen.getByText('Foo Message')).toBeInTheDocument(); + + await userEvent.click(screen.getByText('Output')); + expect(screen.getByText('Foo Link')).toBeInTheDocument(); + }); +}); diff --git a/plugins/scaffolder/src/components/TemplateEditorPage/DryRunResults/DryRunResultsView.tsx b/plugins/scaffolder/src/components/TemplateEditorPage/DryRunResults/DryRunResultsView.tsx new file mode 100644 index 0000000000..9c56039808 --- /dev/null +++ b/plugins/scaffolder/src/components/TemplateEditorPage/DryRunResults/DryRunResultsView.tsx @@ -0,0 +1,195 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { LogViewer } from '@backstage/core-components'; +import { StreamLanguage } from '@codemirror/language'; +import { yaml as yamlSupport } from '@codemirror/legacy-modes/mode/yaml'; +import Box from '@material-ui/core/Box'; +import Divider from '@material-ui/core/Divider'; +import { makeStyles } from '@material-ui/core/styles'; +import Tab from '@material-ui/core/Tab'; +import Tabs from '@material-ui/core/Tabs'; +import CodeMirror from '@uiw/react-codemirror'; +import React, { useEffect, useMemo, useState } from 'react'; +import { TaskStatusStepper } from '../../TaskPage/TaskPage'; +import { TaskPageLinks } from '../../TaskPage/TaskPageLinks'; +import { useDryRun } from '../DryRunContext'; +import { FileBrowser } from '../../FileBrowser'; +import { DryRunResultsSplitView } from './DryRunResultsSplitView'; + +const useStyles = makeStyles({ + root: { + display: 'flex', + flexFlow: 'column nowrap', + }, + contentWrapper: { + flex: 1, + position: 'relative', + }, + content: { + position: 'absolute', + top: 0, + left: 0, + right: 0, + bottom: 0, + + display: 'flex', + '& > *': { + flex: 1, + }, + }, + codeMirror: { + height: '100%', + overflowY: 'auto', + }, +}); + +function FilesContent() { + const classes = useStyles(); + const { selectedResult } = useDryRun(); + const [selectedPath, setSelectedPath] = useState(''); + const selectedFile = selectedResult?.directoryContents.find( + f => f.path === selectedPath, + ); + + useEffect(() => { + if (selectedResult) { + const [firstFile] = selectedResult.directoryContents; + if (firstFile) { + setSelectedPath(firstFile.path); + } else { + setSelectedPath(''); + } + } + return undefined; + }, [selectedResult]); + + if (!selectedResult) { + return null; + } + return ( + + file.path)} + /> + + + ); +} +function LogContent() { + const { selectedResult } = useDryRun(); + const [currentStepId, setUserSelectedStepId] = useState(); + + const steps = useMemo(() => { + if (!selectedResult) { + return []; + } + return ( + selectedResult.steps.map(step => { + const stepLog = selectedResult.log.filter( + l => l.body.stepId === step.id, + ); + return { + id: step.id, + name: step.name, + logString: stepLog.map(l => l.body.message).join('\n'), + status: stepLog[stepLog.length - 1]?.body.status ?? 'completed', + }; + }) ?? [] + ); + }, [selectedResult]); + + if (!selectedResult) { + return null; + } + + const selectedStep = steps.find(s => s.id === currentStepId) ?? steps[0]; + + return ( + + + + + ); +} + +function OutputContent() { + const classes = useStyles(); + const { selectedResult } = useDryRun(); + + if (!selectedResult) { + return null; + } + + return ( + + + {selectedResult.output?.links?.length && ( + + )} + + + + ); +} + +export function DryRunResultsView() { + const classes = useStyles(); + const [selectedTab, setSelectedTab] = useState<'files' | 'log' | 'output'>( + 'files', + ); + + return ( +
+ setSelectedTab(v)}> + + + + + + +
+
+ {selectedTab === 'files' && } + {selectedTab === 'log' && } + {selectedTab === 'output' && } +
+
+
+ ); +} diff --git a/plugins/scaffolder/src/components/TemplateEditorPage/DryRunResults/index.ts b/plugins/scaffolder/src/components/TemplateEditorPage/DryRunResults/index.ts new file mode 100644 index 0000000000..af8ef5214f --- /dev/null +++ b/plugins/scaffolder/src/components/TemplateEditorPage/DryRunResults/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { DryRunResults } from './DryRunResults'; diff --git a/plugins/scaffolder/src/components/TemplateEditorPage/TemplateEditor.tsx b/plugins/scaffolder/src/components/TemplateEditorPage/TemplateEditor.tsx new file mode 100644 index 0000000000..cc4dd8c9a1 --- /dev/null +++ b/plugins/scaffolder/src/components/TemplateEditorPage/TemplateEditor.tsx @@ -0,0 +1,89 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { makeStyles } from '@material-ui/core'; +import React, { useState } from 'react'; +import { FieldExtensionOptions } from '../../extensions'; +import { TemplateDirectoryAccess } from '../../lib/filesystem'; +import { DirectoryEditorProvider } from './DirectoryEditorContext'; +import { DryRunProvider } from './DryRunContext'; +import { DryRunResults } from './DryRunResults'; +import { TemplateEditorBrowser } from './TemplateEditorBrowser'; +import { TemplateEditorForm } from './TemplateEditorForm'; +import { TemplateEditorTextArea } from './TemplateEditorTextArea'; + +const useStyles = makeStyles({ + // Reset and fix sizing to make sure scrolling behaves correctly + root: { + gridArea: 'pageContent', + + display: 'grid', + gridTemplateAreas: ` + "browser editor preview" + "results results results" + `, + gridTemplateColumns: '1fr 3fr 2fr', + gridTemplateRows: '1fr auto', + }, + browser: { + gridArea: 'browser', + overflow: 'auto', + }, + editor: { + gridArea: 'editor', + overflow: 'auto', + }, + preview: { + gridArea: 'preview', + overflow: 'auto', + }, + results: { + gridArea: 'results', + }, +}); + +export const TemplateEditor = (props: { + directory: TemplateDirectoryAccess; + fieldExtensions?: FieldExtensionOptions[]; + onClose?: () => void; +}) => { + const classes = useStyles(); + + const [errorText, setErrorText] = useState(); + + return ( + + +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+ ); +}; diff --git a/plugins/scaffolder/src/components/TemplateEditorPage/TemplateEditorBrowser.test.tsx b/plugins/scaffolder/src/components/TemplateEditorPage/TemplateEditorBrowser.test.tsx new file mode 100644 index 0000000000..cec311a5cd --- /dev/null +++ b/plugins/scaffolder/src/components/TemplateEditorPage/TemplateEditorBrowser.test.tsx @@ -0,0 +1,55 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { renderInTestApp } from '@backstage/test-utils'; +import { screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import React from 'react'; +import { MockFileSystemAccess } from '../../lib/filesystem/MockFileSystemAccess'; +import { DirectoryEditorProvider } from './DirectoryEditorContext'; +import { TemplateEditorBrowser } from './TemplateEditorBrowser'; + +Blob.prototype.text = async function text() { + return new Promise(resolve => { + const reader = new FileReader(); + reader.onload = () => resolve(reader.result as string); + reader.readAsText(this); + }); +}; + +describe('TemplateEditorBrowser', () => { + it('should render files and expand dirs without exploding', async () => { + await renderInTestApp( + + + , + ); + + await expect(screen.findByText('foo.txt')).resolves.toBeInTheDocument(); + expect(screen.getByText('dir')).toBeInTheDocument(); + expect(screen.queryByText('bar.txt')).not.toBeInTheDocument(); + expect(screen.queryByText('baz.txt')).not.toBeInTheDocument(); + + await userEvent.click(screen.getByText('dir')); + expect(screen.getByText('bar.txt')).toBeInTheDocument(); + expect(screen.getByText('baz.txt')).toBeInTheDocument(); + }); +}); diff --git a/plugins/scaffolder/src/components/TemplateEditorPage/TemplateEditorBrowser.tsx b/plugins/scaffolder/src/components/TemplateEditorPage/TemplateEditorBrowser.tsx new file mode 100644 index 0000000000..177ad11c2b --- /dev/null +++ b/plugins/scaffolder/src/components/TemplateEditorPage/TemplateEditorBrowser.tsx @@ -0,0 +1,99 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { Divider, IconButton, makeStyles, Tooltip } from '@material-ui/core'; +import CloseIcon from '@material-ui/icons/Close'; +import RefreshIcon from '@material-ui/icons/Refresh'; +import SaveIcon from '@material-ui/icons/Save'; +import React from 'react'; +import { FileBrowser } from '../FileBrowser'; +import { useDirectoryEditor } from './DirectoryEditorContext'; + +const useStyles = makeStyles(theme => ({ + button: { + padding: theme.spacing(1), + }, + buttons: { + display: 'flex', + flexFlow: 'row nowrap', + alignItems: 'center', + justifyContent: 'flex-start', + }, + buttonsGap: { + flex: '1 1 auto', + }, + buttonsDivider: { + marginBottom: theme.spacing(1), + }, +})); + +/** The local file browser for the template editor */ +export function TemplateEditorBrowser(props: { onClose?: () => void }) { + const classes = useStyles(); + const directoryEditor = useDirectoryEditor(); + const changedFiles = directoryEditor.files.filter(file => file.dirty); + + const handleClose = () => { + if (!props.onClose) { + return; + } + if (changedFiles.length > 0) { + // eslint-disable-next-line no-alert + const accepted = window.confirm( + 'Are you sure? Unsaved changes will be lost', + ); + if (!accepted) { + return; + } + } + props.onClose(); + }; + + return ( + <> +
+ + !file.dirty)} + onClick={() => directoryEditor.save()} + > + + + + + directoryEditor.reload()} + > + + + +
+ + + + + +
+ + file.path)} + /> + + ); +} diff --git a/plugins/scaffolder/src/components/TemplateEditorPage/TemplateEditorForm.tsx b/plugins/scaffolder/src/components/TemplateEditorPage/TemplateEditorForm.tsx new file mode 100644 index 0000000000..11b4da246d --- /dev/null +++ b/plugins/scaffolder/src/components/TemplateEditorPage/TemplateEditorForm.tsx @@ -0,0 +1,240 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { useApiHolder } from '@backstage/core-plugin-api'; +import { JsonObject, JsonValue } from '@backstage/types'; +import { makeStyles } from '@material-ui/core/styles'; +import React, { Component, ReactNode, useMemo, useState } from 'react'; +import useDebounce from 'react-use/lib/useDebounce'; +import yaml from 'yaml'; +import { FieldExtensionOptions } from '../../extensions'; +import { TemplateParameterSchema } from '../../types'; +import { MultistepJsonForm } from '../MultistepJsonForm'; +import { createValidator } from '../TemplatePage'; +import { useDirectoryEditor } from './DirectoryEditorContext'; +import { useDryRun } from './DryRunContext'; + +const useStyles = makeStyles({ + containerWrapper: { + position: 'relative', + width: '100%', + height: '100%', + }, + container: { + position: 'absolute', + top: 0, + bottom: 0, + left: 0, + right: 0, + overflow: 'auto', + }, +}); + +interface ErrorBoundaryProps { + invalidator: unknown; + setErrorText(errorText: string | undefined): void; + children: ReactNode; +} + +interface ErrorBoundaryState { + shouldRender: boolean; +} + +class ErrorBoundary extends Component { + state = { + shouldRender: true, + }; + + componentDidUpdate(prevProps: { invalidator: unknown }) { + if (prevProps.invalidator !== this.props.invalidator) { + this.setState({ shouldRender: true }); + } + } + + componentDidCatch(error: Error) { + this.props.setErrorText(error.message); + this.setState({ shouldRender: false }); + } + + render() { + return this.state.shouldRender ? this.props.children : null; + } +} + +interface TemplateEditorFormProps { + content?: string; + /** Setting this to true will cause the content to be parsed as if it is the template entity spec */ + contentIsSpec?: boolean; + data: JsonObject; + onUpdate: (data: JsonObject) => void; + setErrorText: (errorText?: string) => void; + + onDryRun?: (data: JsonObject) => Promise; + fieldExtensions?: FieldExtensionOptions[]; +} + +function isJsonObject(value: JsonValue | undefined): value is JsonObject { + return typeof value === 'object' && value !== null && !Array.isArray(value); +} + +/** Shows the a template form that is parsed from the provided content */ +export function TemplateEditorForm(props: TemplateEditorFormProps) { + const { + content, + contentIsSpec, + data, + onUpdate, + onDryRun, + setErrorText, + fieldExtensions = [], + } = props; + const classes = useStyles(); + const apiHolder = useApiHolder(); + + const [steps, setSteps] = useState(); + + const fields = useMemo(() => { + return Object.fromEntries( + fieldExtensions.map(({ name, component }) => [name, component]), + ); + }, [fieldExtensions]); + + useDebounce( + () => { + try { + if (!content) { + setSteps(undefined); + return; + } + const parsed: JsonValue = yaml.parse(content); + + if (!isJsonObject(parsed)) { + setSteps(undefined); + return; + } + + let rootObj = parsed; + if (!contentIsSpec) { + const isTemplate = + String(parsed.kind).toLocaleLowerCase('en-US') === 'template'; + if (!isTemplate) { + setSteps(undefined); + return; + } + + rootObj = isJsonObject(parsed.spec) ? parsed.spec : {}; + } + + const { parameters } = rootObj; + if (!Array.isArray(parameters)) { + setErrorText('Template parameters must be an array'); + setSteps(undefined); + return; + } + + const fieldValidators = Object.fromEntries( + fieldExtensions.map(({ name, validation }) => [name, validation]), + ); + + setErrorText(); + setSteps( + parameters.flatMap(param => + isJsonObject(param) + ? [ + { + title: String(param.title), + schema: param, + validate: createValidator(param, fieldValidators, { + apiHolder, + }), + }, + ] + : [], + ), + ); + } catch (e) { + setErrorText(e.message); + } + }, + 250, + [contentIsSpec, content, apiHolder], + ); + + if (!steps) { + return null; + } + + return ( +
+
+ + onUpdate(e.formData)} + onReset={() => onUpdate({})} + finishButtonLabel={onDryRun && 'Try It'} + onFinish={onDryRun && (() => onDryRun(data))} + /> + +
+
+ ); +} + +/** A version of the TemplateEditorForm that is connected to the DirectoryEditor and DryRun contexts */ +export function TemplateEditorFormDirectoryEditorDryRun( + props: Pick, +) { + const { setErrorText, fieldExtensions = [] } = props; + const dryRun = useDryRun(); + + const directoryEditor = useDirectoryEditor(); + const { selectedFile } = directoryEditor; + + const [data, setData] = useState({}); + + const handleDryRun = async () => { + if (!selectedFile) { + return; + } + + await dryRun.execute({ + templateContent: selectedFile.content, + values: data, + files: directoryEditor.files, + }); + }; + + const content = + selectedFile && selectedFile.path.match(/\.ya?ml$/) + ? selectedFile.content + : undefined; + + return ( + + ); +} + +TemplateEditorForm.DirectoryEditorDryRun = + TemplateEditorFormDirectoryEditorDryRun; diff --git a/plugins/scaffolder/src/components/TemplateEditorPage/TemplateEditorIntro.tsx b/plugins/scaffolder/src/components/TemplateEditorPage/TemplateEditorIntro.tsx new file mode 100644 index 0000000000..dc411ee965 --- /dev/null +++ b/plugins/scaffolder/src/components/TemplateEditorPage/TemplateEditorIntro.tsx @@ -0,0 +1,127 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import Card from '@material-ui/core/Card'; +import CardActionArea from '@material-ui/core/CardActionArea'; +import CardContent from '@material-ui/core/CardContent'; +import Tooltip from '@material-ui/core/Tooltip'; +import Typography from '@material-ui/core/Typography'; +import InfoOutlinedIcon from '@material-ui/icons/InfoOutlined'; +import { makeStyles } from '@material-ui/core/styles'; +import { WebFileSystemAccess } from '../../lib/filesystem'; + +const useStyles = makeStyles(theme => ({ + introText: { + textAlign: 'center', + marginTop: theme.spacing(2), + }, + card: { + position: 'relative', + maxWidth: 340, + marginTop: theme.spacing(4), + margin: theme.spacing(0, 2), + }, + infoIcon: { + position: 'absolute', + top: theme.spacing(1), + right: theme.spacing(1), + }, +})); + +interface EditorIntroProps { + style?: JSX.IntrinsicElements['div']['style']; + onSelect?: (option: 'local' | 'form') => void; +} + +export function TemplateEditorIntro(props: EditorIntroProps) { + const classes = useStyles(); + const supportsLoad = WebFileSystemAccess.isSupported(); + + const cardLoadLocal = ( + + props.onSelect?.('local')} + > + + + Load Template Directory + + + Load a local template directory, allowing you to both edit and try + executing your own template. + + + + {!supportsLoad && ( +
+ + + +
+ )} +
+ ); + + const cardFormEditor = ( + + props.onSelect?.('form')}> + + + Edit Template Form + + + Preview and edit a template form, either using a sample template or + by loading a template from the catalog. + + + + + ); + + return ( +
+ + Get started by choosing one of the options below + +
+ {supportsLoad && cardLoadLocal} + {cardFormEditor} + {!supportsLoad && cardLoadLocal} +
+
+ ); +} diff --git a/plugins/scaffolder/src/components/TemplateEditorPage/TemplateEditorPage.test.tsx b/plugins/scaffolder/src/components/TemplateEditorPage/TemplateEditorPage.test.tsx new file mode 100644 index 0000000000..bf9cd8c464 --- /dev/null +++ b/plugins/scaffolder/src/components/TemplateEditorPage/TemplateEditorPage.test.tsx @@ -0,0 +1,58 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { catalogApiRef } from '@backstage/plugin-catalog-react'; +import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; +import { screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import React from 'react'; +import { TemplateEditorPage } from './TemplateEditorPage'; + +describe('TemplateEditorPage', () => { + it('renders without exploding', async () => { + await renderInTestApp(); + + expect(screen.getByText('Load Template Directory')).toBeInTheDocument(); + expect(screen.getByText('Edit Template Form')).toBeInTheDocument(); + }); + + it('template directory loading should not be supported in Jest', async () => { + await renderInTestApp(); + + expect( + screen.getByRole('button', { name: /Load Template Directory/ }), + ).toBeDisabled(); + }); + + it('should be able to continue to form preview', async () => { + await renderInTestApp( + + + , + ); + + await userEvent.click(screen.getByText('Edit Template Form')); + + expect(screen.getByLabelText('Load Existing Template')).toBeInTheDocument(); + }); +}); diff --git a/plugins/scaffolder/src/components/TemplateEditorPage/TemplateEditorPage.tsx b/plugins/scaffolder/src/components/TemplateEditorPage/TemplateEditorPage.tsx index 71c64a9fa4..fce2881190 100644 --- a/plugins/scaffolder/src/components/TemplateEditorPage/TemplateEditorPage.tsx +++ b/plugins/scaffolder/src/components/TemplateEditorPage/TemplateEditorPage.tsx @@ -13,258 +13,76 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import React, { useCallback, useState } from 'react'; -import useAsync from 'react-use/lib/useAsync'; -import useDebounce from 'react-use/lib/useDebounce'; -import { Entity } from '@backstage/catalog-model'; -import { Content, Header, InfoCard, Page } from '@backstage/core-components'; -import { alertApiRef, useApi, useApiHolder } from '@backstage/core-plugin-api'; +import React, { useState } from 'react'; +import { Content, Header, Page } from '@backstage/core-components'; import { - catalogApiRef, - humanizeEntityRef, -} from '@backstage/plugin-catalog-react'; -import { JsonObject } from '@backstage/types'; -import { yaml as yamlSupport } from '@codemirror/legacy-modes/mode/yaml'; -import { showPanel } from '@codemirror/view'; -import { StreamLanguage } from '@codemirror/language'; - -import { - FormControl, - Grid, - InputLabel, - LinearProgress, - makeStyles, - MenuItem, - Select, -} from '@material-ui/core'; -import { IChangeEvent } from '@rjsf/core'; -import CodeMirror from '@uiw/react-codemirror'; -import yaml from 'yaml'; + TemplateDirectoryAccess, + WebFileSystemAccess, +} from '../../lib/filesystem'; +import { TemplateEditorIntro } from './TemplateEditorIntro'; +import { TemplateEditor } from './TemplateEditor'; +import { TemplateFormPreviewer } from './TemplateFormPreviewer'; import { FieldExtensionOptions } from '../../extensions'; -import { TemplateParameterSchema } from '../../types'; -import { MultistepJsonForm } from '../MultistepJsonForm'; -import { createValidator } from '../TemplatePage'; -const EXAMPLE_TEMPLATE_PARAMS_YAML = `# Edit the template parameters below to see how they will render in the scaffolder form UI -parameters: - - title: Fill in some steps - required: - - name - properties: - name: - title: Name - type: string - description: Unique name of the component - owner: - title: Owner - type: string - description: Owner of the component - ui:field: OwnerPicker - ui:options: - allowedKinds: - - Group - - title: Choose a location - required: - - repoUrl - properties: - repoUrl: - title: Repository Location - type: string - ui:field: RepoUrlPicker - ui:options: - allowedHosts: - - github.com -`; +type Selection = + | { + type: 'local'; + directory: TemplateDirectoryAccess; + } + | { + type: 'form'; + }; -type TemplateOption = { - label: string; - value: Entity; -}; - -const useStyles = makeStyles({ - templateSelect: { - marginBottom: '10px', - }, - grid: { - height: '100%', - }, - codeMirror: { - height: '95%', - }, -}); - -export const TemplateEditorPage = ({ - defaultPreviewTemplate = EXAMPLE_TEMPLATE_PARAMS_YAML, - customFieldExtensions = [], -}: { +interface TemplateEditorPageProps { defaultPreviewTemplate?: string; customFieldExtensions?: FieldExtensionOptions[]; -}) => { - const classes = useStyles(); - const alertApi = useApi(alertApiRef); - const catalogApi = useApi(catalogApiRef); - const apiHolder = useApiHolder(); - const [selectedTemplate, setSelectedTemplate] = useState(''); - const [schema, setSchema] = useState({ - title: '', - steps: [], - }); - const [templateOptions, setTemplateOptions] = useState([]); - const [templateYaml, setTemplateYaml] = useState(defaultPreviewTemplate); - const [formState, setFormState] = useState({}); +} - const { loading } = useAsync( - () => - catalogApi - .getEntities({ - filter: { kind: 'template' }, - fields: [ - 'kind', - 'metadata.namespace', - 'metadata.name', - 'metadata.title', - 'spec.parameters', - ], - }) - .then(({ items }) => - setTemplateOptions( - items.map(template => ({ - label: - template.metadata.title ?? - humanizeEntityRef(template, { defaultKind: 'template' }), - value: template, - })), - ), - ) - .catch(e => - alertApi.post({ - message: `Error loading exisiting templates: ${e.message}`, - severity: 'error', - }), - ), - [catalogApi], - ); +export function TemplateEditorPage(props: TemplateEditorPageProps) { + const [selection, setSelection] = useState(); - const errorPanel = document.createElement('div'); - errorPanel.style.color = 'red'; - - useDebounce( - () => { - try { - const parsedTemplate = yaml.parse(templateYaml); - - setSchema({ - title: 'Preview', - steps: parsedTemplate.parameters.map((param: JsonObject) => ({ - title: param.title, - schema: param, - })), - }); - setFormState({}); - } catch (e) { - errorPanel.textContent = e.message; - } - }, - 250, - [setFormState, setSchema, templateYaml], - ); - - const handleSelectChange = useCallback( - selected => { - setSelectedTemplate(selected); - setTemplateYaml(yaml.stringify(selected.spec)); - }, - [setTemplateYaml], - ); - - const handleFormReset = () => setFormState({}); - const handleFormChange = useCallback( - (e: IChangeEvent) => setFormState(e.formData), - [setFormState], - ); - - const handleCodeChange = useCallback( - (code: string) => { - setTemplateYaml(code); - }, - [setTemplateYaml], - ); - - const customFieldComponents = Object.fromEntries( - customFieldExtensions.map(({ name, component }) => [name, component]), - ); - - const customFieldValidators = Object.fromEntries( - customFieldExtensions.map(({ name, validation }) => [name, validation]), - ); + let content: JSX.Element | null = null; + if (selection?.type === 'local') { + content = ( + setSelection(undefined)} + /> + ); + } else if (selection?.type === 'form') { + content = ( + setSelection(undefined)} + /> + ); + } else { + content = ( + + { + if (option === 'local') { + WebFileSystemAccess.requestDirectoryAccess() + .then(directory => setSelection({ type: 'local', directory })) + .catch(() => {}); + } else if (option === 'form') { + setSelection({ type: 'form' }); + } + }} + /> + + ); + } return (
- - {loading && } - - - - - Load Existing Template - - - - ({ dom: errorPanel, top: true })), - ]} - onChange={handleCodeChange} - /> - - - {schema && ( - - { - return { - ...step, - validate: createValidator( - step.schema, - customFieldValidators, - { apiHolder }, - ), - }; - })} - /> - - )} - - - + {content} ); -}; +} diff --git a/plugins/scaffolder/src/components/TemplateEditorPage/TemplateEditorTextArea.tsx b/plugins/scaffolder/src/components/TemplateEditorPage/TemplateEditorTextArea.tsx new file mode 100644 index 0000000000..588b0cd4ba --- /dev/null +++ b/plugins/scaffolder/src/components/TemplateEditorPage/TemplateEditorTextArea.tsx @@ -0,0 +1,153 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { StreamLanguage } from '@codemirror/language'; +import { yaml as yamlSupport } from '@codemirror/legacy-modes/mode/yaml'; +import { showPanel } from '@codemirror/view'; +import { IconButton, makeStyles, Paper, Tooltip } from '@material-ui/core'; +import RefreshIcon from '@material-ui/icons/Refresh'; +import SaveIcon from '@material-ui/icons/Save'; +import { useKeyboardEvent } from '@react-hookz/web'; +import CodeMirror from '@uiw/react-codemirror'; +import React, { useMemo } from 'react'; +import { useDirectoryEditor } from './DirectoryEditorContext'; + +const useStyles = makeStyles(theme => ({ + container: { + position: 'relative', + width: '100%', + height: '100%', + }, + codeMirror: { + position: 'absolute', + top: 0, + bottom: 0, + left: 0, + right: 0, + }, + errorPanel: { + color: theme.palette.error.main, + lineHeight: 2, + margin: theme.spacing(0, 1), + }, + floatingButtons: { + position: 'absolute', + top: theme.spacing(1), + right: theme.spacing(3), + }, + floatingButton: { + padding: theme.spacing(1), + }, +})); + +/** A wrapper around CodeMirror with an error panel and extra actions available */ +export function TemplateEditorTextArea(props: { + content?: string; + onUpdate?: (content: string) => void; + errorText?: string; + onSave?: () => void; + onReload?: () => void; +}) { + const { errorText } = props; + const classes = useStyles(); + + const panelExtension = useMemo(() => { + if (!errorText) { + return showPanel.of(null); + } + + const dom = document.createElement('div'); + dom.classList.add(classes.errorPanel); + dom.textContent = errorText; + return showPanel.of(() => ({ dom, bottom: true })); + }, [classes, errorText]); + + useKeyboardEvent( + e => e.key === 's' && (e.ctrlKey || e.metaKey), + e => { + e.preventDefault(); + if (props.onSave) { + props.onSave(); + } + }, + ); + + return ( +
+ + {(props.onSave || props.onReload) && ( +
+ + {props.onSave && ( + + props.onSave?.()} + > + + + + )} + {props.onReload && ( + + props.onReload?.()} + > + + + + )} + +
+ )} +
+ ); +} + +/** A version of the TemplateEditorTextArea that is connected to the DirectoryEditor context */ +export function TemplateEditorDirectoryEditorTextArea(props: { + errorText?: string; +}) { + const directoryEditor = useDirectoryEditor(); + + const actions = directoryEditor.selectedFile?.dirty + ? { + onSave: () => directoryEditor.save(), + onReload: () => directoryEditor.reload(), + } + : { + onReload: () => directoryEditor.reload(), + }; + + return ( + directoryEditor.selectedFile?.updateContent(content)} + {...actions} + /> + ); +} + +TemplateEditorTextArea.DirectoryEditor = TemplateEditorDirectoryEditorTextArea; diff --git a/plugins/scaffolder/src/components/TemplateEditorPage/TemplateFormPreviewer.tsx b/plugins/scaffolder/src/components/TemplateEditorPage/TemplateFormPreviewer.tsx new file mode 100644 index 0000000000..bfa7071d48 --- /dev/null +++ b/plugins/scaffolder/src/components/TemplateEditorPage/TemplateFormPreviewer.tsx @@ -0,0 +1,216 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { Entity } from '@backstage/catalog-model'; +import { alertApiRef, useApi } from '@backstage/core-plugin-api'; +import { + catalogApiRef, + humanizeEntityRef, +} from '@backstage/plugin-catalog-react'; +import { + FormControl, + IconButton, + InputLabel, + LinearProgress, + makeStyles, + MenuItem, + Select, +} from '@material-ui/core'; +import CloseIcon from '@material-ui/icons/Close'; +import React, { useCallback, useState } from 'react'; +import useAsync from 'react-use/lib/useAsync'; +import yaml from 'yaml'; +import { FieldExtensionOptions } from '../../extensions'; +import { TemplateEditorForm } from './TemplateEditorForm'; +import { TemplateEditorTextArea } from './TemplateEditorTextArea'; + +const EXAMPLE_TEMPLATE_PARAMS_YAML = `# Edit the template parameters below to see how they will render in the scaffolder form UI +parameters: + - title: Fill in some steps + required: + - name + properties: + name: + title: Name + type: string + description: Unique name of the component + owner: + title: Owner + type: string + description: Owner of the component + ui:field: OwnerPicker + ui:options: + allowedKinds: + - Group + - title: Choose a location + required: + - repoUrl + properties: + repoUrl: + title: Repository Location + type: string + ui:field: RepoUrlPicker + ui:options: + allowedHosts: + - github.com +steps: + - id: fetch-base + name: Fetch Base + action: fetch:template + input: + url: ./template + values: + name: \${{parameters.name}} +`; + +type TemplateOption = { + label: string; + value: Entity; +}; + +const useStyles = makeStyles(theme => ({ + root: { + gridArea: 'pageContent', + display: 'grid', + gridTemplateAreas: ` + "controls controls" + "textArea preview" + `, + gridTemplateRows: 'auto 1fr', + gridTemplateColumns: '1fr 1fr', + }, + controls: { + gridArea: 'controls', + display: 'flex', + flexFlow: 'row nowrap', + alignItems: 'center', + margin: theme.spacing(1), + }, + textArea: { + gridArea: 'textArea', + }, + preview: { + gridArea: 'preview', + }, +})); + +export const TemplateFormPreviewer = ({ + defaultPreviewTemplate = EXAMPLE_TEMPLATE_PARAMS_YAML, + customFieldExtensions = [], + onClose, +}: { + defaultPreviewTemplate?: string; + customFieldExtensions?: FieldExtensionOptions[]; + onClose?: () => void; +}) => { + const classes = useStyles(); + const alertApi = useApi(alertApiRef); + const catalogApi = useApi(catalogApiRef); + const [selectedTemplate, setSelectedTemplate] = useState(''); + const [errorText, setErrorText] = useState(); + const [templateOptions, setTemplateOptions] = useState([]); + const [templateYaml, setTemplateYaml] = useState(defaultPreviewTemplate); + const [formState, setFormState] = useState({}); + + const { loading } = useAsync( + () => + catalogApi + .getEntities({ + filter: { kind: 'template' }, + fields: [ + 'kind', + 'metadata.namespace', + 'metadata.name', + 'metadata.title', + 'spec.parameters', + 'spec.steps', + 'spec.output', + ], + }) + .then(({ items }) => + setTemplateOptions( + items.map(template => ({ + label: + template.metadata.title ?? + humanizeEntityRef(template, { defaultKind: 'template' }), + value: template, + })), + ), + ) + .catch(e => + alertApi.post({ + message: `Error loading exisiting templates: ${e.message}`, + severity: 'error', + }), + ), + [catalogApi], + ); + + const handleSelectChange = useCallback( + selected => { + setSelectedTemplate(selected); + setTemplateYaml(yaml.stringify(selected.spec)); + }, + [setTemplateYaml], + ); + + return ( + <> + {loading && } +
+
+ + + Load Existing Template + + + + + + + +
+
+ +
+
+ +
+
+ + ); +}; diff --git a/plugins/scaffolder/src/components/TemplateEditorPage/index.ts b/plugins/scaffolder/src/components/TemplateEditorPage/index.ts index 506ff08f24..7ec6bddb64 100644 --- a/plugins/scaffolder/src/components/TemplateEditorPage/index.ts +++ b/plugins/scaffolder/src/components/TemplateEditorPage/index.ts @@ -13,4 +13,5 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + export { TemplateEditorPage } from './TemplateEditorPage'; diff --git a/plugins/scaffolder/src/components/TemplatePage/TemplatePage.test.tsx b/plugins/scaffolder/src/components/TemplatePage/TemplatePage.test.tsx index d0eca58b9d..34e6901937 100644 --- a/plugins/scaffolder/src/components/TemplatePage/TemplatePage.test.tsx +++ b/plugins/scaffolder/src/components/TemplatePage/TemplatePage.test.tsx @@ -51,6 +51,7 @@ const scaffolderApiMock: jest.Mocked = { getTask: jest.fn(), streamLogs: jest.fn(), listActions: jest.fn(), + listTasks: jest.fn(), }; const featureFlagsApiMock: jest.Mocked = { diff --git a/plugins/scaffolder/src/components/TemplatePage/TemplatePage.tsx b/plugins/scaffolder/src/components/TemplatePage/TemplatePage.tsx index 22f995eae4..f694688801 100644 --- a/plugins/scaffolder/src/components/TemplatePage/TemplatePage.tsx +++ b/plugins/scaffolder/src/components/TemplatePage/TemplatePage.tsx @@ -54,11 +54,16 @@ export const TemplatePage = ({ const secretsContext = useContext(SecretsContext); const errorApi = useApi(errorApiRef); const scaffolderApi = useApi(scaffolderApiRef); - const { templateName } = useParams(); + const { templateName, namespace } = useParams(); + const templateRef = stringifyEntityRef({ + name: templateName, + kind: 'template', + namespace, + }); const navigate = useNavigate(); const scaffolderTaskRoute = useRouteRef(scaffolderTaskRouteRef); const rootRoute = useRouteRef(rootRouteRef); - const { schema, loading, error } = useTemplateParameterSchema(templateName); + const { schema, loading, error } = useTemplateParameterSchema(templateRef); const [formState, setFormState] = useState>(() => { const query = qs.parse(window.location.search, { ignoreQueryPrefix: true, @@ -78,11 +83,7 @@ export const TemplatePage = ({ const handleCreate = async () => { const { taskId } = await scaffolderApi.scaffold({ - templateRef: stringifyEntityRef({ - name: templateName, - kind: 'template', - namespace: 'default', - }), + templateRef, values: formState, secrets: secretsContext?.secrets, }); diff --git a/plugins/scaffolder/src/components/TemplatePage/createValidator.test.ts b/plugins/scaffolder/src/components/TemplatePage/createValidator.test.ts index 047f22e44e..a5b04df71e 100644 --- a/plugins/scaffolder/src/components/TemplatePage/createValidator.test.ts +++ b/plugins/scaffolder/src/components/TemplatePage/createValidator.test.ts @@ -23,8 +23,7 @@ describe('createValidator', () => { const validators: Record> = { CustomPicker: (value, validation, _context) => { - // @ts-ignore - if (!value || !value.value) { + if (!value || !(value as { value?: unknown }).value) { validation.addError('Error !'); } }, diff --git a/plugins/scaffolder/src/components/fields/RepoUrlPicker/GerritRepoPicker.test.tsx b/plugins/scaffolder/src/components/fields/RepoUrlPicker/GerritRepoPicker.test.tsx new file mode 100644 index 0000000000..edc6732588 --- /dev/null +++ b/plugins/scaffolder/src/components/fields/RepoUrlPicker/GerritRepoPicker.test.tsx @@ -0,0 +1,78 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import { GerritRepoPicker } from './GerritRepoPicker'; +import { render, fireEvent } from '@testing-library/react'; + +describe('BitbucketRepoPicker', () => { + describe('owner input field', () => { + it('calls onChange when the owner input changes', () => { + const onChange = jest.fn(); + const { getAllByRole } = render( + , + ); + + const ownerInput = getAllByRole('textbox')[0]; + + fireEvent.change(ownerInput, { target: { value: 'test-owner' } }); + + expect(onChange).toHaveBeenCalledWith({ owner: 'test-owner' }); + }); + }); + + describe('parent field', () => { + it('calls onChange when the parent changes', () => { + const onChange = jest.fn(); + const { getAllByRole } = render( + , + ); + + const parentInput = getAllByRole('textbox')[1]; + + fireEvent.change(parentInput, { target: { value: 'test-parent' } }); + + expect(onChange).toHaveBeenCalledWith({ workspace: 'test-parent' }); + }); + }); + + describe('repoName field', () => { + it('calls onChange when the repoName changes', () => { + const onChange = jest.fn(); + const { getAllByRole } = render( + , + ); + + const repoNameInput = getAllByRole('textbox')[2]; + + fireEvent.change(repoNameInput, { target: { value: 'test-repo' } }); + + expect(onChange).toHaveBeenCalledWith({ repoName: 'test-repo' }); + }); + }); +}); diff --git a/plugins/scaffolder/src/components/fields/RepoUrlPicker/GerritRepoPicker.tsx b/plugins/scaffolder/src/components/fields/RepoUrlPicker/GerritRepoPicker.tsx new file mode 100644 index 0000000000..4cbc0859e8 --- /dev/null +++ b/plugins/scaffolder/src/components/fields/RepoUrlPicker/GerritRepoPicker.tsx @@ -0,0 +1,75 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React from 'react'; +import FormControl from '@material-ui/core/FormControl'; +import FormHelperText from '@material-ui/core/FormHelperText'; +import Input from '@material-ui/core/Input'; +import InputLabel from '@material-ui/core/InputLabel'; +import { RepoUrlPickerState } from './types'; + +export const GerritRepoPicker = (props: { + onChange: (state: RepoUrlPickerState) => void; + state: RepoUrlPickerState; + rawErrors: string[]; +}) => { + const { onChange, rawErrors, state } = props; + const { workspace, repoName, owner } = state; + return ( + <> + 0 && !workspace} + > + Owner + onChange({ owner: e.target.value })} + value={owner} + /> + The owner of the project + + 0 && !workspace} + > + Parent + onChange({ workspace: e.target.value })} + value={workspace} + /> + + The project parent that the repo will belong to + + + 0 && !repoName} + > + Repository + onChange({ repoName: e.target.value })} + value={repoName} + /> + The name of the repository + + + ); +}; diff --git a/plugins/scaffolder/src/components/fields/RepoUrlPicker/RepoUrlPicker.tsx b/plugins/scaffolder/src/components/fields/RepoUrlPicker/RepoUrlPicker.tsx index fbb7bcd821..6f762933b6 100644 --- a/plugins/scaffolder/src/components/fields/RepoUrlPicker/RepoUrlPicker.tsx +++ b/plugins/scaffolder/src/components/fields/RepoUrlPicker/RepoUrlPicker.tsx @@ -23,6 +23,7 @@ import { GithubRepoPicker } from './GithubRepoPicker'; import { GitlabRepoPicker } from './GitlabRepoPicker'; import { AzureRepoPicker } from './AzureRepoPicker'; import { BitbucketRepoPicker } from './BitbucketRepoPicker'; +import { GerritRepoPicker } from './GerritRepoPicker'; import { FieldExtensionComponentProps } from '../../../extensions'; import { RepoUrlPickerHost } from './RepoUrlPickerHost'; import { parseRepoPickerUrl, serializeRepoPickerUrl } from './utils'; @@ -42,6 +43,7 @@ export interface RepoUrlPickerUiOptions { requestUserCredentials?: { secretsKey: string; additionalScopes?: { + gerrit?: string[]; github?: string[]; gitlab?: string[]; bitbucket?: string[]; @@ -170,6 +172,13 @@ export const RepoUrlPicker = ( onChange={updateLocalState} /> )} + {hostType === 'gerrit' && ( + + )} ); }; diff --git a/plugins/scaffolder/src/index.ts b/plugins/scaffolder/src/index.ts index fc132f644c..c42dc95f9e 100644 --- a/plugins/scaffolder/src/index.ts +++ b/plugins/scaffolder/src/index.ts @@ -25,6 +25,8 @@ export type { ListActionsResponse, LogEvent, ScaffolderApi, + ScaffolderDryRunOptions, + ScaffolderDryRunResponse, ScaffolderGetIntegrationsListOptions, ScaffolderGetIntegrationsListResponse, ScaffolderOutputLink, diff --git a/plugins/scaffolder/src/lib/filesystem/MockFileSystemAccess.ts b/plugins/scaffolder/src/lib/filesystem/MockFileSystemAccess.ts new file mode 100644 index 0000000000..d466543dbd --- /dev/null +++ b/plugins/scaffolder/src/lib/filesystem/MockFileSystemAccess.ts @@ -0,0 +1,57 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { TemplateDirectoryAccess, TemplateFileAccess } from './types'; + +class MockFileAccess implements TemplateFileAccess { + constructor(readonly path: string, private content: string) {} + + async file(): Promise { + const blob = new Blob([this.content]); + return Object.assign(blob, { + name: this.path.split('/').pop()!, + lastModified: Date.now(), + webkitRelativePath: this.path, + }); + } + + async save(data: string | Blob | BufferSource): Promise { + this.content = await new Response(data).text(); + } +} + +class MockDirectoryAccess implements TemplateDirectoryAccess { + private readonly files = new Array(); + + constructor(inputFiles: Record) { + this.files = Object.entries(inputFiles).map( + ([path, content]) => new MockFileAccess(path, content), + ); + } + + async listFiles(): Promise { + return this.files; + } +} + +/** @internal */ +export class MockFileSystemAccess { + private constructor() {} + + static createMockDirectory(files: Record) { + return new MockDirectoryAccess(files); + } +} diff --git a/plugins/scaffolder/src/lib/filesystem/WebFileSystemAccess.ts b/plugins/scaffolder/src/lib/filesystem/WebFileSystemAccess.ts new file mode 100644 index 0000000000..36e7b11de2 --- /dev/null +++ b/plugins/scaffolder/src/lib/filesystem/WebFileSystemAccess.ts @@ -0,0 +1,95 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { TemplateDirectoryAccess, TemplateFileAccess } from './types'; + +type WritableFileHandle = FileSystemFileHandle & { + createWritable(): Promise<{ + write(data: string | Blob | BufferSource): Promise; + close(): Promise; + }>; +}; + +// A nicer type than the one from the TS lib +interface IterableDirectoryHandle extends FileSystemDirectoryHandle { + values(): AsyncIterable< + | ({ kind: 'file' } & WritableFileHandle) + | ({ kind: 'directory' } & IterableDirectoryHandle) + >; +} + +const showDirectoryPicker = (window as any).showDirectoryPicker as + | (() => Promise) + | undefined; + +class WebFileAccess implements TemplateFileAccess { + constructor( + readonly path: string, + private readonly handle: WritableFileHandle, + ) {} + + file(): Promise { + return this.handle.getFile(); + } + + async save(data: string | Blob | BufferSource): Promise { + const writable = await this.handle.createWritable(); + await writable.write(data); + await writable.close(); + } +} + +class WebDirectoryAccess implements TemplateDirectoryAccess { + constructor(private readonly handle: IterableDirectoryHandle) {} + + async listFiles(): Promise { + const content = []; + for await (const entry of this.listDirectoryContents(this.handle)) { + content.push(entry); + } + return content; + } + + private async *listDirectoryContents( + dirHandle: IterableDirectoryHandle, + basePath: string[] = [], + ): AsyncIterable { + for await (const handle of dirHandle.values()) { + if (handle.kind === 'file') { + yield new WebFileAccess([...basePath, handle.name].join('/'), handle); + } else if (handle.kind === 'directory') { + yield* this.listDirectoryContents(handle, [...basePath, handle.name]); + } + } + } +} + +/** @internal */ +export class WebFileSystemAccess { + static isSupported(): boolean { + return Boolean(showDirectoryPicker); + } + + static async requestDirectoryAccess(): Promise { + if (!showDirectoryPicker) { + throw new Error('File system access is not supported'); + } + const handle = await showDirectoryPicker(); + return new WebDirectoryAccess(handle); + } + + private constructor() {} +} diff --git a/plugins/scaffolder/src/lib/filesystem/helpers.ts b/plugins/scaffolder/src/lib/filesystem/helpers.ts new file mode 100644 index 0000000000..530cf87277 --- /dev/null +++ b/plugins/scaffolder/src/lib/filesystem/helpers.ts @@ -0,0 +1,27 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export async function blobToBase64(file: Blob) { + const reader = new FileReader(); + reader.readAsDataURL(file); + return new Promise((resolve, reject) => { + reader.onload = () => { + const uri = reader.result as string; + resolve(uri.slice(uri.indexOf(',') + 1)); + }; + reader.onerror = reject; + }); +} diff --git a/plugins/scaffolder/src/lib/filesystem/index.ts b/plugins/scaffolder/src/lib/filesystem/index.ts new file mode 100644 index 0000000000..1f485d0743 --- /dev/null +++ b/plugins/scaffolder/src/lib/filesystem/index.ts @@ -0,0 +1,19 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export type { TemplateFileAccess, TemplateDirectoryAccess } from './types'; +export { blobToBase64 } from './helpers'; +export { WebFileSystemAccess } from './WebFileSystemAccess'; diff --git a/plugins/scaffolder/src/lib/filesystem/types.ts b/plugins/scaffolder/src/lib/filesystem/types.ts new file mode 100644 index 0000000000..11de159b2a --- /dev/null +++ b/plugins/scaffolder/src/lib/filesystem/types.ts @@ -0,0 +1,25 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export interface TemplateFileAccess { + path: string; + file(): Promise; + save(data: string | BufferSource | Blob): Promise; +} + +export interface TemplateDirectoryAccess { + listFiles(): Promise>; +} diff --git a/plugins/scaffolder/src/next/Router/Router.test.tsx b/plugins/scaffolder/src/next/Router/Router.test.tsx index 5094c786e0..1aaca1a94b 100644 --- a/plugins/scaffolder/src/next/Router/Router.test.tsx +++ b/plugins/scaffolder/src/next/Router/Router.test.tsx @@ -47,7 +47,9 @@ describe('Router', () => { describe('/templates/:templateName', () => { it('should render the TemplateWizard page', async () => { - await renderInTestApp(, { routeEntries: ['/templates/foo'] }); + await renderInTestApp(, { + routeEntries: ['/templates/default/foo'], + }); expect(TemplateWizardPage).toHaveBeenCalled(); }); @@ -67,7 +69,7 @@ describe('Router', () => { , - { routeEntries: ['/templates/foo'] }, + { routeEntries: ['/templates/default/foo'] }, ); const mock = TemplateWizardPage as jest.Mock; diff --git a/plugins/scaffolder/src/next/TemplateListPage/TemplateCard/TemplateCard.test.tsx b/plugins/scaffolder/src/next/TemplateListPage/TemplateCard/TemplateCard.test.tsx index 45f4dc6317..44e3a0bbbf 100644 --- a/plugins/scaffolder/src/next/TemplateListPage/TemplateCard/TemplateCard.test.tsx +++ b/plugins/scaffolder/src/next/TemplateListPage/TemplateCard/TemplateCard.test.tsx @@ -233,7 +233,7 @@ describe('TemplateCard', () => { expect(getByRole('button', { name: 'Choose' })).toBeInTheDocument(); expect(getByRole('button', { name: 'Choose' })).toHaveAttribute( 'href', - '/templates/bob', + '/templates/default/bob', ); }); }); diff --git a/plugins/scaffolder/src/next/TemplateListPage/TemplateCard/TemplateCard.tsx b/plugins/scaffolder/src/next/TemplateListPage/TemplateCard/TemplateCard.tsx index 0e67ecd82a..874c20a9ac 100644 --- a/plugins/scaffolder/src/next/TemplateListPage/TemplateCard/TemplateCard.tsx +++ b/plugins/scaffolder/src/next/TemplateListPage/TemplateCard/TemplateCard.tsx @@ -26,7 +26,11 @@ import { } from '@material-ui/core'; import { CardHeader } from './CardHeader'; import { MarkdownContent, UserIcon, Button } from '@backstage/core-components'; -import { RELATION_OWNED_BY } from '@backstage/catalog-model'; +import { + parseEntityRef, + RELATION_OWNED_BY, + stringifyEntityRef, +} from '@backstage/catalog-model'; import { EntityRefLinks, getEntityRelations, @@ -91,7 +95,8 @@ export const TemplateCard = (props: TemplateCardProps) => { const styles = useStyles(); const ownedByRelations = getEntityRelations(template, RELATION_OWNED_BY); const templateRoute = useRouteRef(selectedTemplateRouteRef); - const href = templateRoute({ templateName: template.metadata.name }); + const { name, namespace } = parseEntityRef(stringifyEntityRef(template)); + const href = templateRoute({ templateName: name, namespace: namespace }); return ( diff --git a/plugins/scaffolder/src/plugin.ts b/plugins/scaffolder/src/plugin.ts index 9bbb52a71f..5ea4ae49a4 100644 --- a/plugins/scaffolder/src/plugin.ts +++ b/plugins/scaffolder/src/plugin.ts @@ -30,6 +30,7 @@ import { createRoutableExtension, discoveryApiRef, fetchApiRef, + identityApiRef, } from '@backstage/core-plugin-api'; import { OwnedEntityPicker } from './components/fields/OwnedEntityPicker/OwnedEntityPicker'; import { EntityTagsPicker } from './components/fields/EntityTagsPicker/EntityTagsPicker'; @@ -47,12 +48,14 @@ export const scaffolderPlugin = createPlugin({ discoveryApi: discoveryApiRef, scmIntegrationsApi: scmIntegrationsApiRef, fetchApi: fetchApiRef, + identityApi: identityApiRef, }, - factory: ({ discoveryApi, scmIntegrationsApi, fetchApi }) => + factory: ({ discoveryApi, scmIntegrationsApi, fetchApi, identityApi }) => new ScaffolderClient({ discoveryApi, scmIntegrationsApi, fetchApi, + identityApi, }), }), ], diff --git a/plugins/scaffolder/src/routes.ts b/plugins/scaffolder/src/routes.ts index 8ec9f9b68a..8d6875e351 100644 --- a/plugins/scaffolder/src/routes.ts +++ b/plugins/scaffolder/src/routes.ts @@ -28,10 +28,19 @@ export const rootRouteRef = createRouteRef({ id: 'scaffolder', }); +/** + * @deprecated This is the old template route, can be deleted before next major release + */ +export const legacySelectedTemplateRouteRef = createSubRouteRef({ + id: 'scaffolder/legacy/selected-template', + parent: rootRouteRef, + path: '/templates/:templateName', +}); + export const selectedTemplateRouteRef = createSubRouteRef({ id: 'scaffolder/selected-template', parent: rootRouteRef, - path: '/templates/:templateName', + path: '/templates/:namespace/:templateName', }); export const scaffolderTaskRouteRef = createSubRouteRef({ @@ -40,6 +49,12 @@ export const scaffolderTaskRouteRef = createSubRouteRef({ path: '/tasks/:taskId', }); +export const scaffolderListTaskRouteRef = createSubRouteRef({ + id: 'scaffolder/list-tasks', + parent: rootRouteRef, + path: '/tasks', +}); + export const actionsRouteRef = createSubRouteRef({ id: 'scaffolder/actions', parent: rootRouteRef, diff --git a/plugins/scaffolder/src/setupTests.ts b/plugins/scaffolder/src/setupTests.ts index 10252413a7..5499f969dc 100644 --- a/plugins/scaffolder/src/setupTests.ts +++ b/plugins/scaffolder/src/setupTests.ts @@ -19,3 +19,16 @@ import 'cross-fetch/polyfill'; const { EventSourcePolyfill } = jest.requireMock('event-source-polyfill'); global.EventSource = EventSourcePolyfill; + +// Patch jsdom to add feature used by CodeMirror +document.createRange = () => { + const range = new Range(); + + range.getClientRects = () => ({ + item: () => null, + length: 0, + [Symbol.iterator]: jest.fn(), + }); + + return range; +}; diff --git a/plugins/scaffolder/src/types.ts b/plugins/scaffolder/src/types.ts index bddd6a7b70..710af6ad23 100644 --- a/plugins/scaffolder/src/types.ts +++ b/plugins/scaffolder/src/types.ts @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { TaskSpec } from '@backstage/plugin-scaffolder-common'; +import { TaskSpec, TaskStep } from '@backstage/plugin-scaffolder-common'; import { JsonObject, JsonValue, Observable } from '@backstage/types'; import { JSONSchema7 } from 'json-schema'; @@ -149,6 +149,27 @@ export interface ScaffolderStreamLogsOptions { taskId: string; after?: number; } + +/** @public */ +export interface ScaffolderDryRunOptions { + template: JsonValue; + values: JsonObject; + secrets?: Record; + directoryContents: { path: string; base64Content: string }[]; +} + +/** @public */ +export interface ScaffolderDryRunResponse { + directoryContents: Array<{ + path: string; + base64Content: string; + executable: boolean; + }>; + log: Array>; + steps: TaskStep[]; + output: ScaffolderTaskOutput; +} + /** * An API to interact with the scaffolder backend. * @@ -171,6 +192,12 @@ export interface ScaffolderApi { getTask(taskId: string): Promise; + listTasks?({ + filterByOwnership, + }: { + filterByOwnership: 'owned' | 'all'; + }): Promise<{ tasks: ScaffolderTask[] }>; + getIntegrationsList( options: ScaffolderGetIntegrationsListOptions, ): Promise; @@ -181,4 +208,6 @@ export interface ScaffolderApi { listActions(): Promise; streamLogs(options: ScaffolderStreamLogsOptions): Observable; + + dryRun?(options: ScaffolderDryRunOptions): Promise; } diff --git a/plugins/search-backend-module-elasticsearch/CHANGELOG.md b/plugins/search-backend-module-elasticsearch/CHANGELOG.md index b5c8adaea7..a4247a2210 100644 --- a/plugins/search-backend-module-elasticsearch/CHANGELOG.md +++ b/plugins/search-backend-module-elasticsearch/CHANGELOG.md @@ -1,5 +1,62 @@ # @backstage/plugin-search-backend-module-elasticsearch +## 0.1.5 + +### Patch Changes + +- 281cec1b61: Use more precise matching for query filters +- 915700f64f: The provided search engine now adds a pagination-aware `rank` value to all results. +- ddce23d080: Now possible to set a custom index template on the elasticsearch search engine. +- 7d8acfc32e: Additional types now exported publicly: + + - ElasticSearchAgentOptions + - ElasticSearchConcreteQuery + - ElasticSearchQueryTranslator + - ElasticSearchConnectionConstructor, + - ElasticSearchTransportConstructor, + - ElasticSearchNodeOptions, + - ElasticSearchOptions, + - ElasticSearchAuth, + +- Updated dependencies + - @backstage/plugin-search-common@0.3.5 + - @backstage/plugin-search-backend-node@0.6.2 + +## 0.1.5-next.2 + +### Patch Changes + +- 7d8acfc32e: Additional types now exported publicly: + + - ElasticSearchAgentOptions + - ElasticSearchConcreteQuery + - ElasticSearchQueryTranslator + - ElasticSearchConnectionConstructor, + - ElasticSearchTransportConstructor, + - ElasticSearchNodeOptions, + - ElasticSearchOptions, + - ElasticSearchAuth, + +- Updated dependencies + - @backstage/plugin-search-common@0.3.5-next.1 + - @backstage/plugin-search-backend-node@0.6.2-next.2 + +## 0.1.5-next.1 + +### Patch Changes + +- 281cec1b61: Use more precise matching for query filters +- Updated dependencies + - @backstage/plugin-search-backend-node@0.6.2-next.1 + - @backstage/plugin-search-common@0.3.5-next.0 + +## 0.1.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-backend-node@0.6.2-next.0 + ## 0.1.4 ### Patch Changes @@ -91,10 +148,10 @@ - 022507c860: **BREAKING** The `ElasticSearchSearchEngine` implements the new stream-based indexing - process expected by the latest `@backstage/search-backend-node`. + process expected by the latest `@backstage/plugin-search-backend-node`. When updating to this version, you must also update to the latest version of - `@backstage/search-backend-node`. Check [this upgrade guide](https://backstage.io/docs/features/search/how-to-guides#how-to-migrate-from-search-alpha-to-beta) + `@backstage/plugin-search-backend-node`. Check [this upgrade guide](https://backstage.io/docs/features/search/how-to-guides#how-to-migrate-from-search-alpha-to-beta) for further details. ### Patch Changes diff --git a/plugins/search-backend-module-elasticsearch/api-report.md b/plugins/search-backend-module-elasticsearch/api-report.md index 7ef026745f..04b7f96f93 100644 --- a/plugins/search-backend-module-elasticsearch/api-report.md +++ b/plugins/search-backend-module-elasticsearch/api-report.md @@ -15,17 +15,37 @@ import { Logger } from 'winston'; import { SearchEngine } from '@backstage/plugin-search-common'; import { SearchQuery } from '@backstage/plugin-search-common'; -// Warning: (ae-missing-release-tag) "ElasticSearchClientOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// Warning: (ae-unresolved-link) The @link reference could not be resolved: The package "@backstage/plugin-search-backend-module-elasticsearch" does not have an export "ElasticSearchEngine" -// +// @public (undocumented) +export interface ElasticSearchAgentOptions { + // (undocumented) + keepAlive?: boolean; + // (undocumented) + keepAliveMsecs?: number; + // (undocumented) + maxFreeSockets?: number; + // (undocumented) + maxSockets?: number; +} + +// @public (undocumented) +export type ElasticSearchAuth = + | { + username: string; + password: string; + } + | { + apiKey: + | string + | { + id: string; + api_key: string; + }; + }; + // @public export interface ElasticSearchClientOptions { - // Warning: (ae-forgotten-export) The symbol "ElasticSearchAgentOptions" needs to be exported by the entry point index.d.ts - // // (undocumented) agent?: ElasticSearchAgentOptions | ((opts?: any) => unknown) | false; - // Warning: (ae-forgotten-export) The symbol "ElasticSearchAuth" needs to be exported by the entry point index.d.ts - // // (undocumented) auth?: ElasticSearchAuth; // (undocumented) @@ -36,8 +56,6 @@ export interface ElasticSearchClientOptions { }; // (undocumented) compression?: 'gzip'; - // Warning: (ae-forgotten-export) The symbol "ElasticSearchConnectionConstructor" needs to be exported by the entry point index.d.ts - // // (undocumented) Connection?: ElasticSearchConnectionConstructor; // (undocumented) @@ -50,8 +68,6 @@ export interface ElasticSearchClientOptions { maxRetries?: number; // (undocumented) name?: string | symbol; - // Warning: (ae-forgotten-export) The symbol "ElasticSearchNodeOptions" needs to be exported by the entry point index.d.ts - // // (undocumented) node?: | string @@ -92,12 +108,48 @@ export interface ElasticSearchClientOptions { ssl?: ConnectionOptions; // (undocumented) suggestCompression?: boolean; - // Warning: (ae-forgotten-export) The symbol "ElasticSearchTransportConstructor" needs to be exported by the entry point index.d.ts - // // (undocumented) Transport?: ElasticSearchTransportConstructor; } +// @public +export type ElasticSearchConcreteQuery = { + documentTypes?: string[]; + elasticSearchQuery: Object; + pageSize: number; +}; + +// @public (undocumented) +export interface ElasticSearchConnectionConstructor { + // (undocumented) + new (opts?: any): any; + // (undocumented) + roles: { + MASTER: string; + DATA: string; + INGEST: string; + ML: string; + }; + // (undocumented) + statuses: { + ALIVE: string; + DEAD: string; + }; +} + +// @public +export type ElasticSearchCustomIndexTemplate = { + name: string; + body: ElasticSearchCustomIndexTemplateBody; +}; + +// @public +export type ElasticSearchCustomIndexTemplateBody = { + index_patterns: string[]; + composed_of?: string[]; + template?: Record; +}; + // @public (undocumented) export type ElasticSearchHighlightConfig = { fragmentDelimiter: string; @@ -115,6 +167,41 @@ export type ElasticSearchHighlightOptions = { }; // @public (undocumented) +export interface ElasticSearchNodeOptions { + // (undocumented) + agent?: ElasticSearchAgentOptions; + // (undocumented) + headers?: Record; + // (undocumented) + id?: string; + // (undocumented) + roles?: { + master: boolean; + data: boolean; + ingest: boolean; + ml: boolean; + }; + // (undocumented) + ssl?: ConnectionOptions; + // (undocumented) + url: URL; +} + +// @public +export type ElasticSearchOptions = { + logger: Logger; + config: Config; + aliasPostfix?: string; + indexPrefix?: string; +}; + +// @public +export type ElasticSearchQueryTranslator = ( + query: SearchQuery, + options?: ElasticSearchQueryTranslatorOptions, +) => ElasticSearchConcreteQuery; + +// @public export type ElasticSearchQueryTranslatorOptions = { highlightOptions?: ElasticSearchHighlightConfig; }; @@ -128,8 +215,6 @@ export class ElasticSearchSearchEngine implements SearchEngine { logger: Logger, highlightOptions?: ElasticSearchHighlightOptions, ); - // Warning: (ae-forgotten-export) The symbol "ElasticSearchOptions" needs to be exported by the entry point index.d.ts - // // (undocumented) static fromConfig({ logger, @@ -142,22 +227,18 @@ export class ElasticSearchSearchEngine implements SearchEngine { newClient(create: (options: ElasticSearchClientOptions) => T): T; // (undocumented) query(query: SearchQuery): Promise; - // Warning: (ae-forgotten-export) The symbol "ElasticSearchQueryTranslator" needs to be exported by the entry point index.d.ts - // + // (undocumented) + setIndexTemplate(template: ElasticSearchCustomIndexTemplate): Promise; // (undocumented) setTranslator(translator: ElasticSearchQueryTranslator): void; - // Warning: (ae-forgotten-export) The symbol "ConcreteElasticSearchQuery" needs to be exported by the entry point index.d.ts - // // (undocumented) protected translator( query: SearchQuery, options?: ElasticSearchQueryTranslatorOptions, - ): ConcreteElasticSearchQuery; + ): ElasticSearchConcreteQuery; } -// Warning: (ae-missing-release-tag) "ElasticSearchSearchEngineIndexer" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export class ElasticSearchSearchEngineIndexer extends BatchSearchEngineIndexer { constructor(options: ElasticSearchSearchEngineIndexerOptions); // (undocumented) @@ -170,9 +251,7 @@ export class ElasticSearchSearchEngineIndexer extends BatchSearchEngineIndexer { initialize(): Promise; } -// Warning: (ae-missing-release-tag) "ElasticSearchSearchEngineIndexerOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export type ElasticSearchSearchEngineIndexerOptions = { type: string; indexPrefix: string; @@ -181,4 +260,17 @@ export type ElasticSearchSearchEngineIndexerOptions = { logger: Logger; elasticSearchClient: Client; }; + +// @public (undocumented) +export interface ElasticSearchTransportConstructor { + // (undocumented) + new (opts?: any): any; + // (undocumented) + sniffReasons: { + SNIFF_ON_START: string; + SNIFF_INTERVAL: string; + SNIFF_ON_CONNECTION_FAULT: string; + DEFAULT: string; + }; +} ``` diff --git a/plugins/search-backend-module-elasticsearch/package.json b/plugins/search-backend-module-elasticsearch/package.json index d06d4a8db4..719e923c5b 100644 --- a/plugins/search-backend-module-elasticsearch/package.json +++ b/plugins/search-backend-module-elasticsearch/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-search-backend-module-elasticsearch", "description": "A module for the search backend that implements search using ElasticSearch", - "version": "0.1.4", + "version": "0.1.5", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -25,8 +25,8 @@ "dependencies": { "@acuris/aws-es-connection": "^2.2.0", "@backstage/config": "^1.0.1", - "@backstage/plugin-search-backend-node": "^0.6.1", - "@backstage/plugin-search-common": "^0.3.4", + "@backstage/plugin-search-backend-node": "^0.6.2", + "@backstage/plugin-search-common": "^0.3.5", "@elastic/elasticsearch": "7.13.0", "aws-sdk": "^2.948.0", "elastic-builder": "^2.16.0", @@ -35,8 +35,8 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/backend-common": "^0.13.3", - "@backstage/cli": "^0.17.1", + "@backstage/backend-common": "^0.14.0", + "@backstage/cli": "^0.17.2", "@elastic/elasticsearch-mock": "^1.0.0" }, "files": [ diff --git a/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchClientOptions.ts b/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchClientOptions.ts index 73cb149c0a..c5d1f11b0c 100644 --- a/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchClientOptions.ts +++ b/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchClientOptions.ts @@ -18,10 +18,12 @@ import type { ConnectionOptions as TLSConnectionOptions } from 'tls'; /** * Options used to configure the `@elastic/elasticsearch` client and * are what will be passed as an argument to the - * {@link ElasticSearchEngine.newClient} method + * {@link ElasticSearchSearchEngine.newClient} method * * They are drawn from the `ClientOptions` class of `@elastic/elasticsearch`, * but are maintained separately so that this interface is not coupled to + * + * @public */ export interface ElasticSearchClientOptions { provider?: 'aws' | 'elastic'; @@ -65,6 +67,9 @@ export interface ElasticSearchClientOptions { disablePrototypePoisoningProtection?: boolean | 'proto' | 'constructor'; } +/** + * @public + */ export type ElasticSearchAuth = | { username: string; @@ -79,6 +84,9 @@ export type ElasticSearchAuth = }; }; +/** + * @public + */ export interface ElasticSearchNodeOptions { url: URL; id?: string; @@ -93,6 +101,9 @@ export interface ElasticSearchNodeOptions { }; } +/** + * @public + */ export interface ElasticSearchAgentOptions { keepAlive?: boolean; keepAliveMsecs?: number; @@ -100,6 +111,9 @@ export interface ElasticSearchAgentOptions { maxFreeSockets?: number; } +/** + * @public + */ export interface ElasticSearchConnectionConstructor { new (opts?: any): any; statuses: { @@ -113,7 +127,9 @@ export interface ElasticSearchConnectionConstructor { ML: string; }; } - +/** + * @public + */ export interface ElasticSearchTransportConstructor { new (opts?: any): any; sniffReasons: { diff --git a/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchSearchEngine.test.ts b/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchSearchEngine.test.ts index 24ceec2875..7892b731ed 100644 --- a/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchSearchEngine.test.ts +++ b/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchSearchEngine.test.ts @@ -19,7 +19,7 @@ import { ConfigReader } from '@backstage/config'; import { Client, errors } from '@elastic/elasticsearch'; import Mock from '@elastic/elasticsearch-mock'; import { - ConcreteElasticSearchQuery, + ElasticSearchConcreteQuery, decodePageCursor, ElasticSearchSearchEngine, encodePageCursor, @@ -50,6 +50,23 @@ jest.mock('./ElasticSearchSearchEngineIndexer', () => ({ .mockImplementation(() => indexerMock), })); +const customIndexTemplate = { + name: 'custom-index-template', + body: { + index_patterns: ['*'], + template: { + settings: { + number_of_shards: 1, + }, + mappings: { + _source: { + enabled: false, + }, + }, + }, + }, +}; + describe('ElasticSearchSearchEngine', () => { let testSearchEngine: ElasticSearchSearchEngine; let inspectableSearchEngine: ElasticSearchSearchEngineForTranslatorTests; @@ -72,6 +89,20 @@ describe('ElasticSearchSearchEngine', () => { client = testSearchEngine['elasticSearchClient']; }); + describe('custom index template', () => { + it('should set custom index template', async () => { + const indexTemplateSpy = jest.fn().mockReturnValue(customIndexTemplate); + mock.add( + { method: 'PUT', path: '/_index_template/custom-index-template' }, + indexTemplateSpy, + ); + await inspectableSearchEngine.setIndexTemplate(customIndexTemplate); + + expect(indexTemplateSpy).toHaveBeenCalled(); + expect(indexTemplateSpy).toHaveBeenCalledTimes(1); + }); + }); + describe('queryTranslator', () => { beforeAll(() => { mock.clearAll(); @@ -131,7 +162,7 @@ describe('ElasticSearchSearchEngine', () => { types: ['indexName'], term: 'testTerm', filters: { kind: 'testKind' }, - }) as ConcreteElasticSearchQuery; + }) as ElasticSearchConcreteQuery; expect(actualTranslatedQuery).toMatchObject({ documentTypes: ['indexName'], @@ -153,7 +184,7 @@ describe('ElasticSearchSearchEngine', () => { }, filter: { match: { - kind: 'testKind', + 'kind.keyword': 'testKind', }, }, }, @@ -170,7 +201,7 @@ describe('ElasticSearchSearchEngine', () => { types: ['indexName'], term: 'testTerm', pageCursor: 'MQ==', - }) as ConcreteElasticSearchQuery; + }) as ElasticSearchConcreteQuery; expect(actualTranslatedQuery).toMatchObject({ documentTypes: ['indexName'], @@ -204,8 +235,13 @@ describe('ElasticSearchSearchEngine', () => { const actualTranslatedQuery = translatorUnderTest({ types: ['indexName'], term: 'testTerm', - filters: { kind: 'testKind', namespace: 'testNameSpace' }, - }) as ConcreteElasticSearchQuery; + filters: { + kind: 'testKind', + namespace: 'testNameSpace', + foo: 123, + bar: true, + }, + }) as ElasticSearchConcreteQuery; expect(actualTranslatedQuery).toMatchObject({ documentTypes: ['indexName'], @@ -228,12 +264,22 @@ describe('ElasticSearchSearchEngine', () => { filter: [ { match: { - kind: 'testKind', + 'kind.keyword': 'testKind', }, }, { match: { - namespace: 'testNameSpace', + 'namespace.keyword': 'testNameSpace', + }, + }, + { + match: { + foo: '123', + }, + }, + { + match: { + bar: 'true', }, }, ], @@ -251,7 +297,7 @@ describe('ElasticSearchSearchEngine', () => { types: ['indexName'], term: 'testTerm', filters: { kind: ['testKind', 'kastTeint'] }, - }) as ConcreteElasticSearchQuery; + }) as ElasticSearchConcreteQuery; expect(actualTranslatedQuery).toMatchObject({ documentTypes: ['indexName'], @@ -312,7 +358,7 @@ describe('ElasticSearchSearchEngine', () => { fragmentDelimiter: ' ... ', }, }, - ) as ConcreteElasticSearchQuery; + ) as ElasticSearchConcreteQuery; expect(actualTranslatedQuery).toMatchObject({ documentTypes: ['indexName'], @@ -354,7 +400,7 @@ describe('ElasticSearchSearchEngine', () => { types: ['indexName'], term: 'testTerm', filters: { kind: { a: 'b' } }, - }) as ConcreteElasticSearchQuery; + }) as ElasticSearchConcreteQuery; expect(actualTranslatedQuery).toThrow(); }); }); @@ -477,6 +523,7 @@ describe('ElasticSearchSearchEngine', () => { .map((_, i) => ({ type: 'mytype', document: { value: `${i}` }, + rank: i + 1, })), ), }); @@ -521,6 +568,7 @@ describe('ElasticSearchSearchEngine', () => { .map((_, i) => ({ type: 'mytype', document: { value: `${i}` }, + rank: i + 1, })), ), nextPageCursor: 'MQ==', @@ -568,6 +616,7 @@ describe('ElasticSearchSearchEngine', () => { .map((_, i) => ({ type: 'mytype', document: { value: `${i}` }, + rank: i + 1, })) .slice(25), ), @@ -621,6 +670,7 @@ describe('ElasticSearchSearchEngine', () => { .map((_, i) => ({ type: 'mytype', document: { value: `${i}` }, + rank: i + 1, highlight: { preTag: '', postTag: '', @@ -745,6 +795,10 @@ describe('ElasticSearchSearchEngine', () => { }); describe('indexer', () => { + beforeEach(async () => { + await testSearchEngine.setIndexTemplate(customIndexTemplate); + }); + it('should get indexer', async () => { const indexer = await testSearchEngine.getIndexer('test-index'); diff --git a/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchSearchEngine.ts b/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchSearchEngine.ts index 00c25905c3..31112e3446 100644 --- a/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchSearchEngine.ts +++ b/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchSearchEngine.ts @@ -36,25 +36,69 @@ import { ElasticSearchSearchEngineIndexer } from './ElasticSearchSearchEngineInd export type { ElasticSearchClientOptions }; -export type ConcreteElasticSearchQuery = { +/** + * Elasticsearch specific index template + * @public + */ +export type ElasticSearchCustomIndexTemplate = { + name: string; + body: ElasticSearchCustomIndexTemplateBody; +}; + +/** + * Elasticsearch specific index template body + * @public + */ +export type ElasticSearchCustomIndexTemplateBody = { + /** + * Array of wildcard (*) expressions used to match the names of data streams and indices during creation. + */ + index_patterns: string[]; + /** + * An ordered list of component template names. + * Component templates are merged in the order specified, + * meaning that the last component template specified has the highest precedence. + */ + composed_of?: string[]; + /** + * See available properties of template + * https://www.elastic.co/guide/en/elasticsearch/reference/7.15/indices-put-template.html#put-index-template-api-request-body + */ + template?: Record; +}; + +/** + * Search query that the elasticsearch engine understands. + * @public + */ +export type ElasticSearchConcreteQuery = { documentTypes?: string[]; elasticSearchQuery: Object; pageSize: number; }; /** + * Options available for the Elasticsearch specific query translator. * @public */ export type ElasticSearchQueryTranslatorOptions = { highlightOptions?: ElasticSearchHighlightConfig; }; -type ElasticSearchQueryTranslator = ( +/** + * Elasticsearch specific query translator. + * @public + */ +export type ElasticSearchQueryTranslator = ( query: SearchQuery, options?: ElasticSearchQueryTranslatorOptions, -) => ConcreteElasticSearchQuery; +) => ElasticSearchConcreteQuery; -type ElasticSearchOptions = { +/** + * Options for instansiate ElasticSearchSearchEngine + * @public + */ +export type ElasticSearchOptions = { logger: Logger; config: Config; aliasPostfix?: string; @@ -161,14 +205,16 @@ export class ElasticSearchSearchEngine implements SearchEngine { protected translator( query: SearchQuery, options?: ElasticSearchQueryTranslatorOptions, - ): ConcreteElasticSearchQuery { + ): ElasticSearchConcreteQuery { const { term, filters = {}, types, pageCursor } = query; const filter = Object.entries(filters) .filter(([_, value]) => Boolean(value)) .map(([key, value]: [key: string, value: any]) => { if (['string', 'number', 'boolean'].includes(typeof value)) { - return esb.matchQuery(key, value.toString()); + // Use exact matching for string datatype fields + const keyword = typeof value === 'string' ? `${key}.keyword` : key; + return esb.matchQuery(keyword, value.toString()); } if (Array.isArray(value)) { return esb @@ -221,8 +267,18 @@ export class ElasticSearchSearchEngine implements SearchEngine { this.translator = translator; } + async setIndexTemplate(template: ElasticSearchCustomIndexTemplate) { + try { + await this.elasticSearchClient.indices.putIndexTemplate(template); + this.logger.info('Custom index template set'); + } catch (error) { + this.logger.error(`Unable to set custom index template: ${error}`); + } + } + async getIndexer(type: string) { const alias = this.constructSearchAlias(type); + const indexer = new ElasticSearchSearchEngineIndexer({ type, indexPrefix: this.indexPrefix, @@ -278,27 +334,30 @@ export class ElasticSearchSearchEngine implements SearchEngine { : undefined; return { - results: result.body.hits.hits.map((d: ElasticSearchResult) => { - const resultItem: IndexableResult = { - type: this.getTypeFromIndex(d._index), - document: d._source, - }; - - if (d.highlight) { - resultItem.highlight = { - preTag: this.highlightOptions.preTag as string, - postTag: this.highlightOptions.postTag as string, - fields: Object.fromEntries( - Object.entries(d.highlight).map(([field, fragments]) => [ - field, - fragments.join(this.highlightOptions.fragmentDelimiter), - ]), - ), + results: result.body.hits.hits.map( + (d: ElasticSearchResult, index: number) => { + const resultItem: IndexableResult = { + type: this.getTypeFromIndex(d._index), + document: d._source, + rank: pageSize * page + index + 1, }; - } - return resultItem; - }), + if (d.highlight) { + resultItem.highlight = { + preTag: this.highlightOptions.preTag as string, + postTag: this.highlightOptions.postTag as string, + fields: Object.fromEntries( + Object.entries(d.highlight).map(([field, fragments]) => [ + field, + fragments.join(this.highlightOptions.fragmentDelimiter), + ]), + ), + }; + } + + return resultItem; + }, + ), nextPageCursor, previousPageCursor, }; diff --git a/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchSearchEngineIndexer.ts b/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchSearchEngineIndexer.ts index 2a738e1506..fd16d0c963 100644 --- a/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchSearchEngineIndexer.ts +++ b/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchSearchEngineIndexer.ts @@ -20,6 +20,10 @@ import { Client } from '@elastic/elasticsearch'; import { Readable } from 'stream'; import { Logger } from 'winston'; +/** + * Options for instansiate ElasticSearchSearchEngineIndexer + * @public + */ export type ElasticSearchSearchEngineIndexerOptions = { type: string; indexPrefix: string; @@ -35,6 +39,10 @@ function duration(startTimestamp: [number, number]): string { return `${seconds.toFixed(1)}s`; } +/** + * Elasticsearch specific search engine indexer. + * @public + */ export class ElasticSearchSearchEngineIndexer extends BatchSearchEngineIndexer { private received: number = 0; private processed: number = 0; diff --git a/plugins/search-backend-module-elasticsearch/src/engines/index.ts b/plugins/search-backend-module-elasticsearch/src/engines/index.ts index c5628776ef..8ac86970a1 100644 --- a/plugins/search-backend-module-elasticsearch/src/engines/index.ts +++ b/plugins/search-backend-module-elasticsearch/src/engines/index.ts @@ -16,11 +16,22 @@ export { ElasticSearchSearchEngine } from './ElasticSearchSearchEngine'; export type { - ConcreteElasticSearchQuery, + ElasticSearchAgentOptions, + ElasticSearchConnectionConstructor, + ElasticSearchTransportConstructor, + ElasticSearchNodeOptions, + ElasticSearchAuth, +} from './ElasticSearchClientOptions'; +export type { + ElasticSearchConcreteQuery, ElasticSearchClientOptions, ElasticSearchHighlightConfig, ElasticSearchHighlightOptions, + ElasticSearchQueryTranslator, ElasticSearchQueryTranslatorOptions, + ElasticSearchOptions, + ElasticSearchCustomIndexTemplate, + ElasticSearchCustomIndexTemplateBody, } from './ElasticSearchSearchEngine'; export type { ElasticSearchSearchEngineIndexer, diff --git a/plugins/search-backend-module-elasticsearch/src/index.ts b/plugins/search-backend-module-elasticsearch/src/index.ts index 744e12f8c8..adf7fa43f2 100644 --- a/plugins/search-backend-module-elasticsearch/src/index.ts +++ b/plugins/search-backend-module-elasticsearch/src/index.ts @@ -22,10 +22,20 @@ export { ElasticSearchSearchEngine } from './engines'; export type { + ElasticSearchAgentOptions, + ElasticSearchConcreteQuery, ElasticSearchClientOptions, ElasticSearchHighlightConfig, ElasticSearchHighlightOptions, + ElasticSearchQueryTranslator, ElasticSearchQueryTranslatorOptions, + ElasticSearchConnectionConstructor, + ElasticSearchTransportConstructor, + ElasticSearchNodeOptions, + ElasticSearchOptions, + ElasticSearchAuth, ElasticSearchSearchEngineIndexer, ElasticSearchSearchEngineIndexerOptions, + ElasticSearchCustomIndexTemplate, + ElasticSearchCustomIndexTemplateBody, } from './engines'; diff --git a/plugins/search-backend-module-pg/CHANGELOG.md b/plugins/search-backend-module-pg/CHANGELOG.md index b4823f448d..fe18f8254b 100644 --- a/plugins/search-backend-module-pg/CHANGELOG.md +++ b/plugins/search-backend-module-pg/CHANGELOG.md @@ -1,5 +1,41 @@ # @backstage/plugin-search-backend-module-pg +## 0.3.4 + +### Patch Changes + +- 915700f64f: The provided search engine now adds a pagination-aware `rank` value to all results. +- Updated dependencies + - @backstage/plugin-search-common@0.3.5 + - @backstage/backend-common@0.14.0 + - @backstage/plugin-search-backend-node@0.6.2 + +## 0.3.4-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-common@0.3.5-next.1 + - @backstage/backend-common@0.14.0-next.2 + - @backstage/plugin-search-backend-node@0.6.2-next.2 + +## 0.3.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.1 + - @backstage/plugin-search-backend-node@0.6.2-next.1 + - @backstage/plugin-search-common@0.3.5-next.0 + +## 0.3.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + - @backstage/plugin-search-backend-node@0.6.2-next.0 + ## 0.3.3 ### Patch Changes @@ -83,10 +119,10 @@ - 022507c860: **BREAKING** The `PgSearchEngine` implements the new stream-based indexing process expected - by the latest `@backstage/search-backend-node`. + by the latest `@backstage/plugin-search-backend-node`. When updating to this version, you must also update to the latest version of - `@backstage/search-backend-node`. Check [this upgrade guide](https://backstage.io/docs/features/search/how-to-guides#how-to-migrate-from-search-alpha-to-beta) + `@backstage/plugin-search-backend-node`. Check [this upgrade guide](https://backstage.io/docs/features/search/how-to-guides#how-to-migrate-from-search-alpha-to-beta) for further details. ### Patch Changes diff --git a/plugins/search-backend-module-pg/package.json b/plugins/search-backend-module-pg/package.json index d570ca6b74..b34d2c65b8 100644 --- a/plugins/search-backend-module-pg/package.json +++ b/plugins/search-backend-module-pg/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-search-backend-module-pg", "description": "A module for the search backend that implements search using PostgreSQL", - "version": "0.3.3", + "version": "0.3.4", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -23,15 +23,15 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/backend-common": "^0.13.3", - "@backstage/plugin-search-backend-node": "^0.6.1", - "@backstage/plugin-search-common": "^0.3.4", + "@backstage/backend-common": "^0.14.0", + "@backstage/plugin-search-backend-node": "^0.6.2", + "@backstage/plugin-search-common": "^0.3.5", "lodash": "^4.17.21", "knex": "^1.0.2" }, "devDependencies": { - "@backstage/backend-test-utils": "^0.1.24", - "@backstage/cli": "^0.17.1" + "@backstage/backend-test-utils": "^0.1.25", + "@backstage/cli": "^0.17.2" }, "files": [ "dist", diff --git a/plugins/search-backend-module-pg/src/PgSearchEngine/PgSearchEngine.test.ts b/plugins/search-backend-module-pg/src/PgSearchEngine/PgSearchEngine.test.ts index cc89f28c80..3ed002567e 100644 --- a/plugins/search-backend-module-pg/src/PgSearchEngine/PgSearchEngine.test.ts +++ b/plugins/search-backend-module-pg/src/PgSearchEngine/PgSearchEngine.test.ts @@ -174,6 +174,7 @@ describe('PgSearchEngine', () => { location: 'location-1', }, type: 'my-type', + rank: 1, }, ], nextPageCursor: undefined, @@ -215,6 +216,7 @@ describe('PgSearchEngine', () => { location: `location-${i}`, }, type: 'my-type', + rank: i + 1, })), nextPageCursor: 'MQ==', }); @@ -257,6 +259,7 @@ describe('PgSearchEngine', () => { location: `location-${i}`, }, type: 'my-type', + rank: i + 1, })) .slice(25), previousPageCursor: 'MA==', diff --git a/plugins/search-backend-module-pg/src/PgSearchEngine/PgSearchEngine.ts b/plugins/search-backend-module-pg/src/PgSearchEngine/PgSearchEngine.ts index 445d1bb5c1..bf98c58de9 100644 --- a/plugins/search-backend-module-pg/src/PgSearchEngine/PgSearchEngine.ts +++ b/plugins/search-backend-module-pg/src/PgSearchEngine/PgSearchEngine.ts @@ -18,6 +18,7 @@ import { SearchEngine } from '@backstage/plugin-search-backend-node'; import { SearchQuery, IndexableResultSet, + IndexableResult, } from '@backstage/plugin-search-common'; import { PgSearchEngineIndexer } from './PgSearchEngineIndexer'; import { @@ -104,10 +105,13 @@ export class PgSearchEngine implements SearchEngine { ? encodePageCursor({ page: page - 1 }) : undefined; - const results = pageRows.map(({ type, document }) => ({ - type, - document, - })); + const results = pageRows.map( + ({ type, document }, index): IndexableResult => ({ + type, + document, + rank: page * pageSize + index + 1, + }), + ); return { results, nextPageCursor, previousPageCursor }; } diff --git a/plugins/search-backend-node/CHANGELOG.md b/plugins/search-backend-node/CHANGELOG.md index 1ec5f9d0e1..82f83436cd 100644 --- a/plugins/search-backend-node/CHANGELOG.md +++ b/plugins/search-backend-node/CHANGELOG.md @@ -1,5 +1,56 @@ # @backstage/plugin-search-backend-node +## 0.6.2 + +### Patch Changes + +- e7794a0aaa: propagate indexing errors so they don't appear successful to the task scheduler +- 3bb25a9acc: Introducing a `NewlineDelimitedJsonCollatorFactory`, which can be used to create search indices from newline delimited JSON files stored in external storage readable via a configured `UrlReader` instance. + + This is useful if you have an independent process periodically generating `*.ndjson` files consisting of `IndexableDocument` objects and want to be able to generate a fresh index based on the latest version of such a file. + +- 3bb25a9acc: Fixed a bug that prevented `TestPipeline.withSubject` from identifying valid `Readable` subjects that were technically transform streams. +- 915700f64f: The provided search engine now adds a pagination-aware `rank` value to all results. +- 7d8acfc32e: Replaced all `@beta` exports with `@public` exports +- Updated dependencies + - @backstage/plugin-search-common@0.3.5 + - @backstage/backend-tasks@0.3.2 + - @backstage/backend-common@0.14.0 + - @backstage/plugin-permission-common@0.6.2 + +## 0.6.2-next.2 + +### Patch Changes + +- 7d8acfc32e: Replaced all `@beta` exports with `@public` exports +- Updated dependencies + - @backstage/plugin-search-common@0.3.5-next.1 + - @backstage/backend-common@0.14.0-next.2 + - @backstage/backend-tasks@0.3.2-next.2 + +## 0.6.2-next.1 + +### Patch Changes + +- 3bb25a9acc: Introducing a `NewlineDelimitedJsonCollatorFactory`, which can be used to create search indices from newline delimited JSON files stored in external storage readable via a configured `UrlReader` instance. + + This is useful if you have an independent process periodically generating `*.ndjson` files consisting of `IndexableDocument` objects and want to be able to generate a fresh index based on the latest version of such a file. + +- 3bb25a9acc: Fixed a bug that prevented `TestPipeline.withSubject` from identifying valid `Readable` subjects that were technically transform streams. +- Updated dependencies + - @backstage/backend-tasks@0.3.2-next.1 + - @backstage/backend-common@0.13.6-next.1 + - @backstage/plugin-permission-common@0.6.2-next.0 + - @backstage/plugin-search-common@0.3.5-next.0 + +## 0.6.2-next.0 + +### Patch Changes + +- e7794a0aaa: propagate indexing errors so they don't appear successful to the task scheduler +- Updated dependencies + - @backstage/backend-tasks@0.3.2-next.0 + ## 0.6.1 ### Patch Changes diff --git a/plugins/search-backend-node/api-report.md b/plugins/search-backend-node/api-report.md index 0f379b3fc1..7649bc5d0f 100644 --- a/plugins/search-backend-node/api-report.md +++ b/plugins/search-backend-node/api-report.md @@ -5,6 +5,7 @@ ```ts /// +import { Config } from '@backstage/config'; import { DocumentCollatorFactory } from '@backstage/plugin-search-common'; import { DocumentDecoratorFactory } from '@backstage/plugin-search-common'; import { DocumentTypeInfo } from '@backstage/plugin-search-common'; @@ -12,6 +13,7 @@ import { IndexableDocument } from '@backstage/plugin-search-common'; import { IndexableResultSet } from '@backstage/plugin-search-common'; import { Logger } from 'winston'; import { default as lunr_2 } from 'lunr'; +import { Permission } from '@backstage/plugin-permission-common'; import { QueryTranslator } from '@backstage/plugin-search-common'; import { Readable } from 'stream'; import { SearchEngine } from '@backstage/plugin-search-common'; @@ -19,9 +21,10 @@ import { SearchQuery } from '@backstage/plugin-search-common'; import { TaskFunction } from '@backstage/backend-tasks'; import { TaskRunner } from '@backstage/backend-tasks'; import { Transform } from 'stream'; +import { UrlReader } from '@backstage/backend-common'; import { Writable } from 'stream'; -// @beta +// @public export abstract class BatchSearchEngineIndexer extends Writable { constructor(options: BatchSearchEngineOptions); abstract finalize(): Promise; @@ -29,19 +32,19 @@ export abstract class BatchSearchEngineIndexer extends Writable { abstract initialize(): Promise; } -// @beta (undocumented) +// @public export type BatchSearchEngineOptions = { batchSize: number; }; -// @beta (undocumented) +// @public export type ConcreteLunrQuery = { lunrQueryBuilder: lunr_2.Index.QueryBuilder; documentTypes?: string[]; pageSize: number; }; -// @beta +// @public export abstract class DecoratorBase extends Transform { constructor(); abstract decorate( @@ -51,7 +54,7 @@ export abstract class DecoratorBase extends Transform { abstract initialize(): Promise; } -// @beta (undocumented) +// @public export class IndexBuilder { constructor({ logger, searchEngine }: IndexBuilderOptions); addCollator({ factory, schedule }: RegisterCollatorParameters): void; @@ -59,22 +62,20 @@ export class IndexBuilder { build(): Promise<{ scheduler: Scheduler; }>; - // (undocumented) getDocumentTypes(): Record; - // (undocumented) getSearchEngine(): SearchEngine; } -// @beta (undocumented) +// @public export type IndexBuilderOptions = { searchEngine: SearchEngine; logger: Logger; }; -// @beta (undocumented) +// @public export type LunrQueryTranslator = (query: SearchQuery) => ConcreteLunrQuery; -// @beta (undocumented) +// @public export class LunrSearchEngine implements SearchEngine { constructor({ logger }: { logger: Logger }); // (undocumented) @@ -97,7 +98,7 @@ export class LunrSearchEngine implements SearchEngine { protected translator: QueryTranslator; } -// @beta (undocumented) +// @public export class LunrSearchEngineIndexer extends BatchSearchEngineIndexer { constructor(); // (undocumented) @@ -112,18 +113,43 @@ export class LunrSearchEngineIndexer extends BatchSearchEngineIndexer { initialize(): Promise; } -// @beta +// @public +export class NewlineDelimitedJsonCollatorFactory + implements DocumentCollatorFactory +{ + static fromConfig( + _config: Config, + options: NewlineDelimitedJsonCollatorFactoryOptions, + ): NewlineDelimitedJsonCollatorFactory; + // (undocumented) + getCollator(): Promise; + // (undocumented) + readonly type: string; + // (undocumented) + readonly visibilityPermission: Permission | undefined; +} + +// @public +export type NewlineDelimitedJsonCollatorFactoryOptions = { + type: string; + searchPattern: string; + reader: UrlReader; + logger: Logger; + visibilityPermission?: Permission; +}; + +// @public export interface RegisterCollatorParameters { factory: DocumentCollatorFactory; schedule: TaskRunner; } -// @beta +// @public export interface RegisterDecoratorParameters { factory: DocumentDecoratorFactory; } -// @beta (undocumented) +// @public export class Scheduler { constructor({ logger }: { logger: Logger }); addToSchedule({ id, task, scheduledRunner }: ScheduleTaskParameters): void; @@ -132,25 +158,22 @@ export class Scheduler { } // @public -export interface ScheduleTaskParameters { - // (undocumented) +export type ScheduleTaskParameters = { id: string; - // (undocumented) - scheduledRunner: TaskRunner; - // (undocumented) task: TaskFunction; -} + scheduledRunner: TaskRunner; +}; export { SearchEngine }; -// @beta +// @public export class TestPipeline { execute(): Promise; withDocuments(documents: IndexableDocument[]): TestPipeline; static withSubject(subject: Readable | Transform | Writable): TestPipeline; } -// @beta +// @public export type TestPipelineResult = { error: unknown; documents: IndexableDocument[]; diff --git a/plugins/search-backend-node/package.json b/plugins/search-backend-node/package.json index 0981050d29..3ebfb8782f 100644 --- a/plugins/search-backend-node/package.json +++ b/plugins/search-backend-node/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-search-backend-node", "description": "A library for Backstage backend plugins that want to interact with the search backend plugin", - "version": "0.6.1", + "version": "0.6.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -23,19 +23,24 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/backend-tasks": "^0.3.1", + "@backstage/backend-common": "^0.14.0", + "@backstage/backend-tasks": "^0.3.2", + "@backstage/config": "^1.0.1", "@backstage/errors": "^1.0.0", - "@backstage/plugin-search-common": "^0.3.4", + "@backstage/plugin-permission-common": "^0.6.2", + "@backstage/plugin-search-common": "^0.3.5", "@types/lunr": "^2.3.3", "lodash": "^4.17.21", "lunr": "^2.3.9", + "ndjson": "^2.0.0", "node-abort-controller": "^3.0.1", "uuid": "^8.3.2", "winston": "^3.2.1" }, "devDependencies": { - "@backstage/backend-common": "^0.13.3", - "@backstage/cli": "^0.17.1" + "@backstage/backend-common": "^0.14.0", + "@backstage/cli": "^0.17.2", + "@types/ndjson": "^2.0.1" }, "files": [ "dist" diff --git a/plugins/search-backend-node/src/IndexBuilder.ts b/plugins/search-backend-node/src/IndexBuilder.ts index 2131c6937d..3e1dae1c2f 100644 --- a/plugins/search-backend-node/src/IndexBuilder.ts +++ b/plugins/search-backend-node/src/IndexBuilder.ts @@ -28,7 +28,8 @@ import { } from './types'; /** - * @beta + * Used for adding collators, decorators and compile them into tasks which are added to a scheduler returned to the caller. + * @public */ export class IndexBuilder { private collators: Record; @@ -45,10 +46,16 @@ export class IndexBuilder { this.searchEngine = searchEngine; } + /** + * Responsible for returning the registered search engine. + */ getSearchEngine(): SearchEngine { return this.searchEngine; } + /** + * Responsible for returning the registered document types. + */ getDocumentTypes(): Record { return this.documentTypes; } diff --git a/plugins/search-backend-node/src/Scheduler.ts b/plugins/search-backend-node/src/Scheduler.ts index 9f3d95b112..404823c53a 100644 --- a/plugins/search-backend-node/src/Scheduler.ts +++ b/plugins/search-backend-node/src/Scheduler.ts @@ -24,16 +24,18 @@ type TaskEnvelope = { }; /** - * @public ScheduleTaskParameters + * ScheduleTaskParameters + * @public */ -export interface ScheduleTaskParameters { +export type ScheduleTaskParameters = { id: string; task: TaskFunction; scheduledRunner: TaskRunner; -} +}; /** - * @beta + * Scheduler responsible for all search tasks. + * @public */ export class Scheduler { private logger: Logger; diff --git a/plugins/search-backend-node/src/collators/NewlineDelimitedJsonCollatorFactory.test.ts b/plugins/search-backend-node/src/collators/NewlineDelimitedJsonCollatorFactory.test.ts new file mode 100644 index 0000000000..198995b3cf --- /dev/null +++ b/plugins/search-backend-node/src/collators/NewlineDelimitedJsonCollatorFactory.test.ts @@ -0,0 +1,157 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + getVoidLogger, + ReadUrlResponse, + UrlReader, + UrlReaders, +} from '@backstage/backend-common'; +import { ConfigReader } from '@backstage/config'; +import { Readable } from 'stream'; +import { NewlineDelimitedJsonCollatorFactory } from './NewlineDelimitedJsonCollatorFactory'; +import { TestPipeline } from '../test-utils'; + +describe('DefaultCatalogCollatorFactory', () => { + const config = new ConfigReader({}); + const logger = getVoidLogger(); + + it('has expected type', () => { + const factory = NewlineDelimitedJsonCollatorFactory.fromConfig(config, { + type: 'expected-type', + searchPattern: 'test://folder/prefix-*', + logger, + reader: UrlReaders.default({ logger, config }), + }); + expect(factory.type).toBe('expected-type'); + }); + + describe('getCollator', () => { + let readable: Readable; + let reader: jest.Mocked< + UrlReader & { readUrl: jest.Mock> } + >; + let factory: NewlineDelimitedJsonCollatorFactory; + + beforeEach(async () => { + jest.clearAllMocks(); + + readable = new Readable(); + readable._read = () => {}; + reader = { + search: jest.fn(), + read: jest.fn(), + readTree: jest.fn(), + readUrl: jest.fn(), + }; + factory = NewlineDelimitedJsonCollatorFactory.fromConfig(config, { + type: 'expected-type', + searchPattern: 'test://folder/prefix-*', + logger, + reader: UrlReaders.create({ + logger, + config, + factories: [() => [{ predicate: () => true, reader }]], + }), + }); + }); + + it('throws if url reader throws an error during search', async () => { + reader.search.mockRejectedValue(new Error('Expected error')); + + await expect(() => factory.getCollator()).rejects.toThrowError( + 'Expected error', + ); + }); + + it('throws if no matching files are found', async () => { + reader.search.mockResolvedValue({ files: [], etag: '' }); + + await expect(() => factory.getCollator()).rejects.toThrowError( + 'Could not find an .ndjson file matching', + ); + }); + + it('throws if matching file is not .ndjson', async () => { + reader.search.mockResolvedValue({ + files: [{ url: 'test://folder/prefix-1.avro', content: jest.fn() }], + etag: '', + }); + reader.readUrl.mockResolvedValue({ + buffer: jest.fn(), + stream: jest.fn().mockReturnValue(readable), + }); + + await expect(() => factory.getCollator()).rejects.toThrowError( + 'Could not find an .ndjson file matching', + ); + }); + + it('gets stream using latest matched url', async () => { + reader.search.mockResolvedValue({ + files: [ + { url: 'test://folder/prefix-1.ndjson', content: jest.fn() }, + { url: 'test://folder/prefix-2.ndjson', content: jest.fn() }, + ], + etag: '', + }); + reader.readUrl.mockResolvedValue({ + buffer: jest.fn(), + stream: jest.fn().mockReturnValue(readable), + }); + + await factory.getCollator(); + + expect(reader.search).toHaveBeenCalledWith( + 'test://folder/prefix-*', + undefined, + ); + expect(reader.readUrl).toHaveBeenCalledWith( + 'test://folder/prefix-2.ndjson', + undefined, + ); + }); + + it('transforms newline delimited json into readable stream of documents', async () => { + reader.search.mockResolvedValue({ + files: [{ url: 'test://folder/prefix-1.ndjson', content: jest.fn() }], + etag: '', + }); + reader.readUrl.mockResolvedValue({ + buffer: jest.fn(), + stream: jest + .fn() + .mockReturnValue( + Readable.from( + '{"title": "Title 1", "location": "/title-1", "text": "text 1"}\n{"title": "Title 2", "location": "/title-2", "text": "text 2"}', + ), + ), + }); + + const collator = await factory.getCollator(); + const pipeline = TestPipeline.withSubject(collator); + const { documents } = await pipeline.execute(); + + expect(documents).toHaveLength(2); + expect(documents[0].title).toBe('Title 1'); + expect(documents[0].location).toBe('/title-1'); + expect(documents[0].text).toBe('text 1'); + expect(documents[1].title).toBe('Title 2'); + expect(documents[1].location).toBe('/title-2'); + expect(documents[1].text).toBe('text 2'); + }); + }); +}); diff --git a/plugins/search-backend-node/src/collators/NewlineDelimitedJsonCollatorFactory.ts b/plugins/search-backend-node/src/collators/NewlineDelimitedJsonCollatorFactory.ts new file mode 100644 index 0000000000..fde6b9412a --- /dev/null +++ b/plugins/search-backend-node/src/collators/NewlineDelimitedJsonCollatorFactory.ts @@ -0,0 +1,145 @@ +/* + * 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 { UrlReader } from '@backstage/backend-common'; +import { Config } from '@backstage/config'; +import { Permission } from '@backstage/plugin-permission-common'; +import { DocumentCollatorFactory } from '@backstage/plugin-search-common'; +import { parse as parseNdjson } from 'ndjson'; +import { Readable } from 'stream'; +import { Logger } from 'winston'; + +/** + * Options for instansiate NewlineDelimitedJsonCollatorFactory + * @public + */ +export type NewlineDelimitedJsonCollatorFactoryOptions = { + type: string; + searchPattern: string; + reader: UrlReader; + logger: Logger; + visibilityPermission?: Permission; +}; + +/** + * Factory class producing a collator that can be used to index documents + * sourced from the latest newline delimited JSON file matching a given search + * pattern. "Latest" is determined by the name of the file (last alphabetically + * is considered latest). + * + * @remarks + * The reader provided must implement the `search()` method as well as the + * `readUrl` method whose response includes the `stream()` method. Naturally, + * the reader must also be configured to understand the given search pattern. + * + * @example + * Here's an example configuration using Google Cloud Storage, which would + * return the latest file under the `bucket` GCS bucket with files like + * `xyz-2021.ndjson` or `xyz-2022.ndjson`. + * ```ts + * indexBuilder.addCollator({ + * schedule, + * factory: NewlineDelimitedJsonCollatorFactory.fromConfig(env.config, { + * type: 'techdocs', + * searchPattern: 'https://storage.cloud.google.com/bucket/xyz-*', + * reader: env.reader, + * logger: env.logger, + * }) + * }); + * ``` + * + * @public + */ +export class NewlineDelimitedJsonCollatorFactory + implements DocumentCollatorFactory +{ + readonly type: string; + + public readonly visibilityPermission: Permission | undefined; + + private constructor( + type: string, + private readonly searchPattern: string, + private readonly reader: UrlReader, + private readonly logger: Logger, + visibilityPermission: Permission | undefined, + ) { + this.type = type; + this.visibilityPermission = visibilityPermission; + } + + /** + * Returns a NewlineDelimitedJsonCollatorFactory instance from configuration + * and a set of options. + */ + static fromConfig( + _config: Config, + options: NewlineDelimitedJsonCollatorFactoryOptions, + ): NewlineDelimitedJsonCollatorFactory { + return new NewlineDelimitedJsonCollatorFactory( + options.type, + options.searchPattern, + options.reader, + options.logger, + options.visibilityPermission, + ); + } + + /** + * Returns the "latest" URL for the given search pattern (e.g. the one at the + * end of the list, sorted alphabetically). + */ + private async lastUrl(): Promise { + try { + // Search for files matching the given pattern, then sort/reverse. The + // first item in the list will be the "latest" file. + this.logger.info( + `Attempting to find latest .ndjson matching ${this.searchPattern}`, + ); + const { files } = await this.reader.search(this.searchPattern); + const candidates = files + .filter(file => file.url.endsWith('.ndjson')) + .sort((a, b) => a.url.localeCompare(b.url)) + .reverse(); + + return candidates[0]?.url; + } catch (e) { + this.logger.error(`Could not search for ${this.searchPattern}`, e); + throw e; + } + } + + async getCollator(): Promise { + // Search for files matching the given pattern. + const lastUrl = await this.lastUrl(); + + // Abort if no such file could be found. + if (!lastUrl) { + const noMatchingFile = `Could not find an .ndjson file matching ${this.searchPattern}`; + this.logger.error(noMatchingFile); + throw new Error(noMatchingFile); + } else { + this.logger.info(`Using latest .ndjson file ${lastUrl}`); + } + + // Use the UrlReader to try and stream the file. + const readerResponse = await this.reader.readUrl!(lastUrl); + const stream = readerResponse.stream!(); + + // Use ndjson's parser to turn the raw file into an object-mode stream. + return stream.pipe(parseNdjson()); + } +} diff --git a/plugins/search-backend-node/src/collators/index.ts b/plugins/search-backend-node/src/collators/index.ts new file mode 100644 index 0000000000..82598d2727 --- /dev/null +++ b/plugins/search-backend-node/src/collators/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 type { NewlineDelimitedJsonCollatorFactoryOptions } from './NewlineDelimitedJsonCollatorFactory'; + +export { NewlineDelimitedJsonCollatorFactory } from './NewlineDelimitedJsonCollatorFactory'; diff --git a/plugins/search-backend-node/src/engines/LunrSearchEngine.test.ts b/plugins/search-backend-node/src/engines/LunrSearchEngine.test.ts index 84b1a35165..098d1e8b16 100644 --- a/plugins/search-backend-node/src/engines/LunrSearchEngine.test.ts +++ b/plugins/search-backend-node/src/engines/LunrSearchEngine.test.ts @@ -431,6 +431,7 @@ describe('LunrSearchEngine', () => { location: 'test/location', }, type: 'test-index', + rank: 1, }, ], nextPageCursor: undefined, @@ -469,6 +470,7 @@ describe('LunrSearchEngine', () => { text: 'testText', location: 'test/location', }, + rank: 1, }, ], nextPageCursor: undefined, @@ -521,6 +523,7 @@ describe('LunrSearchEngine', () => { location: `${highlightTags.pre}test/location${highlightTags.post}`, }, }, + rank: 1, }, ], nextPageCursor: undefined, @@ -559,6 +562,7 @@ describe('LunrSearchEngine', () => { text: 'testText', location: 'test/location', }, + rank: 1, }, ], nextPageCursor: undefined, @@ -598,6 +602,7 @@ describe('LunrSearchEngine', () => { text: 'testText', location: 'test/location', }, + rank: 1, }, ], nextPageCursor: undefined, @@ -637,6 +642,7 @@ describe('LunrSearchEngine', () => { text: 'Hello World.', location: 'test/location', }, + rank: 1, }, ], nextPageCursor: undefined, @@ -676,6 +682,7 @@ describe('LunrSearchEngine', () => { text: 'Searching', location: 'test/location', }, + rank: 1, }, ], nextPageCursor: undefined, @@ -722,6 +729,7 @@ describe('LunrSearchEngine', () => { text: 'testText', location: 'test/location2', }, + rank: 1, }, ], nextPageCursor: undefined, @@ -777,6 +785,7 @@ describe('LunrSearchEngine', () => { location: 'test/location2', extraField: 'testExtraField', }, + rank: 1, }, ], nextPageCursor: undefined, @@ -823,6 +832,7 @@ describe('LunrSearchEngine', () => { text: 'testText', location: 'test:location2', }, + rank: 1, }, ], nextPageCursor: undefined, @@ -886,6 +896,7 @@ describe('LunrSearchEngine', () => { text: 'testText', title: 'testTitle', }, + rank: 1, }, { document: { @@ -893,6 +904,7 @@ describe('LunrSearchEngine', () => { text: 'testText', title: 'testTitle', }, + rank: 2, }, ], nextPageCursor: undefined, @@ -931,6 +943,7 @@ describe('LunrSearchEngine', () => { location: `test/location/${i}`, }, type: 'test-index', + rank: i + 1, })), nextPageCursor: 'MQ==', previousPageCursor: undefined, @@ -968,6 +981,7 @@ describe('LunrSearchEngine', () => { location: `test/location/${i}`, }, type: 'test-index', + rank: i + 1, })) .slice(25), nextPageCursor: undefined, diff --git a/plugins/search-backend-node/src/engines/LunrSearchEngine.ts b/plugins/search-backend-node/src/engines/LunrSearchEngine.ts index 656cd3e1eb..03cb421653 100644 --- a/plugins/search-backend-node/src/engines/LunrSearchEngine.ts +++ b/plugins/search-backend-node/src/engines/LunrSearchEngine.ts @@ -27,7 +27,8 @@ import { Logger } from 'winston'; import { LunrSearchEngineIndexer } from './LunrSearchEngineIndexer'; /** - * @beta + * Type of translated query for the Lunr Search Engine. + * @public */ export type ConcreteLunrQuery = { lunrQueryBuilder: lunr.Index.QueryBuilder; @@ -41,12 +42,14 @@ type LunrResultEnvelope = { }; /** - * @beta + * Translator repsonsible for translating search term and filters to a query that the Lunr Search Engine understands. + * @public */ export type LunrQueryTranslator = (query: SearchQuery) => ConcreteLunrQuery; /** - * @beta + * Lunr specific search engine implementation. + * @public */ export class LunrSearchEngine implements SearchEngine { protected lunrIndices: Record = {}; @@ -204,9 +207,10 @@ export class LunrSearchEngine implements SearchEngine { // Translate results into IndexableResultSet const realResultSet: IndexableResultSet = { - results: results.slice(offset, offset + pageSize).map(d => ({ + results: results.slice(offset, offset + pageSize).map((d, index) => ({ type: d.type, document: this.docStore[d.result.ref], + rank: page * pageSize + index + 1, highlight: { preTag: this.highlightPreTag, postTag: this.highlightPostTag, diff --git a/plugins/search-backend-node/src/engines/LunrSearchEngineIndexer.ts b/plugins/search-backend-node/src/engines/LunrSearchEngineIndexer.ts index 4da27be94b..e723957077 100644 --- a/plugins/search-backend-node/src/engines/LunrSearchEngineIndexer.ts +++ b/plugins/search-backend-node/src/engines/LunrSearchEngineIndexer.ts @@ -19,7 +19,8 @@ import lunr from 'lunr'; import { BatchSearchEngineIndexer } from '../indexing'; /** - * @beta + * Lunr specific search engine indexer + * @public */ export class LunrSearchEngineIndexer extends BatchSearchEngineIndexer { private schemaInitialized = false; diff --git a/plugins/search-backend-node/src/index.ts b/plugins/search-backend-node/src/index.ts index e5202ac640..d342bb9259 100644 --- a/plugins/search-backend-node/src/index.ts +++ b/plugins/search-backend-node/src/index.ts @@ -22,6 +22,7 @@ export { IndexBuilder } from './IndexBuilder'; export { Scheduler } from './Scheduler'; +export * from './collators'; export { LunrSearchEngine } from './engines'; export type { ConcreteLunrQuery, diff --git a/plugins/search-backend-node/src/indexing/BatchSearchEngineIndexer.ts b/plugins/search-backend-node/src/indexing/BatchSearchEngineIndexer.ts index 6070a577ed..24b90c2a59 100644 --- a/plugins/search-backend-node/src/indexing/BatchSearchEngineIndexer.ts +++ b/plugins/search-backend-node/src/indexing/BatchSearchEngineIndexer.ts @@ -19,7 +19,8 @@ import { IndexableDocument } from '@backstage/plugin-search-common'; import { Writable } from 'stream'; /** - * @beta + * Options for {@link BatchSearchEngineIndexer} + * @public */ export type BatchSearchEngineOptions = { batchSize: number; @@ -28,7 +29,7 @@ export type BatchSearchEngineOptions = { /** * Base class encapsulating batch-based stream processing. Useful as a base * class for search engine indexers. - * @beta + * @public */ export abstract class BatchSearchEngineIndexer extends Writable { private batchSize: number; diff --git a/plugins/search-backend-node/src/indexing/DecoratorBase.ts b/plugins/search-backend-node/src/indexing/DecoratorBase.ts index 0541ab82d7..bcf5729674 100644 --- a/plugins/search-backend-node/src/indexing/DecoratorBase.ts +++ b/plugins/search-backend-node/src/indexing/DecoratorBase.ts @@ -21,7 +21,7 @@ import { Transform } from 'stream'; /** * Base class encapsulating simple async transformations. Useful as a base * class for Backstage search decorators. - * @beta + * @public */ export abstract class DecoratorBase extends Transform { private initialized: Promise; diff --git a/plugins/search-backend-node/src/test-utils/TestPipeline.ts b/plugins/search-backend-node/src/test-utils/TestPipeline.ts index dd90de4f38..00dd9b3755 100644 --- a/plugins/search-backend-node/src/test-utils/TestPipeline.ts +++ b/plugins/search-backend-node/src/test-utils/TestPipeline.ts @@ -19,7 +19,7 @@ import { pipeline, Readable, Transform, Writable } from 'stream'; /** * Object resolved after a test pipeline is executed. - * @beta + * @public */ export type TestPipelineResult = { /** @@ -37,7 +37,7 @@ export type TestPipelineResult = { /** * Test utility for Backstage Search collators, decorators, and indexers. - * @beta + * @public */ export class TestPipeline { private collator?: Readable; @@ -66,14 +66,14 @@ export class TestPipeline { return new TestPipeline({ decorator: subject }); } - if (subject instanceof Readable) { - return new TestPipeline({ collator: subject }); - } - if (subject instanceof Writable) { return new TestPipeline({ indexer: subject }); } + if (subject.readable || subject instanceof Readable) { + return new TestPipeline({ collator: subject }); + } + throw new Error( 'Unknown test subject: are you passing a readable, writable, or transform stream?', ); diff --git a/plugins/search-backend-node/src/types.ts b/plugins/search-backend-node/src/types.ts index a92398e8c8..dfcf4d12ce 100644 --- a/plugins/search-backend-node/src/types.ts +++ b/plugins/search-backend-node/src/types.ts @@ -23,7 +23,8 @@ import { import { Logger } from 'winston'; /** - * @beta + * Options required to instantiate the index builder. + * @public */ export type IndexBuilderOptions = { searchEngine: SearchEngine; @@ -32,7 +33,7 @@ export type IndexBuilderOptions = { /** * Parameters required to register a collator. - * @beta + * @public */ export interface RegisterCollatorParameters { /** @@ -48,7 +49,7 @@ export interface RegisterCollatorParameters { /** * Parameters required to register a decorator - * @beta + * @public */ export interface RegisterDecoratorParameters { /** diff --git a/plugins/search-backend/CHANGELOG.md b/plugins/search-backend/CHANGELOG.md index 257748f6a6..e214d6bb16 100644 --- a/plugins/search-backend/CHANGELOG.md +++ b/plugins/search-backend/CHANGELOG.md @@ -1,5 +1,53 @@ # @backstage/plugin-search-backend +## 0.5.3 + +### Patch Changes + +- 7d8acfc32e: `RouterOptions` and `createRouter` now marked as public exports +- 915700f64f: The provided search engine now adds a pagination-aware `rank` value to all results. +- Updated dependencies + - @backstage/plugin-search-common@0.3.5 + - @backstage/plugin-permission-node@0.6.2 + - @backstage/backend-common@0.14.0 + - @backstage/plugin-auth-node@0.2.2 + - @backstage/plugin-search-backend-node@0.6.2 + - @backstage/plugin-permission-common@0.6.2 + +## 0.5.3-next.2 + +### Patch Changes + +- 7d8acfc32e: `RouterOptions` and `createRouter` now marked as public exports +- Updated dependencies + - @backstage/plugin-search-common@0.3.5-next.1 + - @backstage/backend-common@0.14.0-next.2 + - @backstage/plugin-search-backend-node@0.6.2-next.2 + - @backstage/plugin-auth-node@0.2.2-next.2 + - @backstage/plugin-permission-node@0.6.2-next.2 + +## 0.5.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.1 + - @backstage/plugin-auth-node@0.2.2-next.1 + - @backstage/plugin-permission-common@0.6.2-next.0 + - @backstage/plugin-permission-node@0.6.2-next.1 + - @backstage/plugin-search-backend-node@0.6.2-next.1 + - @backstage/plugin-search-common@0.3.5-next.0 + +## 0.5.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + - @backstage/plugin-search-backend-node@0.6.2-next.0 + - @backstage/plugin-auth-node@0.2.2-next.0 + - @backstage/plugin-permission-node@0.6.2-next.0 + ## 0.5.2 ### Patch Changes diff --git a/plugins/search-backend/api-report.md b/plugins/search-backend/api-report.md index c5c8eb7a13..eed61069da 100644 --- a/plugins/search-backend/api-report.md +++ b/plugins/search-backend/api-report.md @@ -11,13 +11,9 @@ import { PermissionAuthorizer } from '@backstage/plugin-permission-common'; import { PermissionEvaluator } from '@backstage/plugin-permission-common'; import { SearchEngine } from '@backstage/plugin-search-backend-node'; -// Warning: (ae-missing-release-tag) "createRouter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public (undocumented) export function createRouter(options: RouterOptions): Promise; -// Warning: (ae-missing-release-tag) "RouterOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public (undocumented) export type RouterOptions = { engine: SearchEngine; diff --git a/plugins/search-backend/package.json b/plugins/search-backend/package.json index 173f885847..6daa65bcb4 100644 --- a/plugins/search-backend/package.json +++ b/plugins/search-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-search-backend", "description": "The Backstage backend plugin that provides your backstage app with search", - "version": "0.5.2", + "version": "0.5.3", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -23,14 +23,14 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/backend-common": "^0.13.3", + "@backstage/backend-common": "^0.14.0", "@backstage/config": "^1.0.1", "@backstage/errors": "^1.0.0", - "@backstage/plugin-auth-node": "^0.2.1", - "@backstage/plugin-permission-common": "^0.6.1", - "@backstage/plugin-permission-node": "^0.6.1", - "@backstage/plugin-search-backend-node": "^0.6.1", - "@backstage/plugin-search-common": "^0.3.4", + "@backstage/plugin-auth-node": "^0.2.2", + "@backstage/plugin-permission-common": "^0.6.2", + "@backstage/plugin-permission-node": "^0.6.2", + "@backstage/plugin-search-backend-node": "^0.6.2", + "@backstage/plugin-search-common": "^0.3.5", "@backstage/types": "^1.0.0", "@types/express": "^4.17.6", "dataloader": "^2.0.0", @@ -43,7 +43,7 @@ "zod": "^3.11.6" }, "devDependencies": { - "@backstage/cli": "^0.17.1", + "@backstage/cli": "^0.17.2", "@types/supertest": "^2.0.8", "supertest": "^6.1.3" }, diff --git a/plugins/search-backend/src/service/AuthorizedSearchEngine.test.ts b/plugins/search-backend/src/service/AuthorizedSearchEngine.test.ts index 256608fd82..b2355a7239 100644 --- a/plugins/search-backend/src/service/AuthorizedSearchEngine.test.ts +++ b/plugins/search-backend/src/service/AuthorizedSearchEngine.test.ts @@ -44,6 +44,7 @@ describe('AuthorizedSearchEngine', () => { .fill(0) .map((_, index) => ({ type, + rank: index + 1, document: { title: `${type}_doc_${index}`, authorization: withAuthorization @@ -261,9 +262,11 @@ describe('AuthorizedSearchEngine', () => { }), ); + const expectedResult = { ...usersWithAuth[8], rank: 1 }; + await expect( authorizedSearchEngine.query({ term: '' }, options), - ).resolves.toEqual({ results: [usersWithAuth[8]] }); + ).resolves.toEqual({ results: [expectedResult] }); expect(mockedQuery).toHaveBeenCalledWith( { term: '', types: ['users'] }, @@ -275,6 +278,7 @@ describe('AuthorizedSearchEngine', () => { const searchResults = [ { type: 'templates', + rank: 1, document: { title: `doc_0_a`, authorization: { resourceRef: `template_doc_0` }, @@ -282,6 +286,7 @@ describe('AuthorizedSearchEngine', () => { }, { type: 'templates', + rank: 2, document: { title: `doc_0_b`, authorization: { resourceRef: `template_doc_0` }, @@ -419,7 +424,9 @@ describe('AuthorizedSearchEngine', () => { { token: 'token' }, ); - const expectedResult = allDocuments.slice(0, 25); + const expectedResult = allDocuments + .slice(0, 25) + .map((r, i) => ({ ...r, rank: i + 1 })); const expectedFirstRequestCursor = 'MQ=='; expect(result).toEqual({ @@ -510,7 +517,8 @@ describe('AuthorizedSearchEngine', () => { const expectedResult = allDocuments .filter(d => d.type !== typeServices) - .slice(0, 25); + .slice(0, 25) + .map((d, i) => ({ ...d, rank: i + 1 })); const expectedFirstRequestCursor = 'MQ=='; expect(result).toEqual({ @@ -587,8 +595,12 @@ describe('AuthorizedSearchEngine', () => { { token: 'token' }, ); + const expectedResults = servicesWithAuth + .slice(5) + .map((r, i) => ({ ...r, rank: 25 + i + 1 })); + expect(result).toEqual({ - results: servicesWithAuth.slice(5), + results: expectedResults, previousPageCursor: encodePageCursor({ page: 0 }), }); }); diff --git a/plugins/search-backend/src/service/AuthorizedSearchEngine.ts b/plugins/search-backend/src/service/AuthorizedSearchEngine.ts index a74cdf8eef..ade43280be 100644 --- a/plugins/search-backend/src/service/AuthorizedSearchEngine.ts +++ b/plugins/search-backend/src/service/AuthorizedSearchEngine.ts @@ -189,10 +189,15 @@ export class AuthorizedSearchEngine implements SearchEngine { ); return { - results: filteredResults.slice( - page * this.pageSize, - (page + 1) * this.pageSize, - ), + results: filteredResults + .slice(page * this.pageSize, (page + 1) * this.pageSize) + .map((result, index) => { + // Overwrite any/all rank entries to avoid leaking knowledge of filtered results. + return { + ...result, + rank: page * this.pageSize + index + 1, + }; + }), previousPageCursor: page === 0 ? undefined : encodePageCursor({ page: page - 1 }), nextPageCursor: diff --git a/plugins/search-backend/src/service/router.ts b/plugins/search-backend/src/service/router.ts index e42a31981c..f54c2d388f 100644 --- a/plugins/search-backend/src/service/router.ts +++ b/plugins/search-backend/src/service/router.ts @@ -51,6 +51,9 @@ const jsonObjectSchema: z.ZodSchema = z.lazy(() => { return z.record(jsonValueSchema); }); +/** + * @public + */ export type RouterOptions = { engine: SearchEngine; types: Record; @@ -61,6 +64,9 @@ export type RouterOptions = { const allowedLocationProtocols = ['http:', 'https:']; +/** + * @public + */ export async function createRouter( options: RouterOptions, ): Promise { diff --git a/plugins/search-common/CHANGELOG.md b/plugins/search-common/CHANGELOG.md index 361552ef09..53a8afb0bf 100644 --- a/plugins/search-common/CHANGELOG.md +++ b/plugins/search-common/CHANGELOG.md @@ -1,5 +1,27 @@ # @backstage/plugin-search-common +## 0.3.5 + +### Patch Changes + +- 7d8acfc32e: `@beta` exports now replaced with `@public` exports +- 484afdf1dc: Added an optional `rank` attribute to the `Result` type. This represents the result rank (starting at 1) for a given result in a result set for a given search. +- Updated dependencies + - @backstage/plugin-permission-common@0.6.2 + +## 0.3.5-next.1 + +### Patch Changes + +- 7d8acfc32e: `@beta` exports now replaced with `@public` exports + +## 0.3.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-common@0.6.2-next.0 + ## 0.3.4 ### Patch Changes diff --git a/plugins/search-common/api-report.md b/plugins/search-common/api-report.md index 495d77ed26..ea9fc2d26e 100644 --- a/plugins/search-common/api-report.md +++ b/plugins/search-common/api-report.md @@ -11,56 +11,54 @@ import { Readable } from 'stream'; import { Transform } from 'stream'; import { Writable } from 'stream'; -// @beta +// @public export interface DocumentCollatorFactory { getCollator(): Promise; readonly type: string; readonly visibilityPermission?: Permission; } -// @beta +// @public export interface DocumentDecoratorFactory { getDecorator(): Promise; readonly types?: string[]; } -// @beta +// @public export type DocumentTypeInfo = { visibilityPermission?: Permission; }; -// @beta +// @public export type IndexableDocument = SearchDocument & { authorization?: { resourceRef: string; }; }; -// @beta (undocumented) +// @public (undocumented) export type IndexableResult = Result; -// @beta (undocumented) +// @public (undocumented) export type IndexableResultSet = ResultSet; -// @beta +// @public export type QueryRequestOptions = { token?: string; }; -// @beta +// @public export type QueryTranslator = (query: SearchQuery) => unknown; -// @beta (undocumented) +// @public (undocumented) export interface Result { - // (undocumented) document: TDocument; - // (undocumented) highlight?: ResultHighlight; - // (undocumented) + rank?: number; type: string; } -// @beta +// @public export interface ResultHighlight { // (undocumented) fields: { @@ -70,7 +68,7 @@ export interface ResultHighlight { preTag: string; } -// @beta (undocumented) +// @public (undocumented) export interface ResultSet { // (undocumented) nextPageCursor?: string; @@ -80,14 +78,14 @@ export interface ResultSet { results: Result[]; } -// @beta +// @public export interface SearchDocument { location: string; text: string; title: string; } -// @beta +// @public export interface SearchEngine { getIndexer(type: string): Promise; query( @@ -97,7 +95,7 @@ export interface SearchEngine { setTranslator(translator: QueryTranslator): void; } -// @beta (undocumented) +// @public (undocumented) export interface SearchQuery { // (undocumented) filters?: JsonObject; @@ -109,9 +107,9 @@ export interface SearchQuery { types?: string[]; } -// @beta (undocumented) +// @public (undocumented) export type SearchResult = Result; -// @beta (undocumented) +// @public (undocumented) export type SearchResultSet = ResultSet; ``` diff --git a/plugins/search-common/package.json b/plugins/search-common/package.json index 0e89e6d808..ae4c65d11e 100644 --- a/plugins/search-common/package.json +++ b/plugins/search-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-search-common", "description": "Common functionalities for Search, to be shared between various search-enabled plugins", - "version": "0.3.4", + "version": "0.3.5", "main": "src/index.ts", "types": "src/index.ts", "private": false, @@ -40,10 +40,10 @@ }, "dependencies": { "@backstage/types": "^1.0.0", - "@backstage/plugin-permission-common": "^0.6.1" + "@backstage/plugin-permission-common": "^0.6.2" }, "devDependencies": { - "@backstage/cli": "^0.17.1" + "@backstage/cli": "^0.17.2" }, "jest": { "roots": [ diff --git a/plugins/search-common/src/types.ts b/plugins/search-common/src/types.ts index 220ac977e5..4b4c25d423 100644 --- a/plugins/search-common/src/types.ts +++ b/plugins/search-common/src/types.ts @@ -19,7 +19,7 @@ import { JsonObject } from '@backstage/types'; import { Readable, Transform, Writable } from 'stream'; /** - * @beta + * @public */ export interface SearchQuery { term: string; @@ -29,10 +29,10 @@ export interface SearchQuery { } /** - * @beta * Metadata for result relevant document fields with matched terms highlighted * via wrapping in associated pre/post tags. The UI is expected to parse these * field excerpts by replacing wrapping tags with applicable UI elements for rendering. + * @public */ export interface ResultHighlight { /** @@ -53,16 +53,34 @@ export interface ResultHighlight { } /** - * @beta + * @public */ export interface Result { + /** + * The "type" of the given document. See: {@link DocumentCollatorFactory."type"} + */ type: string; + + /** + * The raw value of the document, as indexed. + */ document: TDocument; + + /** + * Optional result highlight. Useful for improving the search result + * display/experience. + */ highlight?: ResultHighlight; + + /** + * Optional result rank, where 1 is the first/top result returned. Useful for + * understanding search effectiveness in analytics. + */ + rank?: number; } /** - * @beta + * @public */ export interface ResultSet { results: Result[]; @@ -71,28 +89,28 @@ export interface ResultSet { } /** - * @beta + * @public */ export type SearchResult = Result; /** - * @beta + * @public */ export type SearchResultSet = ResultSet; /** - * @beta + * @public */ export type IndexableResult = Result; /** - * @beta + * @public */ export type IndexableResultSet = ResultSet; /** * Base properties that all search documents must include. - * @beta + * @public */ export interface SearchDocument { /** @@ -117,7 +135,7 @@ export interface SearchDocument { * backends working directly with documents being inserted or retrieved from * search indexes. When dealing with documents in the frontend, use * {@link SearchDocument}. - * @beta + * @public */ export type IndexableDocument = SearchDocument & { /** @@ -134,9 +152,9 @@ export type IndexableDocument = SearchDocument & { /** * Information about a specific document type. Intended to be used in the - * {@link @backstage/search-backend-node#IndexBuilder} to collect information + * {@link @backstage/plugin-search-backend-node#IndexBuilder} to collect information * about the types stored in the index. - * @beta + * @public */ export type DocumentTypeInfo = { /** @@ -148,7 +166,7 @@ export type DocumentTypeInfo = { /** * Factory class for instantiating collators. - * @beta + * @public */ export interface DocumentCollatorFactory { /** @@ -171,7 +189,7 @@ export interface DocumentCollatorFactory { /** * Factory class for instantiating decorators. - * @beta + * @public */ export interface DocumentDecoratorFactory { /** @@ -190,13 +208,13 @@ export interface DocumentDecoratorFactory { /** * A type of function responsible for translating an abstract search query into * a concrete query relevant to a particular search engine. - * @beta + * @public */ export type QueryTranslator = (query: SearchQuery) => unknown; /** * Options when querying a search engine. - * @beta + * @public */ export type QueryRequestOptions = { token?: string; @@ -206,7 +224,7 @@ export type QueryRequestOptions = { * Interface that must be implemented by specific search engines, responsible * for performing indexing and querying and translating abstract queries into * concrete, search engine-specific queries. - * @beta + * @public */ export interface SearchEngine { /** diff --git a/plugins/search-react/CHANGELOG.md b/plugins/search-react/CHANGELOG.md index 37acec9c55..5f38994eeb 100644 --- a/plugins/search-react/CHANGELOG.md +++ b/plugins/search-react/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-search-react +## 0.2.1 + +### Patch Changes + +- 8809159148: Components ``, `` (including ``), `` (including `.Checkbox`, `.Select`, and `.Autocomplete` static prop components), ``, and `` are now exported from `@backstage/plugin-search-react`. They are now deprecated in `@backstage/plugin-search` and will be removed in a future release. +- Updated dependencies + - @backstage/plugin-search-common@0.3.5 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + +## 0.2.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/plugin-search-common@0.3.5-next.0 + ## 0.2.0 ### Minor Changes diff --git a/plugins/search-react/api-report.md b/plugins/search-react/api-report.md index 9f61070854..e4afee45ca 100644 --- a/plugins/search-react/api-report.md +++ b/plugins/search-react/api-report.md @@ -7,12 +7,41 @@ import { ApiRef } from '@backstage/core-plugin-api'; import { AsyncState } from 'react-use/lib/useAsync'; +import { InputBaseProps } from '@material-ui/core'; import { JsonObject } from '@backstage/types'; import { PropsWithChildren } from 'react'; import { default as React_2 } from 'react'; +import { ReactElement } from 'react'; +import { ReactNode } from 'react'; +import { ResultHighlight } from '@backstage/plugin-search-common'; +import { SearchDocument } from '@backstage/plugin-search-common'; import { SearchQuery } from '@backstage/plugin-search-common'; +import { SearchResult as SearchResult_2 } from '@backstage/plugin-search-common'; import { SearchResultSet } from '@backstage/plugin-search-common'; +// @public (undocumented) +export const AutocompleteFilter: ( + props: SearchAutocompleteFilterProps, +) => JSX.Element; + +// @public (undocumented) +export const CheckboxFilter: (props: SearchFilterComponentProps) => JSX.Element; + +// @public (undocumented) +export const DefaultResultListItem: ( + props: DefaultResultListItemProps, +) => JSX.Element; + +// @public +export type DefaultResultListItemProps = { + icon?: ReactNode; + secondaryAction?: ReactNode; + result: SearchDocument; + highlight?: ResultHighlight; + rank?: number; + lineClamp?: number; +}; + // @public (undocumented) export const HighlightedSearchResultText: ({ text, @@ -20,7 +49,7 @@ export const HighlightedSearchResultText: ({ postTag, }: HighlightedSearchResultTextProps) => JSX.Element; -// @public (undocumented) +// @public export type HighlightedSearchResultTextProps = { text: string; preTag: string; @@ -45,6 +74,42 @@ export interface SearchApi { // @public (undocumented) export const searchApiRef: ApiRef; +// @public (undocumented) +export type SearchAutocompleteFilterProps = SearchFilterComponentProps & { + filterSelectedOptions?: boolean; + limitTags?: number; + multiple?: boolean; +}; + +// @public +export const SearchBar: ({ onChange, ...props }: SearchBarProps) => JSX.Element; + +// @public +export const SearchBarBase: ({ + onChange, + onKeyDown, + onSubmit, + debounceTime, + clearButton, + fullWidth, + value: defaultValue, + inputProps: defaultInputProps, + endAdornment: defaultEndAdornment, + ...props +}: SearchBarBaseProps) => JSX.Element; + +// @public +export type SearchBarBaseProps = Omit & { + debounceTime?: number; + clearButton?: boolean; + onClear?: () => void; + onSubmit?: () => void; + onChange: (value: string) => void; +}; + +// @public +export type SearchBarProps = Partial; + // @public export const SearchContextProvider: ( props: SearchContextProviderProps, @@ -74,6 +139,55 @@ export type SearchContextValue = { fetchPreviousPage?: React_2.DispatchWithoutAction; } & SearchContextState; +// @public (undocumented) +export const SearchFilter: { + ({ component: Element, ...props }: SearchFilterWrapperProps): JSX.Element; + Checkbox( + props: Omit & + SearchFilterComponentProps, + ): JSX.Element; + Select( + props: Omit & + SearchFilterComponentProps, + ): JSX.Element; + Autocomplete(props: SearchAutocompleteFilterProps): JSX.Element; +}; + +// @public (undocumented) +export type SearchFilterComponentProps = { + className?: string; + name: string; + label?: string; + values?: string[] | ((partial: string) => Promise); + defaultValue?: string[] | string | null; + valuesDebounceMs?: number; +}; + +// @public (undocumented) +export type SearchFilterWrapperProps = SearchFilterComponentProps & { + component: (props: SearchFilterComponentProps) => ReactElement; + debug?: boolean; +}; + +// @public (undocumented) +export const SearchResult: (props: SearchResultProps) => JSX.Element; + +// @public +export const SearchResultComponent: ({ + children, +}: SearchResultProps) => JSX.Element; + +// @public (undocumented) +export const SearchResultPager: () => JSX.Element; + +// @public +export type SearchResultProps = { + children: (results: { results: SearchResult_2[] }) => JSX.Element; +}; + +// @public (undocumented) +export const SelectFilter: (props: SearchFilterComponentProps) => JSX.Element; + // @public export const useSearch: () => SearchContextValue; diff --git a/plugins/search-react/package.json b/plugins/search-react/package.json index f435933f77..c0914e6409 100644 --- a/plugins/search-react/package.json +++ b/plugins/search-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-react", - "version": "0.2.0", + "version": "0.2.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -31,23 +31,29 @@ "start": "backstage-cli package start" }, "dependencies": { - "@backstage/plugin-search-common": "^0.3.4", - "@backstage/core-plugin-api": "^1.0.2", + "@backstage/plugin-search-common": "^0.3.5", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", "@backstage/version-bridge": "^1.0.1", - "react-use": "^17.3.2", + "@backstage/theme": "^0.2.15", "@backstage/types": "^1.0.0", - "@material-ui/core": "^4.12.2" + "@material-ui/core": "^4.12.2", + "@material-ui/icons": "^4.9.1", + "@material-ui/lab": "4.0.0-alpha.57", + "react-router": "6.0.0-beta.0", + "react-use": "^17.3.2" }, "peerDependencies": { "@types/react": "^16.13.1 || ^17.0.0", "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/core-app-api": "^1.0.2", - "@backstage/test-utils": "^1.1.0", + "@backstage/core-app-api": "^1.0.3", + "@backstage/test-utils": "^1.1.1", "@testing-library/react": "^12.1.3", "@testing-library/react-hooks": "^8.0.0", - "@testing-library/jest-dom": "^5.10.1" + "@testing-library/jest-dom": "^5.10.1", + "@testing-library/user-event": "^14.0.0" }, "files": [ "dist" diff --git a/plugins/search/src/components/DefaultResultListItem/DefaultResultListItem.stories.tsx b/plugins/search-react/src/components/DefaultResultListItem/DefaultResultListItem.stories.tsx similarity index 98% rename from plugins/search/src/components/DefaultResultListItem/DefaultResultListItem.stories.tsx rename to plugins/search-react/src/components/DefaultResultListItem/DefaultResultListItem.stories.tsx index fb56d1ecdb..3b54e69a19 100644 --- a/plugins/search/src/components/DefaultResultListItem/DefaultResultListItem.stories.tsx +++ b/plugins/search-react/src/components/DefaultResultListItem/DefaultResultListItem.stories.tsx @@ -1,5 +1,5 @@ /* - * Copyright 2021 The Backstage Authors + * Copyright 2022 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/plugins/search/src/components/DefaultResultListItem/DefaultResultListItem.test.tsx b/plugins/search-react/src/components/DefaultResultListItem/DefaultResultListItem.test.tsx similarity index 98% rename from plugins/search/src/components/DefaultResultListItem/DefaultResultListItem.test.tsx rename to plugins/search-react/src/components/DefaultResultListItem/DefaultResultListItem.test.tsx index 26d081b3b6..e94d394639 100644 --- a/plugins/search/src/components/DefaultResultListItem/DefaultResultListItem.test.tsx +++ b/plugins/search-react/src/components/DefaultResultListItem/DefaultResultListItem.test.tsx @@ -1,5 +1,5 @@ /* - * Copyright 2021 The Backstage Authors + * Copyright 2022 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/plugins/search/src/components/DefaultResultListItem/DefaultResultListItem.tsx b/plugins/search-react/src/components/DefaultResultListItem/DefaultResultListItem.tsx similarity index 67% rename from plugins/search/src/components/DefaultResultListItem/DefaultResultListItem.tsx rename to plugins/search-react/src/components/DefaultResultListItem/DefaultResultListItem.tsx index 5ce2d0744a..f4260525d6 100644 --- a/plugins/search/src/components/DefaultResultListItem/DefaultResultListItem.tsx +++ b/plugins/search-react/src/components/DefaultResultListItem/DefaultResultListItem.tsx @@ -1,5 +1,5 @@ /* - * Copyright 2021 The Backstage Authors + * Copyright 2022 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,11 +15,12 @@ */ import React, { ReactNode } from 'react'; +import { AnalyticsContext, useAnalytics } from '@backstage/core-plugin-api'; import { ResultHighlight, SearchDocument, } from '@backstage/plugin-search-common'; -import { HighlightedSearchResultText } from '@backstage/plugin-search-react'; +import { HighlightedSearchResultText } from '../HighlightedSearchResultText'; import { ListItem, ListItemIcon, @@ -29,23 +30,43 @@ import { } from '@material-ui/core'; import { Link } from '@backstage/core-components'; -type Props = { +/** + * Props for {@link DefaultResultListItem} + * + * @public + */ +export type DefaultResultListItemProps = { icon?: ReactNode; secondaryAction?: ReactNode; result: SearchDocument; highlight?: ResultHighlight; + rank?: number; lineClamp?: number; }; -export const DefaultResultListItem = ({ +/** + * A default result list item. + * + * @public + */ +export const DefaultResultListItemComponent = ({ result, highlight, + rank, icon, secondaryAction, lineClamp = 5, -}: Props) => { +}: DefaultResultListItemProps) => { + const analytics = useAnalytics(); + const handleClick = () => { + analytics.captureEvent('discover', result.title, { + attributes: { to: result.location }, + value: rank, + }); + }; + return ( - + {icon && {icon}} ); }; + +/** + * @public + */ +const HigherOrderDefaultResultListItem = ( + props: DefaultResultListItemProps, +) => { + return ( + + + + ); +}; + +export { HigherOrderDefaultResultListItem as DefaultResultListItem }; diff --git a/plugins/search/src/components/DefaultResultListItem/index.ts b/plugins/search-react/src/components/DefaultResultListItem/index.ts similarity index 84% rename from plugins/search/src/components/DefaultResultListItem/index.ts rename to plugins/search-react/src/components/DefaultResultListItem/index.ts index 77f975a9ef..a555f25c02 100644 --- a/plugins/search/src/components/DefaultResultListItem/index.ts +++ b/plugins/search-react/src/components/DefaultResultListItem/index.ts @@ -1,5 +1,5 @@ /* - * Copyright 2021 The Backstage Authors + * Copyright 2022 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,3 +15,4 @@ */ export { DefaultResultListItem } from './DefaultResultListItem'; +export type { DefaultResultListItemProps } from './DefaultResultListItem'; diff --git a/plugins/search-react/src/components/HighlightedSearchResultText/HighlightedSearchResultText.tsx b/plugins/search-react/src/components/HighlightedSearchResultText/HighlightedSearchResultText.tsx index a749f184fe..bd6ef2d09c 100644 --- a/plugins/search-react/src/components/HighlightedSearchResultText/HighlightedSearchResultText.tsx +++ b/plugins/search-react/src/components/HighlightedSearchResultText/HighlightedSearchResultText.tsx @@ -25,6 +25,8 @@ const useStyles = makeStyles( ); /** + * Props for {@link HighlightedSearchResultText}. + * * @public */ export type HighlightedSearchResultTextProps = { diff --git a/plugins/search/src/components/SearchBar/SearchBar.stories.tsx b/plugins/search-react/src/components/SearchBar/SearchBar.stories.tsx similarity index 93% rename from plugins/search/src/components/SearchBar/SearchBar.stories.tsx rename to plugins/search-react/src/components/SearchBar/SearchBar.stories.tsx index 9728101e97..88aed35f8d 100644 --- a/plugins/search/src/components/SearchBar/SearchBar.stories.tsx +++ b/plugins/search-react/src/components/SearchBar/SearchBar.stories.tsx @@ -1,5 +1,5 @@ /* - * Copyright 2021 The Backstage Authors + * Copyright 2022 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,14 +14,14 @@ * limitations under the License. */ -import { Grid, makeStyles, Paper } from '@material-ui/core'; import React, { ComponentType } from 'react'; -import { - searchApiRef, - MockSearchApi, - SearchContextProvider, -} from '@backstage/plugin-search-react'; +import { Grid, makeStyles, Paper } from '@material-ui/core'; + import { TestApiProvider } from '@backstage/test-utils'; + +import { searchApiRef, MockSearchApi } from '../../api'; +import { SearchContextProvider } from '../../context'; + import { SearchBar } from './SearchBar'; export default { diff --git a/plugins/search-react/src/components/SearchBar/SearchBar.test.tsx b/plugins/search-react/src/components/SearchBar/SearchBar.test.tsx new file mode 100644 index 0000000000..d65278f7ec --- /dev/null +++ b/plugins/search-react/src/components/SearchBar/SearchBar.test.tsx @@ -0,0 +1,352 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import { screen, render, waitFor, act } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +import { configApiRef, analyticsApiRef } from '@backstage/core-plugin-api'; +import { ApiProvider, ConfigReader } from '@backstage/core-app-api'; +import { MockAnalyticsApi, TestApiRegistry } from '@backstage/test-utils'; + +import { searchApiRef } from '../../api'; +import { SearchContextProvider } from '../../context'; +import { SearchBar } from './SearchBar'; + +jest.mock('@backstage/core-plugin-api', () => ({ + ...jest.requireActual('@backstage/core-plugin-api'), +})); + +describe('SearchBar', () => { + const initialState = { + term: '', + filters: {}, + types: ['*'], + pageCursor: '', + }; + + const query = jest.fn().mockResolvedValue({}); + const analyticsApiSpy = new MockAnalyticsApi(); + let apiRegistry: TestApiRegistry; + + apiRegistry = TestApiRegistry.from( + [ + configApiRef, + new ConfigReader({ + app: { title: 'Mock title' }, + }), + ], + [searchApiRef, { query }], + ); + + const name = 'Search'; + const term = 'term'; + + afterAll(() => { + jest.resetAllMocks(); + }); + + it('Renders without exploding', async () => { + render( + + + + + , + ); + + await waitFor(() => { + expect(screen.getByRole('textbox', { name })).toBeInTheDocument(); + expect( + screen.getByPlaceholderText('Search in Mock title'), + ).toBeInTheDocument(); + }); + }); + + it('Renders with custom placeholder', async () => { + render( + + + + + , + , + ); + + await waitFor(() => { + expect( + screen.getByPlaceholderText('This is a custom placeholder'), + ).toBeInTheDocument(); + }); + }); + + it('Renders based on initial search', async () => { + render( + + + + + , + , + ); + + await waitFor(() => { + expect(screen.getByRole('textbox', { name })).toHaveValue(term); + }); + }); + + it('Updates term state when text is entered', async () => { + const user = userEvent.setup({ delay: null }); + jest.useFakeTimers(); + const defaultDebounceTime = 200; + + render( + + + + + , + , + ); + + const textbox = screen.getByRole('textbox', { name }); + + const value = 'value'; + + await user.type(textbox, value); + + act(() => { + jest.advanceTimersByTime(defaultDebounceTime); + }); + + await waitFor(() => { + expect(textbox).toHaveValue(value); + }); + + expect(query).toHaveBeenLastCalledWith( + expect.objectContaining({ term: value }), + ); + jest.useRealTimers(); + }); + + it('Clear button clears term state', async () => { + render( + + + + + , + ); + + await waitFor(() => { + expect(screen.getByRole('textbox', { name })).toHaveValue(term); + }); + + await userEvent.click(screen.getByRole('button', { name: 'Clear' })); + + await waitFor(() => { + expect(screen.getByRole('textbox', { name })).toHaveValue(''); + }); + + expect(query).toHaveBeenLastCalledWith( + expect.objectContaining({ term: '' }), + ); + }); + + it('Should not show clear button', async () => { + render( + + + + + , + ); + + await waitFor(() => { + expect( + screen.queryByRole('button', { name: 'Clear' }), + ).not.toBeInTheDocument(); + }); + }); + + it('Adheres to provided debounceTime', async () => { + const user = userEvent.setup({ delay: null }); + jest.useFakeTimers(); + + const debounceTime = 600; + + render( + + + + + , + , + ); + + await waitFor(() => { + expect(screen.getByRole('textbox', { name })).toBeInTheDocument(); + }); + + const textbox = screen.getByRole('textbox', { name }); + + const value = 'value'; + + await user.type(textbox, value); + + expect(query).not.toHaveBeenLastCalledWith( + expect.objectContaining({ term: value }), + ); + + act(() => { + jest.advanceTimersByTime(debounceTime); + }); + expect(textbox).toHaveValue(value); + + expect(query).toHaveBeenLastCalledWith( + expect.objectContaining({ term: value }), + ); + jest.useRealTimers(); + }); + + it('does not capture analytics event if not enabled in app', async () => { + const user = userEvent.setup({ delay: null }); + jest.useFakeTimers(); + + const debounceTime = 600; + + render( + + + + + , + , + ); + + await waitFor(() => { + expect(screen.getByRole('textbox', { name })).toBeInTheDocument(); + }); + + const textbox = screen.getByRole('textbox', { name }); + + const value = 'value'; + + await user.type(textbox, value); + + act(() => { + jest.advanceTimersByTime(debounceTime); + }); + + await waitFor(() => expect(textbox).toHaveValue(value)); + + expect(analyticsApiSpy.getEvents()).toHaveLength(0); + jest.useRealTimers(); + }); + + it('captures analytics events if enabled in app', async () => { + const user = userEvent.setup({ delay: null }); + jest.useFakeTimers(); + + const debounceTime = 600; + + apiRegistry = TestApiRegistry.from( + [analyticsApiRef, analyticsApiSpy], + [ + configApiRef, + new ConfigReader({ + app: { + title: 'Mock title', + analytics: { + ga: { + trackingId: 'xyz123', + }, + }, + }, + }), + ], + [searchApiRef, { query }], + ); + + render( + + + + + , + ); + + await waitFor(() => { + expect(screen.getByRole('textbox', { name })).toBeInTheDocument(); + }); + + const textbox = screen.getByRole('textbox', { name }); + + const value = 'value'; + + await user.type(textbox, value); + + expect(analyticsApiSpy.getEvents()).toHaveLength(0); + + act(() => { + jest.advanceTimersByTime(debounceTime); + }); + + await waitFor(() => expect(textbox).toHaveValue(value)); + + expect(analyticsApiSpy.getEvents()).toHaveLength(1); + expect(analyticsApiSpy.getEvents()[0]).toEqual({ + action: 'search', + context: { + extension: 'SearchBar', + pluginId: 'search', + routeRef: 'unknown', + searchTypes: 'software-catalog,techdocs', + }, + subject: 'value', + }); + + await user.clear(textbox); + + // make sure new term is captured + await user.type(textbox, 'new value'); + + act(() => { + jest.advanceTimersByTime(debounceTime); + }); + + await waitFor(() => expect(textbox).toHaveValue('new value')); + + expect(analyticsApiSpy.getEvents()).toHaveLength(2); + expect(analyticsApiSpy.getEvents()[1]).toEqual({ + action: 'search', + context: { + extension: 'SearchBar', + pluginId: 'search', + routeRef: 'unknown', + searchTypes: 'software-catalog,techdocs', + }, + subject: 'new value', + }); + jest.useRealTimers(); + }); +}); diff --git a/plugins/search-react/src/components/SearchBar/SearchBar.tsx b/plugins/search-react/src/components/SearchBar/SearchBar.tsx new file mode 100644 index 0000000000..d3d25455ce --- /dev/null +++ b/plugins/search-react/src/components/SearchBar/SearchBar.tsx @@ -0,0 +1,189 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React, { + ChangeEvent, + KeyboardEvent, + useState, + useEffect, + useCallback, +} from 'react'; +import useDebounce from 'react-use/lib/useDebounce'; +import { + InputBase, + InputBaseProps, + InputAdornment, + IconButton, +} from '@material-ui/core'; +import SearchIcon from '@material-ui/icons/Search'; +import ClearButton from '@material-ui/icons/Clear'; + +import { + AnalyticsContext, + configApiRef, + useApi, +} from '@backstage/core-plugin-api'; + +import { + SearchContextProvider, + useSearch, + useSearchContextCheck, +} from '../../context'; +import { TrackSearch } from '../SearchTracker'; + +/** + * Props for {@link SearchBarBase}. + * + * @public + */ +export type SearchBarBaseProps = Omit & { + debounceTime?: number; + clearButton?: boolean; + onClear?: () => void; + onSubmit?: () => void; + onChange: (value: string) => void; +}; + +/** + * All search boxes exported by the search plugin are based on the , + * and this one is based on the component from Material UI. + * Recommended if you don't use Search Provider or Search Context. + * + * @public + */ +export const SearchBarBase = ({ + onChange, + onKeyDown, + onSubmit, + debounceTime = 200, + clearButton = true, + fullWidth = true, + value: defaultValue, + inputProps: defaultInputProps = {}, + endAdornment: defaultEndAdornment, + ...props +}: SearchBarBaseProps) => { + const configApi = useApi(configApiRef); + const [value, setValue] = useState(defaultValue as string); + const hasSearchContext = useSearchContextCheck(); + + useEffect(() => { + setValue(prevValue => + prevValue !== defaultValue ? (defaultValue as string) : prevValue, + ); + }, [defaultValue]); + + useDebounce(() => onChange(value), debounceTime, [value]); + + const handleChange = useCallback( + (e: ChangeEvent) => { + setValue(e.target.value); + }, + [setValue], + ); + + const handleKeyDown = useCallback( + (e: KeyboardEvent) => { + if (onKeyDown) onKeyDown(e); + if (onSubmit && e.key === 'Enter') { + onSubmit(); + } + }, + [onKeyDown, onSubmit], + ); + + const handleClear = useCallback(() => { + onChange(''); + }, [onChange]); + + const placeholder = `Search in ${ + configApi.getOptionalString('app.title') || 'Backstage' + }`; + + const startAdornment = ( + + + + + + ); + + const endAdornment = ( + + + + + + ); + + const searchBar = ( + + + + ); + + return hasSearchContext ? ( + searchBar + ) : ( + {searchBar} + ); +}; + +/** + * Props for {@link SearchBar}. + * + * @public + */ +export type SearchBarProps = Partial; + +/** + * Recommended search bar when you use the Search Provider or Search Context. + * + * @public + */ +export const SearchBar = ({ onChange, ...props }: SearchBarProps) => { + const { term, setTerm } = useSearch(); + + const handleChange = useCallback( + (newValue: string) => { + if (onChange) { + onChange(newValue); + } else { + setTerm(newValue); + } + }, + [onChange, setTerm], + ); + + return ( + + + + ); +}; diff --git a/plugins/search-react/src/components/SearchBar/index.tsx b/plugins/search-react/src/components/SearchBar/index.tsx new file mode 100644 index 0000000000..075a0c7dc2 --- /dev/null +++ b/plugins/search-react/src/components/SearchBar/index.tsx @@ -0,0 +1,18 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { SearchBar, SearchBarBase } from './SearchBar'; +export type { SearchBarProps, SearchBarBaseProps } from './SearchBar'; diff --git a/plugins/search-react/src/components/SearchFilter/SearchFilter.Autocomplete.test.tsx b/plugins/search-react/src/components/SearchFilter/SearchFilter.Autocomplete.test.tsx new file mode 100644 index 0000000000..14c8967b57 --- /dev/null +++ b/plugins/search-react/src/components/SearchFilter/SearchFilter.Autocomplete.test.tsx @@ -0,0 +1,361 @@ +/* + * 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 { TestApiProvider } from '@backstage/test-utils'; +import { screen, render, waitFor, within } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import React from 'react'; + +import { searchApiRef } from '../../api'; +import { SearchContextProvider, useSearch } from '../../context'; +import { SearchFilter } from './SearchFilter'; + +const SearchContextFilterSpy = ({ name }: { name: string }) => { + const { filters } = useSearch(); + const value = filters[name]; + return ( + + {Array.isArray(value) ? value.join(',') : value} + + ); +}; + +describe('SearchFilter.Autocomplete', () => { + const query = jest.fn().mockResolvedValue({}); + const emptySearchContext = { + term: '', + types: [], + filters: {}, + }; + + const name = 'field'; + const values = ['value1', 'value2']; + + it('renders as expected', async () => { + render( + + + + + , + ); + + const autocomplete = screen.getByRole('combobox'); + const input = within(autocomplete).getByRole('textbox'); + await userEvent.click(input); + + await waitFor(() => { + screen.getByRole('listbox'); + }); + + expect(screen.getByRole('option', { name: values[0] })).toBeInTheDocument(); + expect(screen.getByRole('option', { name: values[1] })).toBeInTheDocument(); + }); + + it('renders as expected with async values', async () => { + render( + + + values} /> + + , + ); + + const autocomplete = screen.getByRole('combobox'); + const input = within(autocomplete).getByRole('textbox'); + await userEvent.click(input); + + await waitFor(() => { + screen.getByRole('listbox'); + }); + + expect(screen.getByRole('option', { name: values[0] })).toBeInTheDocument(); + expect(screen.getByRole('option', { name: values[1] })).toBeInTheDocument(); + }); + + it('does not affect unrelated filter state', async () => { + render( + + + + + + + , + ); + + // The spy should show the initial value. + expect(screen.getByTestId('unrelated-filter-spy')).toHaveTextContent( + 'value', + ); + + // Select a value from the autocomplete filter. + const autocomplete = screen.getByRole('combobox'); + const input = within(autocomplete).getByRole('textbox'); + await userEvent.click(input); + await waitFor(() => { + screen.getByRole('listbox'); + }); + await userEvent.click(screen.getByRole('option', { name: values[1] })); + + // Wait for the autocomplete filter's value to change. + await waitFor(() => { + expect(screen.getByTestId(`${name}-filter-spy`)).toHaveTextContent( + values[1], + ); + }); + + // Unrelated filter spy should maintain the same value. + expect(screen.getByTestId('unrelated-filter-spy')).toHaveTextContent( + 'value', + ); + }); + + describe('single', () => { + it('renders as expected with defaultValue', async () => { + render( + + + + + + , + ); + + const autocomplete = screen.getByRole('combobox'); + const input = within(autocomplete).getByRole('textbox'); + + await waitFor(() => { + expect(input).toHaveValue(values[1]); + expect(screen.getByTestId(`${name}-filter-spy`)).toHaveTextContent( + values[1], + ); + }); + }); + + it('renders as expected with initial context', async () => { + render( + + + + + + , + ); + + const autocomplete = screen.getByRole('combobox'); + const input = within(autocomplete).getByRole('textbox'); + + await waitFor(() => { + expect(input).toHaveValue(values[0]); + expect(screen.getByTestId(`${name}-filter-spy`)).toHaveTextContent( + values[0], + ); + }); + }); + + it('sets filter state when selecting a value', async () => { + render( + + + + + + , + ); + + // Select the first option in the autocomplete. + const autocomplete = screen.getByRole('combobox'); + const input = within(autocomplete).getByRole('textbox'); + await userEvent.click(input); + await waitFor(() => { + screen.getByRole('listbox'); + }); + await userEvent.click(screen.getByRole('option', { name: values[0] })); + + // The value should be present in the context. + await waitFor(() => { + expect(screen.getByTestId(`${name}-filter-spy`)).toHaveTextContent( + values[0], + ); + }); + + // Click the "Clear" button to remove the value. + const clearButton = within(autocomplete).getByLabelText('Clear'); + await userEvent.click(clearButton); + + // That value should have been unset from the context. + await waitFor(() => { + expect(screen.getByTestId(`${name}-filter-spy`)).toHaveTextContent(''); + }); + }); + }); + + describe('multiple', () => { + it('renders as expected with defaultValue', async () => { + render( + + + + + + , + ); + + await waitFor(() => { + expect(screen.getByText(values[0])).toBeInTheDocument(); + expect(screen.getByText(values[1])).toBeInTheDocument(); + expect(screen.getByTestId(`${name}-filter-spy`)).toHaveTextContent( + values.join(','), + ); + }); + }); + + it('renders as expected with initial context', async () => { + render( + + + + + + , + ); + + await waitFor(() => { + expect(screen.getByText(values[0])).toBeInTheDocument(); + expect(screen.getByText(values[1])).toBeInTheDocument(); + expect(screen.getByTestId(`${name}-filter-spy`)).toHaveTextContent( + values.join(','), + ); + }); + }); + + it('respects tag limit configuration', async () => { + render( + + + + + , + ); + + const autocomplete = screen.getByRole('combobox'); + const input = within(autocomplete).getByRole('textbox'); + + // Select the second value. + await userEvent.click(input); + await waitFor(() => { + screen.getByRole('listbox'); + }); + await userEvent.click(screen.getByRole('option', { name: values[1] })); + await waitFor(() => { + expect( + screen.getByRole('button', { name: values[1] }), + ).toBeInTheDocument(); + }); + + // Select the first value. + await userEvent.click(input); + await waitFor(() => { + screen.getByRole('listbox'); + }); + await userEvent.click(screen.getByRole('option', { name: values[0] })); + await waitFor(() => { + expect( + screen.getByRole('button', { name: values[0] }), + ).toBeInTheDocument(); + }); + + // Blur the field and only one tag should be shown with a +1. + input.blur(); + expect( + screen.queryByRole('button', { name: values[0] }), + ).not.toBeInTheDocument(); + expect(screen.getByText('+1')).toBeInTheDocument(); + }); + + it('sets filter state when selecting a value', async () => { + render( + + + + + + , + ); + + const autocomplete = screen.getByRole('combobox'); + + // Select both values in the autocomplete. + const input = within(autocomplete).getByRole('textbox'); + await userEvent.click(input); + await waitFor(() => { + screen.getByRole('listbox'); + }); + await userEvent.click(screen.getByRole('option', { name: values[0] })); + await userEvent.click(input); + await waitFor(() => { + screen.getByRole('listbox'); + }); + await userEvent.click(screen.getByRole('option', { name: values[1] })); + + // Both options should be present in the context. + await waitFor(() => { + expect(screen.getByTestId(`${name}-filter-spy`)).toHaveTextContent( + values.join(','), + ); + }); + + // Click the "Clear" button to remove the value. + const clearButton = within(autocomplete).getByLabelText('Clear'); + await userEvent.click(clearButton); + + // There should be no content in the filter context. + await waitFor(() => { + expect(screen.getByTestId(`${name}-filter-spy`)).toHaveTextContent(''); + }); + }); + }); +}); diff --git a/plugins/search-react/src/components/SearchFilter/SearchFilter.Autocomplete.tsx b/plugins/search-react/src/components/SearchFilter/SearchFilter.Autocomplete.tsx new file mode 100644 index 0000000000..35d1f376e5 --- /dev/null +++ b/plugins/search-react/src/components/SearchFilter/SearchFilter.Autocomplete.tsx @@ -0,0 +1,120 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React, { ChangeEvent, useState } from 'react'; +import { Chip, TextField } from '@material-ui/core'; +import { + Autocomplete, + AutocompleteGetTagProps, + AutocompleteRenderInputParams, +} from '@material-ui/lab'; + +import { useSearch } from '../../context'; +import { useAsyncFilterValues, useDefaultFilterValue } from './hooks'; +import { SearchFilterComponentProps } from './SearchFilter'; + +/** + * @public + */ +export type SearchAutocompleteFilterProps = SearchFilterComponentProps & { + filterSelectedOptions?: boolean; + limitTags?: number; + multiple?: boolean; +}; + +/** + * @public + */ +export const AutocompleteFilter = (props: SearchAutocompleteFilterProps) => { + const { + className, + defaultValue, + name, + values: givenValues, + valuesDebounceMs, + label, + filterSelectedOptions, + limitTags, + multiple, + } = props; + const [inputValue, setInputValue] = useState(''); + useDefaultFilterValue(name, defaultValue); + const asyncValues = + typeof givenValues === 'function' ? givenValues : undefined; + const defaultValues = + typeof givenValues === 'function' ? undefined : givenValues; + const { value: values, loading } = useAsyncFilterValues( + asyncValues, + inputValue, + defaultValues, + valuesDebounceMs, + ); + const { filters, setFilters } = useSearch(); + const filterValue = + (filters[name] as string | string[] | undefined) || (multiple ? [] : null); + + // Set new filter values on input change. + const handleChange = ( + _: ChangeEvent<{}>, + newValue: string | string[] | null, + ) => { + setFilters(prevState => { + const { [name]: filter, ...others } = prevState; + + if (newValue) { + return { ...others, [name]: newValue }; + } + return { ...others }; + }); + }; + + // Provide the input field. + const renderInput = (params: AutocompleteRenderInputParams) => ( + + ); + + // Render tags as primary-colored chips. + const renderTags = ( + tagValue: string[], + getTagProps: AutocompleteGetTagProps, + ) => + tagValue.map((option: string, index: number) => ( + + )); + + return ( + setInputValue(newValue)} + renderInput={renderInput} + renderTags={renderTags} + /> + ); +}; diff --git a/plugins/search/src/components/SearchFilter/SearchFilter.stories.tsx b/plugins/search-react/src/components/SearchFilter/SearchFilter.stories.tsx similarity index 95% rename from plugins/search/src/components/SearchFilter/SearchFilter.stories.tsx rename to plugins/search-react/src/components/SearchFilter/SearchFilter.stories.tsx index 7e8a7d585a..2c8a629f01 100644 --- a/plugins/search/src/components/SearchFilter/SearchFilter.stories.tsx +++ b/plugins/search-react/src/components/SearchFilter/SearchFilter.stories.tsx @@ -1,5 +1,5 @@ /* - * Copyright 2021 The Backstage Authors + * Copyright 2022 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,14 +14,13 @@ * limitations under the License. */ -import { Grid, Paper } from '@material-ui/core'; import React, { ComponentType } from 'react'; -import { - searchApiRef, - MockSearchApi, - SearchContextProvider, -} from '@backstage/plugin-search-react'; +import { Grid, Paper } from '@material-ui/core'; + import { TestApiProvider } from '@backstage/test-utils'; + +import { searchApiRef, MockSearchApi } from '../../api'; +import { SearchContextProvider } from '../../context'; import { SearchFilter } from './SearchFilter'; export default { diff --git a/plugins/search-react/src/components/SearchFilter/SearchFilter.test.tsx b/plugins/search-react/src/components/SearchFilter/SearchFilter.test.tsx new file mode 100644 index 0000000000..89a1818e8d --- /dev/null +++ b/plugins/search-react/src/components/SearchFilter/SearchFilter.test.tsx @@ -0,0 +1,410 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import { screen, render, waitFor } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +import { useApi } from '@backstage/core-plugin-api'; + +import { SearchContextProvider } from '../../context'; +import { SearchFilter } from './SearchFilter'; + +jest.mock('@backstage/core-plugin-api', () => ({ + ...jest.requireActual('@backstage/core-plugin-api'), + useApi: jest.fn().mockReturnValue({}), +})); + +describe('SearchFilter', () => { + const initialState = { + term: '', + filters: {}, + types: [], + }; + + const label = 'Field'; + const name = 'field'; + const values = ['value1', 'value2']; + const filters = { unrelated: 'unrelated' }; + + const query = jest.fn().mockResolvedValue({}); + (useApi as jest.Mock).mockReturnValue({ query: query }); + + afterAll(() => { + jest.resetAllMocks(); + }); + + it('Check that element was rendered and received props', async () => { + const CustomFilter = (props: { name: string }) =>
{props.name}
; + + render(); + + expect(screen.getByRole('heading', { name })).toBeInTheDocument(); + }); + + describe('Checkbox', () => { + it('Renders field name and values when provided as props', async () => { + render( + + + , + ); + + await waitFor(() => { + expect(screen.getByText(label)).toBeInTheDocument(); + }); + + expect( + screen.getByRole('checkbox', { name: values[0] }), + ).toBeInTheDocument(); + expect( + screen.getByRole('checkbox', { name: values[1] }), + ).toBeInTheDocument(); + }); + + it('Renders correctly based on filter state', async () => { + render( + + + , + ); + + await waitFor(() => { + expect(screen.getByText(label)).toBeInTheDocument(); + }); + + expect( + screen.getByRole('checkbox', { name: values[0] }), + ).not.toBeChecked(); + expect(screen.getByRole('checkbox', { name: values[1] })).toBeChecked(); + }); + + it('Renders correctly based on defaultValue', async () => { + render( + + + , + ); + + await waitFor(() => { + expect(screen.getByText(label)).toBeInTheDocument(); + }); + + expect(screen.getByRole('checkbox', { name: values[0] })).toBeChecked(); + expect( + screen.getByRole('checkbox', { name: values[1] }), + ).not.toBeChecked(); + }); + + it('Checking / unchecking a value sets filter state', async () => { + render( + + + , + ); + + await waitFor(() => { + expect(screen.getByText(label)).toBeInTheDocument(); + }); + + const checkBox = screen.getByRole('checkbox', { name: values[0] }); + + // Check the box. + await userEvent.click(checkBox); + await waitFor(() => { + expect(query).toHaveBeenLastCalledWith( + expect.objectContaining({ filters: { field: [values[0]] } }), + ); + }); + + // Uncheck the box. + await userEvent.click(checkBox); + await waitFor(() => { + expect(query).toHaveBeenLastCalledWith( + expect.objectContaining({ filters: {} }), + ); + }); + }); + + it('Checking / unchecking a value maintains unrelated filter state', async () => { + render( + + + , + ); + + await waitFor(() => { + expect(screen.getByText(label)).toBeInTheDocument(); + }); + + const checkBox = screen.getByRole('checkbox', { name: values[0] }); + + // Check the box. + await userEvent.click(checkBox); + await waitFor(() => { + expect(query).toHaveBeenLastCalledWith( + expect.objectContaining({ + filters: { ...filters, field: [values[0]] }, + }), + ); + }); + + // Uncheck the box. + await userEvent.click(checkBox); + await waitFor(() => { + expect(query).toHaveBeenLastCalledWith( + expect.objectContaining({ filters }), + ); + }); + }); + }); + + describe('Select', () => { + it('Renders field name and values when provided as props', async () => { + render( + + + , + ); + + await waitFor(() => { + expect(screen.getByText(label)).toBeInTheDocument(); + }); + + await userEvent.click(screen.getByRole('button')); + + await waitFor(() => { + expect(screen.getByRole('listbox')).toBeInTheDocument(); + }); + + expect( + screen.getByRole('option', { name: values[0] }), + ).toBeInTheDocument(); + expect( + screen.getByRole('option', { name: values[1] }), + ).toBeInTheDocument(); + }); + + it('Renders values when provided asynchronously', async () => { + render( + + values} + /> + , + ); + + await waitFor(() => { + expect(screen.getByRole('button')).toBeInTheDocument(); + expect( + screen.getByRole('button').getAttribute('aria-disabled'), + ).not.toBe('true'); + }); + + await userEvent.click(screen.getByRole('button')); + + await waitFor(() => { + expect(screen.getByRole('listbox')).toBeInTheDocument(); + }); + + expect( + screen.getByRole('option', { name: values[0] }), + ).toBeInTheDocument(); + expect( + screen.getByRole('option', { name: values[1] }), + ).toBeInTheDocument(); + }); + + it('Renders correctly based on filter state', async () => { + render( + + + , + ); + + await waitFor(() => { + expect(screen.getByText(label)).toBeInTheDocument(); + }); + + await userEvent.click(screen.getByRole('button')); + + await waitFor(() => { + expect(screen.getByRole('listbox')).toBeInTheDocument(); + }); + + expect(screen.getByRole('option', { name: values[0] })).toHaveAttribute( + 'aria-selected', + 'true', + ); + expect( + screen.getByRole('option', { name: values[1] }), + ).not.toHaveAttribute('aria-selected'); + expect(screen.getByRole('option', { name: 'All' })).not.toHaveAttribute( + 'aria-selected', + ); + }); + + it('Renders correctly based on defaultValue', async () => { + render( + + + , + ); + + await waitFor(() => { + expect(screen.getByText(label)).toBeInTheDocument(); + }); + + await userEvent.click(screen.getByRole('button')); + + await waitFor(() => { + expect(screen.getByRole('listbox')).toBeInTheDocument(); + }); + + expect(screen.getByRole('option', { name: values[0] })).toHaveAttribute( + 'aria-selected', + 'true', + ); + expect( + screen.getByRole('option', { name: values[1] }), + ).not.toHaveAttribute('aria-selected'); + expect(screen.getByRole('option', { name: 'All' })).not.toHaveAttribute( + 'aria-selected', + ); + }); + + it('Selecting a value sets filter state', async () => { + render( + + + , + ); + + await waitFor(() => { + expect(screen.getByText(label)).toBeInTheDocument(); + }); + + const button = screen.getByRole('button'); + + await userEvent.click(button); + + await waitFor(() => { + expect(screen.getByRole('listbox')).toBeInTheDocument(); + }); + + await userEvent.click(screen.getByRole('option', { name: values[0] })); + + await waitFor(() => { + expect(query).toHaveBeenLastCalledWith( + expect.objectContaining({ + filters: { [name]: values[0] }, + }), + ); + }); + + await userEvent.click(button); + + await waitFor(() => { + expect(screen.getByRole('listbox')).toBeInTheDocument(); + }); + + await userEvent.click(screen.getByRole('option', { name: 'All' })); + + await waitFor(() => { + expect(query).toHaveBeenLastCalledWith( + expect.objectContaining({ + filters: {}, + }), + ); + }); + }); + + it('Selecting a value maintains unrelated filter state', async () => { + render( + + + , + ); + + await waitFor(() => { + expect(screen.getByText(label)).toBeInTheDocument(); + }); + + const button = screen.getByRole('button'); + + await userEvent.click(button); + + await waitFor(() => { + expect(screen.getByRole('listbox')).toBeInTheDocument(); + }); + + await userEvent.click(screen.getByRole('option', { name: values[0] })); + + await waitFor(() => { + expect(query).toHaveBeenLastCalledWith( + expect.objectContaining({ + filters: { ...filters, [name]: values[0] }, + }), + ); + }); + + await userEvent.click(button); + + await waitFor(() => { + expect(screen.getByRole('listbox')).toBeInTheDocument(); + }); + + await userEvent.click(screen.getByRole('option', { name: 'All' })); + + await waitFor(() => { + expect(query).toHaveBeenLastCalledWith( + expect.objectContaining({ filters }), + ); + }); + }); + }); +}); diff --git a/plugins/search-react/src/components/SearchFilter/SearchFilter.tsx b/plugins/search-react/src/components/SearchFilter/SearchFilter.tsx new file mode 100644 index 0000000000..2a6d7baafa --- /dev/null +++ b/plugins/search-react/src/components/SearchFilter/SearchFilter.tsx @@ -0,0 +1,237 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React, { ReactElement, ChangeEvent } from 'react'; +import { + makeStyles, + FormControl, + FormControlLabel, + InputLabel, + Checkbox, + Select, + MenuItem, + FormLabel, +} from '@material-ui/core'; + +import { useSearch } from '../../context'; +import { + AutocompleteFilter, + SearchAutocompleteFilterProps, +} from './SearchFilter.Autocomplete'; +import { useAsyncFilterValues, useDefaultFilterValue } from './hooks'; + +const useStyles = makeStyles({ + label: { + textTransform: 'capitalize', + }, +}); + +/** + * @public + */ +export type SearchFilterComponentProps = { + className?: string; + name: string; + label?: string; + /** + * Either an array of values directly, or an async function to return a list + * of values to be used in the filter. In the autocomplete filter, the last + * input value is provided as an input to allow values to be filtered. This + * function is debounced and values cached. + */ + values?: string[] | ((partial: string) => Promise); + defaultValue?: string[] | string | null; + /** + * Debounce time in milliseconds, used when values is an async callback. + * Defaults to 250ms. + */ + valuesDebounceMs?: number; +}; + +/** + * @public + */ +export type SearchFilterWrapperProps = SearchFilterComponentProps & { + component: (props: SearchFilterComponentProps) => ReactElement; + debug?: boolean; +}; + +/** + * @public + */ +export const CheckboxFilter = (props: SearchFilterComponentProps) => { + const { + className, + defaultValue, + label, + name, + values: givenValues = [], + valuesDebounceMs, + } = props; + const classes = useStyles(); + const { filters, setFilters } = useSearch(); + useDefaultFilterValue(name, defaultValue); + const asyncValues = + typeof givenValues === 'function' ? givenValues : undefined; + const defaultValues = + typeof givenValues === 'function' ? undefined : givenValues; + const { value: values = [], loading } = useAsyncFilterValues( + asyncValues, + '', + defaultValues, + valuesDebounceMs, + ); + + const handleChange = (e: ChangeEvent) => { + const { + target: { value, checked }, + } = e; + + setFilters(prevFilters => { + const { [name]: filter, ...others } = prevFilters; + const rest = ((filter as string[]) || []).filter(i => i !== value); + const items = checked ? [...rest, value] : rest; + return items.length ? { ...others, [name]: items } : others; + }); + }; + + return ( + + {label ? {label} : null} + {values.map((value: string) => ( + + } + label={value} + /> + ))} + + ); +}; + +/** + * @public + */ +export const SelectFilter = (props: SearchFilterComponentProps) => { + const { + className, + defaultValue, + label, + name, + values: givenValues, + valuesDebounceMs, + } = props; + const classes = useStyles(); + useDefaultFilterValue(name, defaultValue); + const asyncValues = + typeof givenValues === 'function' ? givenValues : undefined; + const defaultValues = + typeof givenValues === 'function' ? undefined : givenValues; + const { value: values = [], loading } = useAsyncFilterValues( + asyncValues, + '', + defaultValues, + valuesDebounceMs, + ); + const { filters, setFilters } = useSearch(); + + const handleChange = (e: ChangeEvent<{ value: unknown }>) => { + const { + target: { value }, + } = e; + + setFilters(prevFilters => { + const { [name]: filter, ...others } = prevFilters; + return value ? { ...others, [name]: value as string } : others; + }); + }; + + return ( + + {label ? ( + + {label} + + ) : null} + + + ); +}; + +/** + * @public + */ +const SearchFilter = ({ + component: Element, + ...props +}: SearchFilterWrapperProps) => ; + +SearchFilter.Checkbox = ( + props: Omit & + SearchFilterComponentProps, +) => ; + +SearchFilter.Select = ( + props: Omit & + SearchFilterComponentProps, +) => ; + +/** + * A control surface for a given filter field name, rendered as an autocomplete + * textfield. A hard-coded list of values may be provided, or an async function + * which returns values may be provided instead. + * + * @public + */ +SearchFilter.Autocomplete = (props: SearchAutocompleteFilterProps) => ( + +); + +export { SearchFilter }; diff --git a/plugins/search/src/components/SearchFilter/hooks.test.tsx b/plugins/search-react/src/components/SearchFilter/hooks.test.tsx similarity index 98% rename from plugins/search/src/components/SearchFilter/hooks.test.tsx rename to plugins/search-react/src/components/SearchFilter/hooks.test.tsx index 14346a29eb..80c4a3a3a2 100644 --- a/plugins/search/src/components/SearchFilter/hooks.test.tsx +++ b/plugins/search-react/src/components/SearchFilter/hooks.test.tsx @@ -17,11 +17,9 @@ import React from 'react'; import { ApiProvider } from '@backstage/core-app-api'; import { TestApiRegistry } from '@backstage/test-utils'; import { renderHook } from '@testing-library/react-hooks'; -import { - SearchContextProvider, - useSearch, - searchApiRef, -} from '@backstage/plugin-search-react'; + +import { searchApiRef } from '../../api'; +import { SearchContextProvider, useSearch } from '../../context'; import { useDefaultFilterValue, useAsyncFilterValues } from './hooks'; jest.useFakeTimers(); diff --git a/plugins/search/src/components/SearchFilter/hooks.ts b/plugins/search-react/src/components/SearchFilter/hooks.ts similarity index 96% rename from plugins/search/src/components/SearchFilter/hooks.ts rename to plugins/search-react/src/components/SearchFilter/hooks.ts index da30466522..5771cc5288 100644 --- a/plugins/search/src/components/SearchFilter/hooks.ts +++ b/plugins/search-react/src/components/SearchFilter/hooks.ts @@ -1,5 +1,5 @@ /* - * Copyright 2021 The Backstage Authors + * Copyright 2022 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,11 +17,14 @@ import { useEffect, useRef } from 'react'; import useAsyncFn from 'react-use/lib/useAsyncFn'; import useDebounce from 'react-use/lib/useDebounce'; -import { useSearch } from '@backstage/plugin-search-react'; + +import { useSearch } from '../../context'; /** * Utility hook for either asynchronously loading filter values from a given * function or synchronously providing a given list of default values. + * + * @public */ export const useAsyncFilterValues = ( fn: ((partial: string) => Promise) | undefined, @@ -75,6 +78,8 @@ export const useAsyncFilterValues = ( /** * Utility hook for applying a given default value to the search context. + * + * @public */ export const useDefaultFilterValue = ( name: string, diff --git a/plugins/search-react/src/components/SearchFilter/index.ts b/plugins/search-react/src/components/SearchFilter/index.ts new file mode 100644 index 0000000000..9f92c7c8e2 --- /dev/null +++ b/plugins/search-react/src/components/SearchFilter/index.ts @@ -0,0 +1,23 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { CheckboxFilter, SearchFilter, SelectFilter } from './SearchFilter'; +export { AutocompleteFilter } from './SearchFilter.Autocomplete'; +export type { + SearchFilterComponentProps, + SearchFilterWrapperProps, +} from './SearchFilter'; +export type { SearchAutocompleteFilterProps } from './SearchFilter.Autocomplete'; diff --git a/plugins/search/src/components/SearchResult/SearchResult.stories.tsx b/plugins/search-react/src/components/SearchResult/SearchResult.stories.tsx similarity index 94% rename from plugins/search/src/components/SearchResult/SearchResult.stories.tsx rename to plugins/search-react/src/components/SearchResult/SearchResult.stories.tsx index 207f1ae1d7..32f9a19a07 100644 --- a/plugins/search/src/components/SearchResult/SearchResult.stories.tsx +++ b/plugins/search-react/src/components/SearchResult/SearchResult.stories.tsx @@ -1,5 +1,5 @@ /* - * Copyright 2021 The Backstage Authors + * Copyright 2022 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,20 +14,18 @@ * limitations under the License. */ -import { Link } from '@backstage/core-components'; -import { List, ListItem } from '@material-ui/core'; import React, { ComponentType } from 'react'; +import { List, ListItem } from '@material-ui/core'; import { MemoryRouter } from 'react-router'; -import { DefaultResultListItem } from '../DefaultResultListItem'; -import { - searchApiRef, - MockSearchApi, - SearchContextProvider, -} from '@backstage/plugin-search-react'; -import { SearchResult } from './SearchResult'; +import { Link } from '@backstage/core-components'; import { TestApiProvider } from '@backstage/test-utils'; +import { searchApiRef, MockSearchApi } from '../../api'; +import { SearchContextProvider } from '../../context'; +import { DefaultResultListItem } from '../DefaultResultListItem'; +import { SearchResult } from './SearchResult'; + const mockResults = { results: [ { diff --git a/plugins/search/src/components/SearchResult/SearchResult.test.tsx b/plugins/search-react/src/components/SearchResult/SearchResult.test.tsx similarity index 94% rename from plugins/search/src/components/SearchResult/SearchResult.test.tsx rename to plugins/search-react/src/components/SearchResult/SearchResult.test.tsx index d93eacaef0..988d59d7cf 100644 --- a/plugins/search/src/components/SearchResult/SearchResult.test.tsx +++ b/plugins/search-react/src/components/SearchResult/SearchResult.test.tsx @@ -1,5 +1,5 @@ /* - * Copyright 2021 The Backstage Authors + * Copyright 2022 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,14 +14,16 @@ * limitations under the License. */ -import { renderInTestApp } from '@backstage/test-utils'; -import { waitFor } from '@testing-library/react'; import React from 'react'; -import { useSearch } from '@backstage/plugin-search-react'; +import { waitFor } from '@testing-library/react'; + +import { renderInTestApp } from '@backstage/test-utils'; + +import { useSearch } from '../../context'; import { SearchResult } from './SearchResult'; -jest.mock('@backstage/plugin-search-react', () => ({ - ...jest.requireActual('@backstage/plugin-search-react'), +jest.mock('../../context', () => ({ + ...jest.requireActual('../../context'), useSearch: jest.fn().mockReturnValue({ result: {}, }), diff --git a/plugins/search/src/components/SearchResult/SearchResult.tsx b/plugins/search-react/src/components/SearchResult/SearchResult.tsx similarity index 63% rename from plugins/search/src/components/SearchResult/SearchResult.tsx rename to plugins/search-react/src/components/SearchResult/SearchResult.tsx index a62dddb924..46a2dde305 100644 --- a/plugins/search/src/components/SearchResult/SearchResult.tsx +++ b/plugins/search-react/src/components/SearchResult/SearchResult.tsx @@ -1,5 +1,5 @@ /* - * Copyright 2021 The Backstage Authors + * Copyright 2022 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,20 +14,33 @@ * limitations under the License. */ +import React from 'react'; + import { EmptyState, Progress, ResponseErrorPanel, } from '@backstage/core-components'; +import { AnalyticsContext } from '@backstage/core-plugin-api'; import { SearchResult } from '@backstage/plugin-search-common'; -import React from 'react'; -import { useSearch } from '@backstage/plugin-search-react'; -type Props = { +import { useSearch } from '../../context'; + +/** + * Props for {@link SearchResultComponent} + * + * @public + */ +export type SearchResultProps = { children: (results: { results: SearchResult[] }) => JSX.Element; }; -export const SearchResultComponent = ({ children }: Props) => { +/** + * A component returning the search result. + * + * @public + */ +export const SearchResultComponent = ({ children }: SearchResultProps) => { const { result: { loading, error, value }, } = useSearch(); @@ -51,4 +64,20 @@ export const SearchResultComponent = ({ children }: Props) => { return <>{children({ results: value.results })}; }; -export { SearchResultComponent as SearchResult }; +/** + * @public + */ +const HigherOrderSearchResult = (props: SearchResultProps) => { + return ( + + + + ); +}; + +export { HigherOrderSearchResult as SearchResult }; diff --git a/plugins/search-react/src/components/SearchResult/index.tsx b/plugins/search-react/src/components/SearchResult/index.tsx new file mode 100644 index 0000000000..503ac47bbd --- /dev/null +++ b/plugins/search-react/src/components/SearchResult/index.tsx @@ -0,0 +1,18 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { SearchResult, SearchResultComponent } from './SearchResult'; +export type { SearchResultProps } from './SearchResult'; diff --git a/plugins/search-react/src/components/SearchResultPager/SearchResultPager.test.tsx b/plugins/search-react/src/components/SearchResultPager/SearchResultPager.test.tsx new file mode 100644 index 0000000000..24d9a76e51 --- /dev/null +++ b/plugins/search-react/src/components/SearchResultPager/SearchResultPager.test.tsx @@ -0,0 +1,58 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; + +import { renderInTestApp } from '@backstage/test-utils'; +import { waitFor } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +import { useSearch } from '../../context'; +import { SearchResultPager } from './SearchResultPager'; + +jest.mock('../../context', () => ({ + ...jest.requireActual('../../context'), + useSearch: jest.fn().mockReturnValue({ + result: {}, + }), +})); + +describe('SearchResultPager', () => { + it('renders pager buttons', async () => { + const fetchNextPage = jest.fn(); + const fetchPreviousPage = jest.fn(); + (useSearch as jest.Mock).mockReturnValueOnce({ + result: { loading: false, value: [] }, + fetchNextPage, + fetchPreviousPage, + }); + + const { getByLabelText } = await renderInTestApp(); + + await waitFor(() => { + expect(getByLabelText('previous page')).toBeInTheDocument(); + }); + await userEvent.click(getByLabelText('previous page')); + expect(fetchPreviousPage).toBeCalled(); + + await waitFor(() => { + expect(getByLabelText('next page')).toBeInTheDocument(); + }); + await userEvent.click(getByLabelText('next page')); + + expect(fetchNextPage).toBeCalled(); + }); +}); diff --git a/plugins/search-react/src/components/SearchResultPager/SearchResultPager.tsx b/plugins/search-react/src/components/SearchResultPager/SearchResultPager.tsx new file mode 100644 index 0000000000..68f8223ca0 --- /dev/null +++ b/plugins/search-react/src/components/SearchResultPager/SearchResultPager.tsx @@ -0,0 +1,66 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; + +import { Button, makeStyles } from '@material-ui/core'; +import ArrowBackIosIcon from '@material-ui/icons/ArrowBackIos'; +import ArrowForwardIosIcon from '@material-ui/icons/ArrowForwardIos'; + +import { useSearch } from '../../context'; + +const useStyles = makeStyles(theme => ({ + root: { + display: 'flex', + justifyContent: 'space-between', + gap: theme.spacing(2), + margin: theme.spacing(2, 0), + }, +})); + +/** + * @public + */ +export const SearchResultPager = () => { + const { fetchNextPage, fetchPreviousPage } = useSearch(); + const classes = useStyles(); + + if (!fetchNextPage && !fetchPreviousPage) { + return <>; + } + + return ( + + ); +}; diff --git a/plugins/search-react/src/components/SearchResultPager/index.ts b/plugins/search-react/src/components/SearchResultPager/index.ts new file mode 100644 index 0000000000..5ff203b15d --- /dev/null +++ b/plugins/search-react/src/components/SearchResultPager/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { SearchResultPager } from './SearchResultPager'; diff --git a/plugins/search/src/components/SearchTracker/SearchTracker.tsx b/plugins/search-react/src/components/SearchTracker/SearchTracker.tsx similarity index 91% rename from plugins/search/src/components/SearchTracker/SearchTracker.tsx rename to plugins/search-react/src/components/SearchTracker/SearchTracker.tsx index 35cfa1ec38..f900e45d45 100644 --- a/plugins/search/src/components/SearchTracker/SearchTracker.tsx +++ b/plugins/search-react/src/components/SearchTracker/SearchTracker.tsx @@ -1,5 +1,5 @@ /* - * Copyright 2021 The Backstage Authors + * Copyright 2022 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,7 +16,7 @@ import React, { useEffect } from 'react'; import { useAnalytics } from '@backstage/core-plugin-api'; -import { useSearch } from '@backstage/plugin-search-react'; +import { useSearch } from '../../context'; /** * Capture search event on term change. diff --git a/plugins/search/src/components/SearchTracker/index.ts b/plugins/search-react/src/components/SearchTracker/index.ts similarity index 93% rename from plugins/search/src/components/SearchTracker/index.ts rename to plugins/search-react/src/components/SearchTracker/index.ts index 5e6a75aa25..9932f2eaec 100644 --- a/plugins/search/src/components/SearchTracker/index.ts +++ b/plugins/search-react/src/components/SearchTracker/index.ts @@ -1,5 +1,5 @@ /* - * Copyright 2021 The Backstage Authors + * Copyright 2022 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/plugins/search-react/src/components/index.ts b/plugins/search-react/src/components/index.ts index 679a254827..263ca4ad59 100644 --- a/plugins/search-react/src/components/index.ts +++ b/plugins/search-react/src/components/index.ts @@ -15,3 +15,8 @@ */ export * from './HighlightedSearchResultText'; +export * from './SearchFilter'; +export * from './SearchResult'; +export * from './SearchResultPager'; +export * from './SearchBar'; +export * from './DefaultResultListItem'; diff --git a/plugins/search/CHANGELOG.md b/plugins/search/CHANGELOG.md index 26bd4a37ba..6283cee29b 100644 --- a/plugins/search/CHANGELOG.md +++ b/plugins/search/CHANGELOG.md @@ -1,5 +1,55 @@ # @backstage/plugin-search +## 0.9.0 + +### Minor Changes + +- 2dc4818541: The pre-alpha ``, ``, `etc...` components have been removed. In the unlikely event you were still using/referencing them, please update to using their non-`*Next` equivalents from either `@backstage/plugin-search-react` or `@backstage/plugin-search`. + +### Patch Changes + +- 8809159148: Components ``, `` (including ``), `` (including `.Checkbox`, `.Select`, and `.Autocomplete` static prop components), ``, and `` are now exported from `@backstage/plugin-search-react`. They are now deprecated in `@backstage/plugin-search` and will be removed in a future release. +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- 5388e6bdc5: Fixed a bug that could cause analytics events in other parts of Backstage to capture nonsensical values resembling search modal state under some circumstances. +- 915700f64f: In order to simplify analytics on top of the search experience in Backstage, the provided `<*ResultListItem />` component now captures a `discover` analytics event instead of a `click` event. This event includes the result rank as its `value` and, like a click, the URL/path clicked to as its `to` attribute. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/plugin-search-common@0.3.5 + - @backstage/plugin-search-react@0.2.1 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## 0.8.2-next.2 + +### Patch Changes + +- 5388e6bdc5: Fixed a bug that could cause analytics events in other parts of Backstage to capture nonsensical values resembling search modal state under some circumstances. +- Updated dependencies + - @backstage/plugin-search-common@0.3.5-next.1 + - @backstage/core-components@0.9.5-next.2 + +## 0.8.2-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + - @backstage/plugin-search-react@0.2.1-next.0 + - @backstage/plugin-search-common@0.3.5-next.0 + +## 0.8.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + ## 0.8.1 ### Patch Changes diff --git a/plugins/search/README.md b/plugins/search/README.md index f4a32c9597..3803b71e43 100644 --- a/plugins/search/README.md +++ b/plugins/search/README.md @@ -13,14 +13,17 @@ Run `yarn dev` in the root directory, and then navigate to [/search](http://loca This search plugin is primarily responsible for the following: - Providing a `` routable extension. -- Exposing various search-related components (like ``, - ``, etc), which can be composed by a Backstage App or by +- Exposing various search-related components (like ``, + ``, etc), which can be composed by a Backstage App or by other Backstage Plugins to power search experiences of all kinds. -- Exposing a ``, which manages search state and API - communication with the Backstage backend. -Don't forget, a lot of functionality is available in backend plugins: +Don't forget, a lot of functionality is available in web libraries and backend plugins: +- `@backstage/plugin-search-react`, which is responsible for: + - Exposing a ``, which manages search state and API + communication with the Backstage backend. + - Exposing the `SearchApi` and its corresponding ref. + - Exposing reusable components, such as `` and ``, etc. - `@backstage/plugin-search-backend-node`, which is responsible for the search index management - `@backstage/plugin-search-backend`, which is responsible for query processing diff --git a/plugins/search/api-report.md b/plugins/search/api-report.md index 21b9df5dda..800fa7e7fc 100644 --- a/plugins/search/api-report.md +++ b/plugins/search/api-report.md @@ -6,36 +6,29 @@ /// import { BackstagePlugin } from '@backstage/core-plugin-api'; +import { DefaultResultListItemProps } from '@backstage/plugin-search-react'; import { IconComponent } from '@backstage/core-plugin-api'; import { InputBaseProps } from '@material-ui/core'; import { ReactElement } from 'react'; import { ReactNode } from 'react'; -import { ResultHighlight } from '@backstage/plugin-search-common'; import { RouteRef } from '@backstage/core-plugin-api'; -import { SearchDocument } from '@backstage/plugin-search-common'; -import { SearchResult as SearchResult_2 } from '@backstage/plugin-search-common'; +import { SearchAutocompleteFilterProps as SearchAutocompleteFilterProps_2 } from '@backstage/plugin-search-react'; +import { SearchBarBaseProps as SearchBarBaseProps_2 } from '@backstage/plugin-search-react'; +import { SearchFilterComponentProps as SearchFilterComponentProps_2 } from '@backstage/plugin-search-react'; +import { SearchResultProps } from '@backstage/plugin-search-react'; -// Warning: (ae-missing-release-tag) "DefaultResultListItem" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -export const DefaultResultListItem: ({ - result, - highlight, - icon, - secondaryAction, - lineClamp, -}: { - icon?: ReactNode; - secondaryAction?: ReactNode; - result: SearchDocument; - highlight?: ResultHighlight | undefined; - lineClamp?: number | undefined; -}) => JSX.Element; +// @public @deprecated (undocumented) +export const DefaultResultListItem: ( + props: DefaultResultListItemProps, +) => JSX.Element; -// Warning: (ae-forgotten-export) The symbol "FiltersProps" needs to be exported by the entry point index.d.ts -// Warning: (ae-missing-release-tag) "Filters" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public @deprecated (undocumented) +export type FilterOptions = { + kind: Array; + lifecycle: Array; +}; + +// @public @deprecated (undocumented) export const Filters: ({ filters, filterOptions, @@ -44,51 +37,57 @@ export const Filters: ({ updateChecked, }: FiltersProps) => JSX.Element; -// Warning: (ae-forgotten-export) The symbol "FiltersButtonProps" needs to be exported by the entry point index.d.ts -// Warning: (ae-missing-release-tag) "FiltersButton" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public @deprecated (undocumented) export const FiltersButton: ({ numberOfSelectedFilters, handleToggleFilters, }: FiltersButtonProps) => JSX.Element; -// Warning: (ae-missing-release-tag) "FiltersState" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public @deprecated +export type FiltersButtonProps = { + numberOfSelectedFilters: number; + handleToggleFilters: () => void; +}; + +// @public @deprecated +export type FiltersProps = { + filters: FiltersState; + filterOptions: FilterOptions; + resetFilters: () => void; + updateSelected: (filter: string) => void; + updateChecked: (filter: string) => void; +}; + +// @public @deprecated (undocumented) export type FiltersState = { selected: string; checked: Array; }; -// Warning: (ae-missing-release-tag) "HomePageSearchBar" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public (undocumented) export const HomePageSearchBar: ({ ...props -}: Partial>) => JSX.Element; +}: Partial>) => JSX.Element; // @public export type HomePageSearchBarProps = Partial< - Omit + Omit >; -// Warning: (ae-missing-release-tag) "SearchPage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public (undocumented) export const Router: () => JSX.Element; -// @public (undocumented) -export type SearchAutocompleteFilterProps = SearchFilterComponentProps & { +// @public @deprecated (undocumented) +export type SearchAutocompleteFilterProps = SearchFilterComponentProps_2 & { filterSelectedOptions?: boolean; limitTags?: number; multiple?: boolean; }; -// @public +// @public @deprecated export const SearchBar: ({ onChange, ...props }: SearchBarProps) => JSX.Element; -// @public +// @public @deprecated export const SearchBarBase: ({ onChange, onKeyDown, @@ -100,9 +99,9 @@ export const SearchBarBase: ({ inputProps: defaultInputProps, endAdornment: defaultEndAdornment, ...props -}: SearchBarBaseProps) => JSX.Element; +}: SearchBarBaseProps_2) => JSX.Element; -// @public +// @public @deprecated export type SearchBarBaseProps = Omit & { debounceTime?: number; clearButton?: boolean; @@ -111,20 +110,10 @@ export type SearchBarBaseProps = Omit & { onChange: (value: string) => void; }; -// Warning: (ae-missing-release-tag) "SearchBarNext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public @deprecated (undocumented) -export const SearchBarNext: ({ - onChange, - ...props -}: Partial) => JSX.Element; - -// @public +// @public @deprecated export type SearchBarProps = Partial; -// Warning: (ae-missing-release-tag) "SearchFilter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public @deprecated (undocumented) export const SearchFilter: { ({ component: Element, ...props }: SearchFilterWrapperProps): JSX.Element; Checkbox( @@ -135,10 +124,10 @@ export const SearchFilter: { props: Omit & SearchFilterComponentProps, ): JSX.Element; - Autocomplete(props: SearchAutocompleteFilterProps): JSX.Element; + Autocomplete(props: SearchAutocompleteFilterProps_2): JSX.Element; }; -// @public (undocumented) +// @public @deprecated (undocumented) export type SearchFilterComponentProps = { className?: string; name: string; @@ -148,30 +137,12 @@ export type SearchFilterComponentProps = { valuesDebounceMs?: number; }; -// Warning: (ae-missing-release-tag) "SearchFilterNext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public @deprecated (undocumented) -export const SearchFilterNext: { - ({ component: Element, ...props }: SearchFilterWrapperProps): JSX.Element; - Checkbox( - props: Omit & - SearchFilterComponentProps, - ): JSX.Element; - Select( - props: Omit & - SearchFilterComponentProps, - ): JSX.Element; - Autocomplete(props: SearchAutocompleteFilterProps): JSX.Element; -}; - -// @public (undocumented) export type SearchFilterWrapperProps = SearchFilterComponentProps & { component: (props: SearchFilterComponentProps) => ReactElement; debug?: boolean; }; -// Warning: (ae-missing-release-tag) "SearchModal" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public (undocumented) export const SearchModal: ({ open, @@ -185,8 +156,6 @@ export interface SearchModalChildrenProps { toggleModal: () => void; } -// Warning: (ae-missing-release-tag) "SearchModalProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public (undocumented) export interface SearchModalProps { children?: (props: SearchModalChildrenProps) => JSX.Element; @@ -217,45 +186,25 @@ export type SearchModalValue = { setOpen: (open: boolean) => void; }; -// Warning: (ae-missing-release-tag) "SearchPage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public (undocumented) export const SearchPage: () => JSX.Element; -// Warning: (ae-missing-release-tag) "SearchPageNext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public @deprecated (undocumented) -export const SearchPageNext: () => JSX.Element; - -// Warning: (ae-missing-release-tag) "searchPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public (undocumented) const searchPlugin: BackstagePlugin< { root: RouteRef; - nextRoot: RouteRef; }, {} >; export { searchPlugin as plugin }; export { searchPlugin }; -// Warning: (ae-missing-release-tag) "SearchResult" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -export const SearchResult: ({ - children, -}: { - children: (results: { results: SearchResult_2[] }) => JSX.Element; -}) => JSX.Element; +// @public @deprecated (undocumented) +export const SearchResult: (props: SearchResultProps) => JSX.Element; -// Warning: (ae-missing-release-tag) "SearchResultPager" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public @deprecated (undocumented) export const SearchResultPager: () => JSX.Element; -// Warning: (ae-missing-release-tag) "SearchType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public (undocumented) export const SearchType: { (props: SearchTypeProps): JSX.Element; @@ -274,7 +223,7 @@ export type SearchTypeAccordionProps = { defaultValue?: string; }; -// @public (undocumented) +// @public export type SearchTypeProps = { className?: string; name: string; @@ -291,29 +240,21 @@ export type SearchTypeTabsProps = { defaultValue?: string; }; -// Warning: (ae-missing-release-tag) "SidebarSearch" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public (undocumented) export const SidebarSearch: (props: SidebarSearchProps) => JSX.Element; -// Warning: (ae-missing-release-tag) "SidebarSearchModal" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public (undocumented) export const SidebarSearchModal: ( props: SidebarSearchModalProps, ) => JSX.Element; -// Warning: (ae-missing-release-tag) "SidebarSearchModalProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export type SidebarSearchModalProps = { icon?: IconComponent; children?: (props: SearchModalChildrenProps) => JSX.Element; }; -// Warning: (ae-missing-release-tag) "SidebarSearchProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export type SidebarSearchProps = { icon?: IconComponent; }; diff --git a/plugins/search/package.json b/plugins/search/package.json index 947b39ebdd..b658490b00 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": "0.8.1", + "version": "0.9.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -33,14 +33,14 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-model": "^1.0.2", + "@backstage/catalog-model": "^1.0.3", "@backstage/config": "^1.0.1", - "@backstage/core-components": "^0.9.4", - "@backstage/core-plugin-api": "^1.0.2", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", "@backstage/errors": "^1.0.0", - "@backstage/plugin-catalog-react": "^1.1.0", - "@backstage/plugin-search-common": "^0.3.4", - "@backstage/plugin-search-react": "^0.2.0", + "@backstage/plugin-catalog-react": "^1.1.1", + "@backstage/plugin-search-common": "^0.3.5", + "@backstage/plugin-search-react": "^0.2.1", "@backstage/theme": "^0.2.15", "@backstage/types": "^1.0.0", "@backstage/version-bridge": "^1.0.1", @@ -57,10 +57,10 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/core-app-api": "^1.0.2", - "@backstage/dev-utils": "^1.0.2", - "@backstage/test-utils": "^1.1.0", + "@backstage/cli": "^0.17.2", + "@backstage/core-app-api": "^1.0.3", + "@backstage/dev-utils": "^1.0.3", + "@backstage/test-utils": "^1.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/react-hooks": "^8.0.0", @@ -68,7 +68,7 @@ "@types/jest": "^26.0.7", "@types/node": "^16.11.26", "cross-fetch": "^3.1.5", - "msw": "^0.35.0" + "msw": "^0.42.0" }, "files": [ "dist" diff --git a/plugins/search/src/components/Filters/Filters.tsx b/plugins/search/src/components/Filters/Filters.tsx index a0ca4914a2..9170961e18 100644 --- a/plugins/search/src/components/Filters/Filters.tsx +++ b/plugins/search/src/components/Filters/Filters.tsx @@ -44,17 +44,34 @@ const useStyles = makeStyles(theme => ({ }, })); +/** + * @public + * @deprecated This type and corresponding component will be removed in a + * future release. + */ export type FiltersState = { selected: string; checked: Array; }; +/** + * @public + * @deprecated This type and corresponding component will be removed in a + * future release. + */ export type FilterOptions = { kind: Array; lifecycle: Array; }; -type FiltersProps = { +/** + * Props for {@link Filters}. + * + * @public + * @deprecated This type and corresponding component will be removed in a + * future release. + */ +export type FiltersProps = { filters: FiltersState; filterOptions: FilterOptions; resetFilters: () => void; @@ -62,6 +79,11 @@ type FiltersProps = { updateChecked: (filter: string) => void; }; +/** + * @public + * @deprecated This component will be removed in a future release. Use + * `SearchFilter` from `@backstage/plugin-search-react` instead. + */ export const Filters = ({ filters, filterOptions, diff --git a/plugins/search/src/components/Filters/FiltersButton.tsx b/plugins/search/src/components/Filters/FiltersButton.tsx index 1c2829defe..890c787689 100644 --- a/plugins/search/src/components/Filters/FiltersButton.tsx +++ b/plugins/search/src/components/Filters/FiltersButton.tsx @@ -28,11 +28,22 @@ const useStyles = makeStyles(theme => ({ }, })); -type FiltersButtonProps = { +/** + * Props for {@link FiltersButton}. + * + * @public + * @deprecated This type and corresponding component will be removed in a + * future release. + */ +export type FiltersButtonProps = { numberOfSelectedFilters: number; handleToggleFilters: () => void; }; +/** + * @public + * @deprecated See `SearchFilter` in `@backstage/plugin-search-react` instead. + */ export const FiltersButton = ({ numberOfSelectedFilters, handleToggleFilters, diff --git a/plugins/search/src/components/Filters/index.tsx b/plugins/search/src/components/Filters/index.tsx index de890e0eaf..dc4d811dbd 100644 --- a/plugins/search/src/components/Filters/index.tsx +++ b/plugins/search/src/components/Filters/index.tsx @@ -15,5 +15,6 @@ */ export { FiltersButton } from './FiltersButton'; +export type { FiltersButtonProps } from './FiltersButton'; export { Filters } from './Filters'; -export type { FiltersState } from './Filters'; +export type { FilterOptions, FiltersProps, FiltersState } from './Filters'; diff --git a/plugins/search/src/components/HomePageComponent/HomePageSearchBar.tsx b/plugins/search/src/components/HomePageComponent/HomePageSearchBar.tsx index bc4b409d57..6e9787c2e0 100644 --- a/plugins/search/src/components/HomePageComponent/HomePageSearchBar.tsx +++ b/plugins/search/src/components/HomePageComponent/HomePageSearchBar.tsx @@ -16,7 +16,10 @@ import React, { useCallback, useState } from 'react'; import { makeStyles } from '@material-ui/core/styles'; -import { SearchBarBase, SearchBarBaseProps } from '../SearchBar'; +import { + SearchBarBase, + SearchBarBaseProps, +} from '@backstage/plugin-search-react'; import { useNavigateToQuery } from '../util'; const useStyles = makeStyles({ @@ -37,9 +40,7 @@ export type HomePageSearchBarProps = Partial< >; /** - * The search bar created specifically for the composable home page - * - * @public + * The search bar created specifically for the composable home page. */ export const HomePageSearchBar = ({ ...props }: HomePageSearchBarProps) => { const classes = useStyles(props); diff --git a/plugins/search/src/components/SearchBar/SearchBar.tsx b/plugins/search/src/components/SearchBar/SearchBar.tsx index b5d55fed8e..a854869976 100644 --- a/plugins/search/src/components/SearchBar/SearchBar.tsx +++ b/plugins/search/src/components/SearchBar/SearchBar.tsx @@ -14,35 +14,20 @@ * limitations under the License. */ -import React, { - ChangeEvent, - KeyboardEvent, - useState, - useEffect, - useCallback, -} from 'react'; -import useDebounce from 'react-use/lib/useDebounce'; -import { configApiRef, useApi } from '@backstage/core-plugin-api'; -import { - InputBase, - InputBaseProps, - InputAdornment, - IconButton, -} from '@material-ui/core'; -import SearchIcon from '@material-ui/icons/Search'; -import ClearButton from '@material-ui/icons/Clear'; +import React, { useCallback } from 'react'; + +import { InputBaseProps } from '@material-ui/core'; import { - SearchContextProvider, + SearchBarBase as RealSearchBarBase, useSearch, - useSearchContextCheck, } from '@backstage/plugin-search-react'; -import { TrackSearch } from '../SearchTracker'; /** * Props for {@link SearchBarBase}. * * @public + * @deprecated Import from `@backstage/plugin-search-react` instead. */ export type SearchBarBaseProps = Omit & { debounceTime?: number; @@ -58,100 +43,15 @@ export type SearchBarBaseProps = Omit & { * Recommended if you don't use Search Provider or Search Context. * * @public + * @deprecated Import from `@backstage/plugin-search-react` instead. */ -export const SearchBarBase = ({ - onChange, - onKeyDown, - onSubmit, - debounceTime = 200, - clearButton = true, - fullWidth = true, - value: defaultValue, - inputProps: defaultInputProps = {}, - endAdornment: defaultEndAdornment, - ...props -}: SearchBarBaseProps) => { - const configApi = useApi(configApiRef); - const [value, setValue] = useState(defaultValue as string); - const hasSearchContext = useSearchContextCheck(); - - useEffect(() => { - setValue(prevValue => - prevValue !== defaultValue ? (defaultValue as string) : prevValue, - ); - }, [defaultValue]); - - useDebounce(() => onChange(value), debounceTime, [value]); - - const handleChange = useCallback( - (e: ChangeEvent) => { - setValue(e.target.value); - }, - [setValue], - ); - - const handleKeyDown = useCallback( - (e: KeyboardEvent) => { - if (onKeyDown) onKeyDown(e); - if (onSubmit && e.key === 'Enter') { - onSubmit(); - } - }, - [onKeyDown, onSubmit], - ); - - const handleClear = useCallback(() => { - onChange(''); - }, [onChange]); - - const placeholder = `Search in ${ - configApi.getOptionalString('app.title') || 'Backstage' - }`; - - const startAdornment = ( - - - - - - ); - - const endAdornment = ( - - - - - - ); - - const searchBar = ( - - - - ); - - return hasSearchContext ? ( - searchBar - ) : ( - {searchBar} - ); -}; +export const SearchBarBase = RealSearchBarBase; /** * Props for {@link SearchBar}. * * @public + * @deprecated Import from `@backstage/plugin-search-react` instead. */ export type SearchBarProps = Partial; @@ -159,6 +59,7 @@ export type SearchBarProps = Partial; * Recommended search bar when you use the Search Provider or Search Context. * * @public + * @deprecated Import from `@backstage/plugin-search-react` instead. */ export const SearchBar = ({ onChange, ...props }: SearchBarProps) => { const { term, setTerm } = useSearch(); diff --git a/plugins/search/src/components/SearchFilter/SearchFilter.Autocomplete.tsx b/plugins/search/src/components/SearchFilter/SearchFilter.Autocomplete.tsx index 03f4f28943..3db5112b88 100644 --- a/plugins/search/src/components/SearchFilter/SearchFilter.Autocomplete.tsx +++ b/plugins/search/src/components/SearchFilter/SearchFilter.Autocomplete.tsx @@ -14,103 +14,14 @@ * limitations under the License. */ -import React, { ChangeEvent, useState } from 'react'; -import { Chip, TextField } from '@material-ui/core'; -import { - Autocomplete, - AutocompleteGetTagProps, - AutocompleteRenderInputParams, -} from '@material-ui/lab'; -import { useSearch } from '@backstage/plugin-search-react'; -import { useAsyncFilterValues, useDefaultFilterValue } from './hooks'; -import { SearchFilterComponentProps } from './SearchFilter'; +import { SearchFilterComponentProps } from '@backstage/plugin-search-react'; /** * @public + * @deprecated Import from `@backstage/plugin-search-react` instead. */ export type SearchAutocompleteFilterProps = SearchFilterComponentProps & { filterSelectedOptions?: boolean; limitTags?: number; multiple?: boolean; }; - -export const AutocompleteFilter = (props: SearchAutocompleteFilterProps) => { - const { - className, - defaultValue, - name, - values: givenValues, - valuesDebounceMs, - label, - filterSelectedOptions, - limitTags, - multiple, - } = props; - const [inputValue, setInputValue] = useState(''); - useDefaultFilterValue(name, defaultValue); - const asyncValues = - typeof givenValues === 'function' ? givenValues : undefined; - const defaultValues = - typeof givenValues === 'function' ? undefined : givenValues; - const { value: values, loading } = useAsyncFilterValues( - asyncValues, - inputValue, - defaultValues, - valuesDebounceMs, - ); - const { filters, setFilters } = useSearch(); - const filterValue = - (filters[name] as string | string[] | undefined) || (multiple ? [] : null); - - // Set new filter values on input change. - const handleChange = ( - _: ChangeEvent<{}>, - newValue: string | string[] | null, - ) => { - setFilters(prevState => { - const { [name]: filter, ...others } = prevState; - - if (newValue) { - return { ...others, [name]: newValue }; - } - return { ...others }; - }); - }; - - // Provide the input field. - const renderInput = (params: AutocompleteRenderInputParams) => ( - - ); - - // Render tags as primary-colored chips. - const renderTags = ( - tagValue: string[], - getTagProps: AutocompleteGetTagProps, - ) => - tagValue.map((option: string, index: number) => ( - - )); - - return ( - setInputValue(newValue)} - renderInput={renderInput} - renderTags={renderTags} - /> - ); -}; diff --git a/plugins/search/src/components/SearchFilter/SearchFilter.tsx b/plugins/search/src/components/SearchFilter/SearchFilter.tsx index bfbab8531c..e58bbd5c14 100644 --- a/plugins/search/src/components/SearchFilter/SearchFilter.tsx +++ b/plugins/search/src/components/SearchFilter/SearchFilter.tsx @@ -14,33 +14,18 @@ * limitations under the License. */ -import React, { ReactElement, ChangeEvent } from 'react'; -import { - makeStyles, - FormControl, - FormControlLabel, - InputLabel, - Checkbox, - Select, - MenuItem, - FormLabel, -} from '@material-ui/core'; +import React, { ReactElement } from 'react'; import { AutocompleteFilter, + CheckboxFilter, SearchAutocompleteFilterProps, -} from './SearchFilter.Autocomplete'; -import { useSearch } from '@backstage/plugin-search-react'; -import { useAsyncFilterValues, useDefaultFilterValue } from './hooks'; - -const useStyles = makeStyles({ - label: { - textTransform: 'capitalize', - }, -}); + SelectFilter, +} from '@backstage/plugin-search-react'; /** * @public + * @deprecated Import from `@backstage/plugin-search-react` instead. */ export type SearchFilterComponentProps = { className?: string; @@ -63,152 +48,33 @@ export type SearchFilterComponentProps = { /** * @public + * @deprecated Import from `@backstage/plugin-search-react` instead. */ export type SearchFilterWrapperProps = SearchFilterComponentProps & { component: (props: SearchFilterComponentProps) => ReactElement; debug?: boolean; }; -const CheckboxFilter = (props: SearchFilterComponentProps) => { - const { - className, - defaultValue, - label, - name, - values: givenValues = [], - valuesDebounceMs, - } = props; - const classes = useStyles(); - const { filters, setFilters } = useSearch(); - useDefaultFilterValue(name, defaultValue); - const asyncValues = - typeof givenValues === 'function' ? givenValues : undefined; - const defaultValues = - typeof givenValues === 'function' ? undefined : givenValues; - const { value: values = [], loading } = useAsyncFilterValues( - asyncValues, - '', - defaultValues, - valuesDebounceMs, - ); - - const handleChange = (e: ChangeEvent) => { - const { - target: { value, checked }, - } = e; - - setFilters(prevFilters => { - const { [name]: filter, ...others } = prevFilters; - const rest = ((filter as string[]) || []).filter(i => i !== value); - const items = checked ? [...rest, value] : rest; - return items.length ? { ...others, [name]: items } : others; - }); - }; - - return ( - - {label ? {label} : null} - {values.map((value: string) => ( - - } - label={value} - /> - ))} - - ); -}; - -const SelectFilter = (props: SearchFilterComponentProps) => { - const { - className, - defaultValue, - label, - name, - values: givenValues, - valuesDebounceMs, - } = props; - const classes = useStyles(); - useDefaultFilterValue(name, defaultValue); - const asyncValues = - typeof givenValues === 'function' ? givenValues : undefined; - const defaultValues = - typeof givenValues === 'function' ? undefined : givenValues; - const { value: values = [], loading } = useAsyncFilterValues( - asyncValues, - '', - defaultValues, - valuesDebounceMs, - ); - const { filters, setFilters } = useSearch(); - - const handleChange = (e: ChangeEvent<{ value: unknown }>) => { - const { - target: { value }, - } = e; - - setFilters(prevFilters => { - const { [name]: filter, ...others } = prevFilters; - return value ? { ...others, [name]: value as string } : others; - }); - }; - - return ( - - {label ? ( - - {label} - - ) : null} - - - ); -}; - +/** + * @public + * @deprecated Import from `@backstage/plugin-search-react` instead. + */ const SearchFilter = ({ component: Element, ...props }: SearchFilterWrapperProps) => ; +/** + * @deprecated Import from `@backstage/plugin-search-react` instead. + */ SearchFilter.Checkbox = ( props: Omit & SearchFilterComponentProps, ) => ; +/** + * @deprecated Import from `@backstage/plugin-search-react` instead. + */ SearchFilter.Select = ( props: Omit & SearchFilterComponentProps, @@ -218,18 +84,12 @@ SearchFilter.Select = ( * A control surface for a given filter field name, rendered as an autocomplete * textfield. A hard-coded list of values may be provided, or an async function * which returns values may be provided instead. + * * @public + * @deprecated Import from `@backstage/plugin-search-react` instead. */ SearchFilter.Autocomplete = (props: SearchAutocompleteFilterProps) => ( ); -/** - * @deprecated This component was used for rapid prototyping of the Backstage - * Search platform. Now that the API has stabilized, you should use the - * component instead. This component will be removed in an - * upcoming release. - */ -const SearchFilterNext = SearchFilter; - -export { SearchFilter, SearchFilterNext }; +export { SearchFilter }; diff --git a/plugins/search/src/components/SearchFilter/index.ts b/plugins/search/src/components/SearchFilter/index.ts index 86cd66ff6b..3050330038 100644 --- a/plugins/search/src/components/SearchFilter/index.ts +++ b/plugins/search/src/components/SearchFilter/index.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -export { SearchFilter, SearchFilterNext } from './SearchFilter'; +export { SearchFilter } from './SearchFilter'; export type { SearchFilterComponentProps, SearchFilterWrapperProps, diff --git a/plugins/search/src/components/SearchModal/SearchModal.stories.tsx b/plugins/search/src/components/SearchModal/SearchModal.stories.tsx index a4e92f53ec..aad018c334 100644 --- a/plugins/search/src/components/SearchModal/SearchModal.stories.tsx +++ b/plugins/search/src/components/SearchModal/SearchModal.stories.tsx @@ -27,17 +27,17 @@ import { import { makeStyles } from '@material-ui/core/styles'; import React, { ComponentType } from 'react'; import { rootRouteRef } from '../../plugin'; -import { DefaultResultListItem } from '../DefaultResultListItem'; import { SearchBar } from '../SearchBar'; import { + DefaultResultListItem, searchApiRef, MockSearchApi, SearchContextProvider, + SearchResult, + SearchResultPager, } from '@backstage/plugin-search-react'; import { TestApiProvider } from '@backstage/test-utils'; import { SearchModal } from './SearchModal'; -import { SearchResult } from '../SearchResult'; -import { SearchResultPager } from '../SearchResultPager'; import { SearchType } from '../SearchType'; import { useSearchModal } from './useSearchModal'; diff --git a/plugins/search/src/components/SearchModal/SearchModal.tsx b/plugins/search/src/components/SearchModal/SearchModal.tsx index b3df52d42e..6c37f20001 100644 --- a/plugins/search/src/components/SearchModal/SearchModal.tsx +++ b/plugins/search/src/components/SearchModal/SearchModal.tsx @@ -28,14 +28,14 @@ import { } from '@material-ui/core'; import LaunchIcon from '@material-ui/icons/Launch'; import { makeStyles } from '@material-ui/core/styles'; -import { SearchBar } from '../SearchBar'; -import { DefaultResultListItem } from '../DefaultResultListItem'; -import { SearchResult } from '../SearchResult'; import { + DefaultResultListItem, SearchContextProvider, + SearchBar, + SearchResult, + SearchResultPager, useSearch, } from '@backstage/plugin-search-react'; -import { SearchResultPager } from '../SearchResultPager'; import { useRouteRef } from '@backstage/core-plugin-api'; import { Link, useContent } from '@backstage/core-components'; import { rootRouteRef } from '../../plugin'; @@ -50,6 +50,9 @@ export interface SearchModalChildrenProps { toggleModal: () => void; } +/** + * @public + **/ export interface SearchModalProps { /** * If true, it renders the modal. @@ -167,6 +170,9 @@ export const Modal = ({ toggleModal }: SearchModalProps) => { ); }; +/** + * @public + */ export const SearchModal = ({ open = true, hidden, diff --git a/plugins/search/src/components/SearchModal/useSearchModal.tsx b/plugins/search/src/components/SearchModal/useSearchModal.tsx index b813a8db7a..5b320934c2 100644 --- a/plugins/search/src/components/SearchModal/useSearchModal.tsx +++ b/plugins/search/src/components/SearchModal/useSearchModal.tsx @@ -37,7 +37,7 @@ export type SearchModalValue = { const SearchModalContext = createVersionedContext<{ 1: SearchModalValue | undefined; -}>('analytics-context'); +}>('search-modal-context'); /** * Props for the SearchModalProvider. diff --git a/plugins/search/src/components/SearchPage/SearchPage.tsx b/plugins/search/src/components/SearchPage/SearchPage.tsx index 29a22a6f5c..7145c7a5ef 100644 --- a/plugins/search/src/components/SearchPage/SearchPage.tsx +++ b/plugins/search/src/components/SearchPage/SearchPage.tsx @@ -87,6 +87,9 @@ export const UrlUpdater = () => { return null; }; +/** + * @public + */ export const SearchPage = () => { const outlet = useOutlet(); diff --git a/plugins/search/src/components/SearchResultPager/SearchResultPager.tsx b/plugins/search/src/components/SearchResultPager/SearchResultPager.tsx index 6f4c8628a6..287e485dd1 100644 --- a/plugins/search/src/components/SearchResultPager/SearchResultPager.tsx +++ b/plugins/search/src/components/SearchResultPager/SearchResultPager.tsx @@ -29,6 +29,10 @@ const useStyles = makeStyles(theme => ({ }, })); +/** + * @public + * @deprecated Import from `@backstage/plugin-search-react` instead. + */ export const SearchResultPager = () => { const { fetchNextPage, fetchPreviousPage } = useSearch(); const classes = useStyles(); diff --git a/plugins/search/src/components/SearchType/SearchType.tsx b/plugins/search/src/components/SearchType/SearchType.tsx index 79c3dda2b9..b20e8b5fba 100644 --- a/plugins/search/src/components/SearchType/SearchType.tsx +++ b/plugins/search/src/components/SearchType/SearchType.tsx @@ -47,6 +47,8 @@ const useStyles = makeStyles(theme => ({ })); /** + * Props for {@link SearchType}. + * * @public */ export type SearchTypeProps = { @@ -56,6 +58,9 @@ export type SearchTypeProps = { defaultValue?: string[] | string | null; }; +/** + * @public + */ const SearchType = (props: SearchTypeProps) => { const { className, defaultValue, name, values = [] } = props; const classes = useStyles(); diff --git a/plugins/search/src/components/SidebarSearch/SidebarSearch.tsx b/plugins/search/src/components/SidebarSearch/SidebarSearch.tsx index 25d41fa10f..36ead2ab15 100644 --- a/plugins/search/src/components/SidebarSearch/SidebarSearch.tsx +++ b/plugins/search/src/components/SidebarSearch/SidebarSearch.tsx @@ -21,10 +21,18 @@ import { rootRouteRef } from '../../plugin'; import { useRouteRef, IconComponent } from '@backstage/core-plugin-api'; import { SidebarSearchField, useContent } from '@backstage/core-components'; +/** + * Props for {@link SidebarSearch}. + * + * @public + */ export type SidebarSearchProps = { icon?: IconComponent; }; +/** + * @public + */ export const SidebarSearch = (props: SidebarSearchProps) => { const searchRoute = useRouteRef(rootRouteRef); const { focusContent } = useContent(); diff --git a/plugins/search/src/components/SidebarSearchModal/SidebarSearchModal.tsx b/plugins/search/src/components/SidebarSearchModal/SidebarSearchModal.tsx index 191a09395c..fb4afc75fd 100644 --- a/plugins/search/src/components/SidebarSearchModal/SidebarSearchModal.tsx +++ b/plugins/search/src/components/SidebarSearchModal/SidebarSearchModal.tsx @@ -24,6 +24,11 @@ import { useSearchModal, } from '../SearchModal'; +/** + * Props for {@link SidebarSearchModal}. + * + * @public + */ export type SidebarSearchModalProps = { icon?: IconComponent; children?: (props: SearchModalChildrenProps) => JSX.Element; diff --git a/plugins/search/src/index.ts b/plugins/search/src/index.ts index 1457ac781d..fd0e70c61a 100644 --- a/plugins/search/src/index.ts +++ b/plugins/search/src/index.ts @@ -21,14 +21,19 @@ */ export { Filters, FiltersButton } from './components/Filters'; -export type { FiltersState } from './components/Filters'; +export type { + FilterOptions, + FiltersState, + FiltersProps, + FiltersButtonProps, +} from './components/Filters'; export type { HomePageSearchBarProps } from './components/HomePageComponent'; export { SearchBar, SearchBarBase } from './components/SearchBar'; export type { SearchBarBaseProps, SearchBarProps, } from './components/SearchBar'; -export { SearchFilter, SearchFilterNext } from './components/SearchFilter'; +export { SearchFilter } from './components/SearchFilter'; export type { SearchAutocompleteFilterProps, SearchFilterComponentProps, @@ -60,9 +65,7 @@ export type { SidebarSearchModalProps } from './components/SidebarSearchModal'; export { DefaultResultListItem, HomePageSearchBar, - SearchBarNext, SearchPage, - SearchPageNext, searchPlugin as plugin, searchPlugin, SearchResult, diff --git a/plugins/search/src/plugin.ts b/plugins/search/src/plugin.ts index 96e7bc1eed..19fd501129 100644 --- a/plugins/search/src/plugin.ts +++ b/plugins/search/src/plugin.ts @@ -15,7 +15,11 @@ */ import { SearchClient } from './apis'; -import { searchApiRef } from '@backstage/plugin-search-react'; +import { + searchApiRef, + SearchResult as RealSearchResult, + DefaultResultListItem as RealDefaultResultListItem, +} from '@backstage/plugin-search-react'; import { createApiFactory, createPlugin, @@ -30,10 +34,9 @@ export const rootRouteRef = createRouteRef({ id: 'search', }); -export const rootNextRouteRef = createRouteRef({ - id: 'search:next', -}); - +/** + * @public + */ export const searchPlugin = createPlugin({ id: 'search', apis: [ @@ -47,10 +50,12 @@ export const searchPlugin = createPlugin({ ], routes: { root: rootRouteRef, - nextRoot: rootNextRouteRef, }, }); +/** + * @public + */ export const SearchPage = searchPlugin.provide( createRoutableExtension({ name: 'SearchPage', @@ -60,67 +65,14 @@ export const SearchPage = searchPlugin.provide( ); /** - * @deprecated This component was used for rapid prototyping of the Backstage - * Search platform. Now that the API has stabilized, you should use the - * component instead. This component will be removed in an - * upcoming release. + * @public + * @deprecated Import from `@backstage/plugin-search-react` instead. */ -export const SearchPageNext = searchPlugin.provide( - createRoutableExtension({ - name: 'SearchPageNext', - component: () => import('./components/SearchPage').then(m => m.SearchPage), - mountPoint: rootNextRouteRef, - }), -); - -export const SearchBar = searchPlugin.provide( - createComponentExtension({ - name: 'SearchBar', - component: { - lazy: () => import('./components/SearchBar').then(m => m.SearchBar), - }, - }), -); +export const SearchResult = RealSearchResult; /** - * @deprecated This component was used for rapid prototyping of the Backstage - * Search platform. Now that the API has stabilized, you should use the - * component instead. This component will be removed in an - * upcoming release. + * @public */ -export const SearchBarNext = searchPlugin.provide( - createComponentExtension({ - name: 'SearchBarNext', - component: { - lazy: () => import('./components/SearchBar').then(m => m.SearchBar), - }, - }), -); - -export const SearchResult = searchPlugin.provide( - createComponentExtension({ - name: 'SearchResult', - component: { - lazy: () => import('./components/SearchResult').then(m => m.SearchResult), - }, - }), -); - -/** - * @deprecated This component was used for rapid prototyping of the Backstage - * Search platform. Now that the API has stabilized, you should use the - * component instead. This component will be removed in an - * upcoming release. - */ -export const SearchResultNext = searchPlugin.provide( - createComponentExtension({ - name: 'SearchResultNext', - component: { - lazy: () => import('./components/SearchResult').then(m => m.SearchResult), - }, - }), -); - export const SidebarSearchModal = searchPlugin.provide( createComponentExtension({ name: 'SidebarSearchModal', @@ -133,18 +85,15 @@ export const SidebarSearchModal = searchPlugin.provide( }), ); -export const DefaultResultListItem = searchPlugin.provide( - createComponentExtension({ - name: 'DefaultResultListItem', - component: { - lazy: () => - import('./components/DefaultResultListItem').then( - m => m.DefaultResultListItem, - ), - }, - }), -); +/** + * @public + * @deprecated Import from `@backstage/plugin-search-react` instead. + */ +export const DefaultResultListItem = RealDefaultResultListItem; +/** + * @public + */ export const HomePageSearchBar = searchPlugin.provide( createComponentExtension({ name: 'HomePageSearchBar', diff --git a/plugins/sentry/CHANGELOG.md b/plugins/sentry/CHANGELOG.md index e3e437c2c4..1a469a83af 100644 --- a/plugins/sentry/CHANGELOG.md +++ b/plugins/sentry/CHANGELOG.md @@ -1,5 +1,37 @@ # @backstage/plugin-sentry +## 0.3.44 + +### Patch Changes + +- c55f6cb22d: Exported `isSentryAvailable` which can be used to determine if sentry is available. +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## 0.3.44-next.1 + +### Patch Changes + +- c55f6cb22d: Exported `isSentryAvailable` which can be used to determine if sentry is available. +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## 0.3.44-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + ## 0.3.43 ### Patch Changes diff --git a/plugins/sentry/api-report.md b/plugins/sentry/api-report.md index 5ad2992ee4..043fa7a1fd 100644 --- a/plugins/sentry/api-report.md +++ b/plugins/sentry/api-report.md @@ -31,6 +31,9 @@ export const EntitySentryContent: ({ tableOptions, }: SentryPageProps) => JSX.Element; +// @public +export const isSentryAvailable: (entity: Entity) => boolean; + // Warning: (ae-missing-release-tag) "MockSentryApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) diff --git a/plugins/sentry/package.json b/plugins/sentry/package.json index ed6ce645e7..a75447f0ab 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.3.43", + "version": "0.3.44", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -35,10 +35,10 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-model": "^1.0.2", - "@backstage/core-components": "^0.9.4", - "@backstage/core-plugin-api": "^1.0.2", - "@backstage/plugin-catalog-react": "^1.1.0", + "@backstage/catalog-model": "^1.0.3", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", + "@backstage/plugin-catalog-react": "^1.1.1", "@backstage/theme": "^0.2.15", "@material-table/core": "^3.1.0", "@material-ui/core": "^4.12.2", @@ -53,10 +53,10 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/core-app-api": "^1.0.2", - "@backstage/dev-utils": "^1.0.2", - "@backstage/test-utils": "^1.1.0", + "@backstage/cli": "^0.17.2", + "@backstage/core-app-api": "^1.0.3", + "@backstage/dev-utils": "^1.0.3", + "@backstage/test-utils": "^1.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", @@ -65,7 +65,7 @@ "@types/node": "^16.11.26", "@types/react": "^16.13.1 || ^17.0.0", "cross-fetch": "^3.1.5", - "msw": "^0.35.0" + "msw": "^0.42.0" }, "files": [ "dist", diff --git a/plugins/sentry/src/components/index.ts b/plugins/sentry/src/components/index.ts index 210ba8d718..9af8a05853 100644 --- a/plugins/sentry/src/components/index.ts +++ b/plugins/sentry/src/components/index.ts @@ -15,3 +15,4 @@ */ export * from './SentryIssuesWidget'; +export { isSentryAvailable } from './isSentryAvailable'; diff --git a/plugins/techdocs/src/reader/transformers/injectCss.ts b/plugins/sentry/src/components/isSentryAvailable.ts similarity index 65% rename from plugins/techdocs/src/reader/transformers/injectCss.ts rename to plugins/sentry/src/components/isSentryAvailable.ts index c847d3e1d8..1a7d26f97c 100644 --- a/plugins/techdocs/src/reader/transformers/injectCss.ts +++ b/plugins/sentry/src/components/isSentryAvailable.ts @@ -14,18 +14,12 @@ * limitations under the License. */ -import type { Transformer } from './transformer'; +import { Entity } from '@backstage/catalog-model'; +import { SENTRY_PROJECT_SLUG_ANNOTATION } from './useProjectSlug'; -type InjectCssOptions = { - css: string; -}; - -export const injectCss = ({ css }: InjectCssOptions): Transformer => { - return dom => { - dom - .getElementsByTagName('head')[0] - .insertAdjacentHTML('beforeend', ``); - - return dom; - }; -}; +/** + * @public + * Checks to see if sentry is available + */ +export const isSentryAvailable = (entity: Entity) => + Boolean(entity.metadata.annotations?.[SENTRY_PROJECT_SLUG_ANNOTATION]); diff --git a/plugins/shortcuts/CHANGELOG.md b/plugins/shortcuts/CHANGELOG.md index 919aacaaed..4513e09dab 100644 --- a/plugins/shortcuts/CHANGELOG.md +++ b/plugins/shortcuts/CHANGELOG.md @@ -1,5 +1,30 @@ # @backstage/plugin-shortcuts +## 0.2.7 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + +## 0.2.7-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + +## 0.2.7-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.9.5-next.0 + ## 0.2.6 ### Patch Changes diff --git a/plugins/shortcuts/package.json b/plugins/shortcuts/package.json index eb2b6b4a05..d1d3f9fba4 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.2.6", + "version": "0.2.7", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -24,8 +24,8 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/core-components": "^0.9.4", - "@backstage/core-plugin-api": "^1.0.2", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", "@backstage/theme": "^0.2.15", "@backstage/types": "^1.0.0", "@material-ui/core": "^4.12.2", @@ -42,17 +42,17 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/core-app-api": "^1.0.2", - "@backstage/dev-utils": "^1.0.2", - "@backstage/test-utils": "^1.1.0", + "@backstage/cli": "^0.17.2", + "@backstage/core-app-api": "^1.0.3", + "@backstage/dev-utils": "^1.0.3", + "@backstage/test-utils": "^1.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", "@types/jest": "^26.0.7", "@types/node": "^16.11.26", "cross-fetch": "^3.1.5", - "msw": "^0.35.0" + "msw": "^0.42.0" }, "files": [ "dist" diff --git a/plugins/shortcuts/src/ShortcutItem.test.tsx b/plugins/shortcuts/src/ShortcutItem.test.tsx index 90b8ff06e5..b98dd2dc0b 100644 --- a/plugins/shortcuts/src/ShortcutItem.test.tsx +++ b/plugins/shortcuts/src/ShortcutItem.test.tsx @@ -20,7 +20,7 @@ import { ShortcutItem } from './ShortcutItem'; import { Shortcut } from './types'; import { LocalStoredShortcuts } from './api'; import { MockStorageApi, renderInTestApp } from '@backstage/test-utils'; -import { SidebarContext } from '@backstage/core-components'; +import { SidebarOpenStateProvider } from '@backstage/core-components'; describe('ShortcutItem', () => { const shortcut: Shortcut = { @@ -32,9 +32,9 @@ describe('ShortcutItem', () => { it('displays the shortcut', async () => { await renderInTestApp( - {} }}> + {} }}> - , + , ); expect(screen.getByText('ST')).toBeInTheDocument(); expect(screen.getByText('some title')).toBeInTheDocument(); diff --git a/plugins/shortcuts/src/Shortcuts.test.tsx b/plugins/shortcuts/src/Shortcuts.test.tsx index 8cee9a6622..02b351d3a4 100644 --- a/plugins/shortcuts/src/Shortcuts.test.tsx +++ b/plugins/shortcuts/src/Shortcuts.test.tsx @@ -24,12 +24,12 @@ import { screen, waitFor } from '@testing-library/react'; import { Shortcuts } from './Shortcuts'; import { LocalStoredShortcuts, shortcutsApiRef } from './api'; -import { SidebarContext } from '@backstage/core-components'; +import { SidebarOpenStateProvider } from '@backstage/core-components'; describe('Shortcuts', () => { it('displays an add button', async () => { await renderInTestApp( - {} }}> + {} }}> { > - , + , ); await waitFor(() => !screen.queryByTestId('progress')); expect(screen.getByText('Add Shortcuts')).toBeInTheDocument(); diff --git a/plugins/sonarqube/CHANGELOG.md b/plugins/sonarqube/CHANGELOG.md index 10a4ffcd32..2b3d6840da 100644 --- a/plugins/sonarqube/CHANGELOG.md +++ b/plugins/sonarqube/CHANGELOG.md @@ -1,5 +1,44 @@ # @backstage/plugin-sonarqube +## 0.3.6 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- ee2cd642c5: Updated dependency `rc-progress` to `3.3.3`. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## 0.3.6-next.2 + +### Patch Changes + +- ee2cd642c5: Updated dependency `rc-progress` to `3.3.3`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.2 + +## 0.3.6-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## 0.3.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + ## 0.3.5 ### Patch Changes diff --git a/plugins/sonarqube/package.json b/plugins/sonarqube/package.json index 30cc4e9a61..d9310ffdde 100644 --- a/plugins/sonarqube/package.json +++ b/plugins/sonarqube/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-sonarqube", "description": "", - "version": "0.3.5", + "version": "0.3.6", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -36,33 +36,33 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-model": "^1.0.2", - "@backstage/core-components": "^0.9.4", - "@backstage/core-plugin-api": "^1.0.2", - "@backstage/plugin-catalog-react": "^1.1.0", + "@backstage/catalog-model": "^1.0.3", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", + "@backstage/plugin-catalog-react": "^1.1.1", "@backstage/theme": "^0.2.15", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", "@material-ui/styles": "^4.10.0", "cross-fetch": "^3.1.5", - "rc-progress": "3.3.2", + "rc-progress": "3.3.3", "react-use": "^17.2.4" }, "peerDependencies": { "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/core-app-api": "^1.0.2", - "@backstage/dev-utils": "^1.0.2", - "@backstage/test-utils": "^1.1.0", + "@backstage/cli": "^0.17.2", + "@backstage/core-app-api": "^1.0.3", + "@backstage/dev-utils": "^1.0.3", + "@backstage/test-utils": "^1.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", "@types/jest": "^26.0.7", "@types/node": "^16.11.26", - "msw": "^0.35.0" + "msw": "^0.42.0" }, "files": [ "dist", diff --git a/plugins/splunk-on-call/CHANGELOG.md b/plugins/splunk-on-call/CHANGELOG.md index 429bafb23e..e65afb1285 100644 --- a/plugins/splunk-on-call/CHANGELOG.md +++ b/plugins/splunk-on-call/CHANGELOG.md @@ -1,5 +1,35 @@ # @backstage/plugin-splunk-on-call +## 0.3.30 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## 0.3.30-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## 0.3.30-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + ## 0.3.29 ### Patch Changes diff --git a/plugins/splunk-on-call/package.json b/plugins/splunk-on-call/package.json index 3e79fcee52..baecd2f9c5 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.3.29", + "version": "0.3.30", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -34,10 +34,10 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-model": "^1.0.2", - "@backstage/core-components": "^0.9.4", - "@backstage/core-plugin-api": "^1.0.2", - "@backstage/plugin-catalog-react": "^1.1.0", + "@backstage/catalog-model": "^1.0.3", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", + "@backstage/plugin-catalog-react": "^1.1.1", "@backstage/theme": "^0.2.15", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -51,10 +51,10 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/core-app-api": "^1.0.2", - "@backstage/dev-utils": "^1.0.2", - "@backstage/test-utils": "^1.1.0", + "@backstage/cli": "^0.17.2", + "@backstage/core-app-api": "^1.0.3", + "@backstage/dev-utils": "^1.0.3", + "@backstage/test-utils": "^1.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", @@ -62,7 +62,7 @@ "@types/luxon": "^2.0.4", "@types/node": "^16.11.26", "cross-fetch": "^3.1.5", - "msw": "^0.35.0" + "msw": "^0.42.0" }, "configSchema": "config.d.ts", "files": [ diff --git a/plugins/stack-overflow-backend/CHANGELOG.md b/plugins/stack-overflow-backend/CHANGELOG.md index bca947fecb..9f3a2d98a3 100644 --- a/plugins/stack-overflow-backend/CHANGELOG.md +++ b/plugins/stack-overflow-backend/CHANGELOG.md @@ -4,7 +4,15 @@ ### Patch Changes -- Added apiKey configuration to prevent encoding the key value +- Updated dependencies + - @backstage/plugin-search-common@0.3.5 + +## 0.1.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-common@0.3.5-next.0 ## 0.1.1 diff --git a/plugins/stack-overflow-backend/package.json b/plugins/stack-overflow-backend/package.json index c4416be8b6..cc41da042e 100644 --- a/plugins/stack-overflow-backend/package.json +++ b/plugins/stack-overflow-backend/package.json @@ -33,7 +33,7 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/plugin-search-common": "^0.3.4", + "@backstage/plugin-search-common": "^0.3.5", "@backstage/config": "^1.0.1", "qs": "^6.9.4", "cross-fetch": "^3.1.5", diff --git a/plugins/stack-overflow/CHANGELOG.md b/plugins/stack-overflow/CHANGELOG.md index bdb144c555..d0f5f39f4b 100644 --- a/plugins/stack-overflow/CHANGELOG.md +++ b/plugins/stack-overflow/CHANGELOG.md @@ -1,5 +1,35 @@ # @backstage/plugin-stack-overflow +## 0.1.2 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-search-common@0.3.5 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/plugin-home@0.4.22 + +## 0.1.2-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/plugin-home@0.4.22-next.1 + - @backstage/plugin-search-common@0.3.5-next.0 + +## 0.1.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.9.5-next.0 + - @backstage/plugin-home@0.4.22-next.0 + ## 0.1.1 ### Patch Changes diff --git a/plugins/stack-overflow/README.md b/plugins/stack-overflow/README.md index 6beb843c7d..96a8f524e3 100644 --- a/plugins/stack-overflow/README.md +++ b/plugins/stack-overflow/README.md @@ -23,6 +23,8 @@ This stack overflow frontend plugin is primarily responsible for the following: #### Use specific search result list item for Stack Overflow Question +> Note: For Stack Overflow specific search results to be returned, it needs to be indexed. Use the [stack-overflow-backend plugin](https://github.com/backstage/backstage/blob/master/plugins/stack-overflow-backend/README.md) to index Stack Overflow Questions to search. + When you have your `packages/app/src/components/search/SearchPage.tsx` file ready to make modifications, add the following code snippet to add the `StackOverflowSearchResultListItem` when the type of the search results are `stack-overflow`. ```tsx diff --git a/plugins/stack-overflow/api-report.md b/plugins/stack-overflow/api-report.md index 277b0272d1..e4912fcd18 100644 --- a/plugins/stack-overflow/api-report.md +++ b/plugins/stack-overflow/api-report.md @@ -16,6 +16,9 @@ export const HomePageStackOverflowQuestions: ( } & StackOverflowQuestionsContentProps, ) => JSX.Element; +// @public +export const StackOverflowIcon: () => JSX.Element; + // @public export const stackOverflowPlugin: BackstagePlugin<{}, {}>; @@ -31,6 +34,7 @@ export type StackOverflowQuestion = { // @public export type StackOverflowQuestionsContentProps = { requestParams: StackOverflowQuestionsRequestParams; + icon?: React.ReactNode; }; // @public diff --git a/plugins/stack-overflow/package.json b/plugins/stack-overflow/package.json index 8fee274fb4..1438ddcd50 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.1", + "version": "0.1.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -24,10 +24,10 @@ }, "dependencies": { "@backstage/config": "^1.0.1", - "@backstage/core-components": "^0.9.4", - "@backstage/core-plugin-api": "^1.0.2", - "@backstage/plugin-home": "^0.4.21", - "@backstage/plugin-search-common": "^0.3.4", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", + "@backstage/plugin-home": "^0.4.22", + "@backstage/plugin-search-common": "^0.3.5", "@backstage/theme": "^0.2.15", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -42,15 +42,15 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/core-app-api": "^1.0.2", - "@backstage/dev-utils": "^1.0.2", - "@backstage/test-utils": "^1.1.0", + "@backstage/cli": "^0.17.2", + "@backstage/core-app-api": "^1.0.3", + "@backstage/dev-utils": "^1.0.3", + "@backstage/test-utils": "^1.1.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", "@types/jest": "^26.0.7", "@types/node": "^16.11.26", - "msw": "^0.35.0" + "msw": "^0.42.0" }, "files": [ "dist", diff --git a/plugins/stack-overflow/src/home/StackOverflowQuestions/Content.tsx b/plugins/stack-overflow/src/home/StackOverflowQuestions/Content.tsx index c347399474..fbc3a90a9d 100644 --- a/plugins/stack-overflow/src/home/StackOverflowQuestions/Content.tsx +++ b/plugins/stack-overflow/src/home/StackOverflowQuestions/Content.tsx @@ -22,6 +22,7 @@ import { ListItem, ListItemText, ListItemSecondaryAction, + ListItemIcon, } from '@material-ui/core'; import OpenInNewIcon from '@material-ui/icons/OpenInNew'; import useAsync from 'react-use/lib/useAsync'; @@ -68,8 +69,9 @@ export const Content = (props: StackOverflowQuestionsContentProps) => { return ( {value.map(question => ( - - + + + {props.icon && {props.icon}} { - - + + ))} ); diff --git a/plugins/stack-overflow/src/home/StackOverflowQuestions/StackOverflowQuestions.stories.tsx b/plugins/stack-overflow/src/home/StackOverflowQuestions/StackOverflowQuestions.stories.tsx index e19e0f807b..35ace44c14 100644 --- a/plugins/stack-overflow/src/home/StackOverflowQuestions/StackOverflowQuestions.stories.tsx +++ b/plugins/stack-overflow/src/home/StackOverflowQuestions/StackOverflowQuestions.stories.tsx @@ -20,6 +20,7 @@ import { configApiRef } from '@backstage/core-plugin-api'; import { ConfigReader } from '@backstage/config'; import { Grid } from '@material-ui/core'; import React, { ComponentType } from 'react'; +import { StackOverflowIcon } from '../../icons'; export default { title: 'Plugins/Home/Components/StackOverflow', @@ -60,3 +61,18 @@ export const Default = () => { ); }; + +export const WithIcon = () => { + return ( + + } + /> + + ); +}; diff --git a/plugins/stack-overflow/src/icons/StackOverflowIcon.tsx b/plugins/stack-overflow/src/icons/StackOverflowIcon.tsx new file mode 100644 index 0000000000..8befe53177 --- /dev/null +++ b/plugins/stack-overflow/src/icons/StackOverflowIcon.tsx @@ -0,0 +1,38 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React from 'react'; + +/** + * Stack Overflow Icon + * @public + */ +export const StackOverflowIcon = () => { + return ( + + + + + ); +}; diff --git a/plugins/stack-overflow/src/icons/index.ts b/plugins/stack-overflow/src/icons/index.ts new file mode 100644 index 0000000000..141d87d7b1 --- /dev/null +++ b/plugins/stack-overflow/src/icons/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export * from './StackOverflowIcon'; diff --git a/plugins/stack-overflow/src/index.ts b/plugins/stack-overflow/src/index.ts index 264a0d5d7c..5965d66983 100644 --- a/plugins/stack-overflow/src/index.ts +++ b/plugins/stack-overflow/src/index.ts @@ -18,11 +18,13 @@ * * @packageDocumentation */ + export { stackOverflowPlugin, StackOverflowSearchResultListItem, HomePageStackOverflowQuestions, } from './plugin'; +export { StackOverflowIcon } from './icons'; export type { StackOverflowQuestion, StackOverflowQuestionsContentProps, diff --git a/plugins/stack-overflow/src/search/StackOverflowSearchResultListItem/StackOverflowSearchResultListItem.stories.tsx b/plugins/stack-overflow/src/search/StackOverflowSearchResultListItem/StackOverflowSearchResultListItem.stories.tsx index 1239cabcbe..df5b8038b0 100644 --- a/plugins/stack-overflow/src/search/StackOverflowSearchResultListItem/StackOverflowSearchResultListItem.stories.tsx +++ b/plugins/stack-overflow/src/search/StackOverflowSearchResultListItem/StackOverflowSearchResultListItem.stories.tsx @@ -17,6 +17,7 @@ import { StackOverflowSearchResultListItem } from '../../plugin'; import { wrapInTestApp } from '@backstage/test-utils'; import React, { ComponentType } from 'react'; +import { StackOverflowIcon } from '../../icons'; export default { title: 'Plugins/Search/StackOverflowResultListItem', @@ -37,3 +38,18 @@ export const Default = () => { /> ); }; + +export const WithIcon = () => { + return ( + } + /> + ); +}; diff --git a/plugins/stack-overflow/src/types.ts b/plugins/stack-overflow/src/types.ts index c3fa08cc4f..8f2d8847ec 100644 --- a/plugins/stack-overflow/src/types.ts +++ b/plugins/stack-overflow/src/types.ts @@ -34,6 +34,7 @@ export type StackOverflowQuestion = { */ export type StackOverflowQuestionsContentProps = { requestParams: StackOverflowQuestionsRequestParams; + icon?: React.ReactNode; }; /** diff --git a/plugins/tech-insights-backend-module-jsonfc/CHANGELOG.md b/plugins/tech-insights-backend-module-jsonfc/CHANGELOG.md index 51f1700554..ba2392b5ee 100644 --- a/plugins/tech-insights-backend-module-jsonfc/CHANGELOG.md +++ b/plugins/tech-insights-backend-module-jsonfc/CHANGELOG.md @@ -1,5 +1,29 @@ # @backstage/plugin-tech-insights-backend-module-jsonfc +## 0.1.17 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0 + - @backstage/plugin-tech-insights-node@0.3.1 + +## 0.1.17-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0-next.2 + - @backstage/plugin-tech-insights-node@0.3.1-next.1 + +## 0.1.17-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + - @backstage/plugin-tech-insights-node@0.3.1-next.0 + ## 0.1.16 ### Patch Changes diff --git a/plugins/tech-insights-backend-module-jsonfc/package.json b/plugins/tech-insights-backend-module-jsonfc/package.json index be527ea7fb..a6782c57fb 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.16", + "version": "0.1.17", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -34,11 +34,11 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/backend-common": "^0.13.3", + "@backstage/backend-common": "^0.14.0", "@backstage/config": "^1.0.1", "@backstage/errors": "^1.0.0", "@backstage/plugin-tech-insights-common": "^0.2.4", - "@backstage/plugin-tech-insights-node": "^0.3.0", + "@backstage/plugin-tech-insights-node": "^0.3.1", "ajv": "^8.10.0", "json-rules-engine": "^6.1.2", "lodash": "^4.17.21", @@ -46,7 +46,7 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/cli": "^0.17.1" + "@backstage/cli": "^0.17.2" }, "files": [ "dist" diff --git a/plugins/tech-insights-backend/CHANGELOG.md b/plugins/tech-insights-backend/CHANGELOG.md index d8f09deda3..9e496d9046 100644 --- a/plugins/tech-insights-backend/CHANGELOG.md +++ b/plugins/tech-insights-backend/CHANGELOG.md @@ -1,5 +1,50 @@ # @backstage/plugin-tech-insights-backend +## 0.4.1 + +### Patch Changes + +- 4fee8f59e3: Updated tech-insights fetch/latest endpoint to return the actual latest row based on the timestamp +- aa15229ec3: Introduce additional JsonValue types to be storable as facts. This enables the possibility to store more complex objects for fact checking purposes. The rules engine supports walking keyed object values directly to create rules and checks + + Modify facts database table to have a more restricted timestamp precision for cases where the postgres server isn't configured to contain such value. This fixes the issue where in some cases `maxItems` lifecycle condition didn't work as expected. + +- Updated dependencies + - @backstage/backend-tasks@0.3.2 + - @backstage/backend-common@0.14.0 + - @backstage/catalog-client@1.0.3 + - @backstage/catalog-model@1.0.3 + - @backstage/plugin-tech-insights-node@0.3.1 + +## 0.4.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0-next.2 + - @backstage/backend-tasks@0.3.2-next.2 + - @backstage/plugin-tech-insights-node@0.3.1-next.1 + +## 0.4.1-next.1 + +### Patch Changes + +- 4fee8f59e3: Updated tech-insights fetch/latest endpoint to return the actual latest row based on the timestamp +- Updated dependencies + - @backstage/backend-tasks@0.3.2-next.1 + - @backstage/backend-common@0.13.6-next.1 + - @backstage/catalog-client@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + +## 0.4.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-tasks@0.3.2-next.0 + - @backstage/backend-common@0.13.6-next.0 + - @backstage/plugin-tech-insights-node@0.3.1-next.0 + ## 0.4.0 ### Minor Changes diff --git a/plugins/tech-insights-backend/migrations/2022060100821_facts_timestamp_precision.js b/plugins/tech-insights-backend/migrations/2022060100821_facts_timestamp_precision.js new file mode 100644 index 0000000000..ea889d3928 --- /dev/null +++ b/plugins/tech-insights-backend/migrations/2022060100821_facts_timestamp_precision.js @@ -0,0 +1,36 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// @ts-check + +/** + * @param {import('knex').Knex} knex + */ +exports.up = async function up(knex) { + await knex.schema.alterTable('facts', table => { + table + .dateTime('timestamp', { precision: 0 }) + .defaultTo(knex.fn.now()) + .notNullable() + .comment('The timestamp when this entry was created') + .alter(); + }); +}; + +/** + * @param {import('knex').Knex} _knex + */ +exports.down = async function down(_knex) {}; diff --git a/plugins/tech-insights-backend/package.json b/plugins/tech-insights-backend/package.json index 6ea1207009..5ae890ae1b 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.4.0", + "version": "0.4.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -34,14 +34,14 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/backend-common": "^0.13.3", - "@backstage/backend-tasks": "^0.3.1", - "@backstage/catalog-client": "^1.0.2", - "@backstage/catalog-model": "^1.0.2", + "@backstage/backend-common": "^0.14.0", + "@backstage/backend-tasks": "^0.3.2", + "@backstage/catalog-client": "^1.0.3", + "@backstage/catalog-model": "^1.0.3", "@backstage/config": "^1.0.1", "@backstage/errors": "^1.0.0", "@backstage/plugin-tech-insights-common": "^0.2.4", - "@backstage/plugin-tech-insights-node": "^0.3.0", + "@backstage/plugin-tech-insights-node": "^0.3.1", "@types/express": "^4.17.6", "express": "^4.17.1", "express-promise-router": "^4.1.0", @@ -54,8 +54,8 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/backend-test-utils": "^0.1.24", - "@backstage/cli": "^0.17.1", + "@backstage/backend-test-utils": "^0.1.25", + "@backstage/cli": "^0.17.2", "@types/supertest": "^2.0.8", "@types/semver": "^7.3.8", "supertest": "^6.1.3", diff --git a/plugins/tech-insights-backend/src/service/persistence/TechInsightsDatabase.test.ts b/plugins/tech-insights-backend/src/service/persistence/TechInsightsDatabase.test.ts index c69432d070..e7e8819c66 100644 --- a/plugins/tech-insights-backend/src/service/persistence/TechInsightsDatabase.test.ts +++ b/plugins/tech-insights-backend/src/service/persistence/TechInsightsDatabase.test.ts @@ -115,6 +115,56 @@ const additionalFacts = [ }, ]; +const sameFactsDiffDateSchema = { + id: 'same-fact-diff-date-test', + version: '0.0.1-test', + entityFilter: JSON.stringify([{ kind: 'service' }]), + schema: JSON.stringify({ + testStringFact: { + type: 'string', + description: 'Test fact with a string type', + }, + }), +}; + +const sameFactsDiffDateNow = DateTime.now().toISO(); +const sameFactsDiffDateNearFuture = DateTime.now() + .plus(Duration.fromMillis(555)) + .toISO(); +const sameFactsDiffDateFuture = DateTime.now() + .plus(Duration.fromMillis(1000)) + .toISO(); + +const multipleSameFacts = [ + { + timestamp: sameFactsDiffDateNow, + id: sameFactsDiffDateSchema.id, + version: '0.0.1-test', + entity: 'a:a/a', + facts: JSON.stringify({ + testNumberFact: 1, + }), + }, + { + timestamp: sameFactsDiffDateNearFuture, + id: sameFactsDiffDateSchema.id, + version: '0.0.1-test', + entity: 'a:a/a', + facts: JSON.stringify({ + testNumberFact: 2, + }), + }, + { + timestamp: sameFactsDiffDateFuture, + id: 'multiple-same-facts', + version: '0.0.1-test', + entity: 'a:a/a', + facts: JSON.stringify({ + testNumberFact: 3, + }), + }, +]; + describe('Tech Insights database', () => { const databases = TestDatabases.create(); let store: TechInsightsStore; @@ -215,6 +265,33 @@ describe('Tech Insights database', () => { expect(returnedFact['test-fact']).toMatchObject(baseAssertionFact); }); + it('should return latest fact with multiple entries', async () => { + await testDbClient.batchInsert('fact_schemas', [sameFactsDiffDateSchema]); + await testDbClient.batchInsert( + 'facts', + multipleSameFacts.map(fact => ({ + ...fact, + id: sameFactsDiffDateSchema.id, + })), + ); + + const returnedFacts = await store.getLatestFactsByIds( + ['test-fact', sameFactsDiffDateSchema.id], + 'a:a/a', + ); + + expect(returnedFacts['test-fact']).toMatchObject({ + ...baseAssertionFact, + }); + + expect(returnedFacts[sameFactsDiffDateSchema.id]).toMatchObject({ + ...baseAssertionFact, + id: sameFactsDiffDateSchema.id, + timestamp: DateTime.fromISO(sameFactsDiffDateFuture), + facts: { testNumberFact: 3 }, + }); + }); + it('should return latest facts for multiple ids', async () => { await testDbClient.batchInsert('fact_schemas', [secondSchema]); await testDbClient.batchInsert( diff --git a/plugins/tech-insights-backend/src/service/persistence/TechInsightsDatabase.ts b/plugins/tech-insights-backend/src/service/persistence/TechInsightsDatabase.ts index 3507e280a1..c6751e864a 100644 --- a/plugins/tech-insights-backend/src/service/persistence/TechInsightsDatabase.ts +++ b/plugins/tech-insights-backend/src/service/persistence/TechInsightsDatabase.ts @@ -131,14 +131,16 @@ export class TechInsightsDatabase implements TechInsightsStore { .and.whereIn('id', ids) .join( this.db('facts') - .max('timestamp') + .max('timestamp as maxTimestamp') .column('id as subId') .where({ entity: entityTriplet }) .and.whereIn('id', ids) .groupBy('id') .as('subQ'), - 'facts.id', - 'subQ.subId', + { + 'facts.id': 'subQ.subId', + 'facts.timestamp': 'subQ.maxTimestamp', + }, ); return this.dbFactRowsToTechInsightFacts(results); } diff --git a/plugins/tech-insights-common/package.json b/plugins/tech-insights-common/package.json index f2293f403e..bc9bbcd792 100644 --- a/plugins/tech-insights-common/package.json +++ b/plugins/tech-insights-common/package.json @@ -38,7 +38,7 @@ "@backstage/types": "^1.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1-next.0" + "@backstage/cli": "^0.17.2-next.0" }, "files": [ "dist" diff --git a/plugins/tech-insights-node/CHANGELOG.md b/plugins/tech-insights-node/CHANGELOG.md index 63a22350cb..c6af55abd5 100644 --- a/plugins/tech-insights-node/CHANGELOG.md +++ b/plugins/tech-insights-node/CHANGELOG.md @@ -1,5 +1,30 @@ # @backstage/plugin-tech-insights-node +## 0.3.1 + +### Patch Changes + +- aa15229ec3: Introduce additional JsonValue types to be storable as facts. This enables the possibility to store more complex objects for fact checking purposes. The rules engine supports walking keyed object values directly to create rules and checks + + Modify facts database table to have a more restricted timestamp precision for cases where the postgres server isn't configured to contain such value. This fixes the issue where in some cases `maxItems` lifecycle condition didn't work as expected. + +- Updated dependencies + - @backstage/backend-common@0.14.0 + +## 0.3.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0-next.2 + +## 0.3.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + ## 0.3.0 ### Minor Changes diff --git a/plugins/tech-insights-node/api-report.md b/plugins/tech-insights-node/api-report.md index bcccec65b5..09d409fecd 100644 --- a/plugins/tech-insights-node/api-report.md +++ b/plugins/tech-insights-node/api-report.md @@ -8,6 +8,7 @@ import { Config } from '@backstage/config'; import { DateTime } from 'luxon'; import { Duration } from 'luxon'; import { DurationLike } from 'luxon'; +import { JsonValue } from '@backstage/types'; import { Logger } from 'winston'; import { PluginEndpointDiscovery } from '@backstage/backend-common'; import { TokenManager } from '@backstage/backend-common'; @@ -76,7 +77,14 @@ export type FactRetrieverRegistration = { // @public export type FactSchema = { [name: string]: { - type: 'integer' | 'float' | 'string' | 'boolean' | 'datetime' | 'set'; + type: + | 'integer' + | 'float' + | 'string' + | 'boolean' + | 'datetime' + | 'set' + | 'object'; description: string; since?: string; metadata?: Record; @@ -136,6 +144,7 @@ export type TechInsightFact = { | string[] | boolean[] | DateTime[] + | JsonValue >; timestamp?: DateTime; }; diff --git a/plugins/tech-insights-node/package.json b/plugins/tech-insights-node/package.json index 6d03e9f142..904f149d6c 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.3.0", + "version": "0.3.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -33,15 +33,16 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/backend-common": "^0.13.3", + "@backstage/backend-common": "^0.14.0", "@backstage/config": "^1.0.1", "@backstage/plugin-tech-insights-common": "^0.2.4", + "@backstage/types": "^1.0.0", "@types/luxon": "^2.0.5", "luxon": "^2.0.2", "winston": "^3.2.1" }, "devDependencies": { - "@backstage/cli": "^0.17.1" + "@backstage/cli": "^0.17.2" }, "files": [ "dist" diff --git a/plugins/tech-insights-node/src/facts.ts b/plugins/tech-insights-node/src/facts.ts index ffd3549bd5..bcc2aa42e1 100644 --- a/plugins/tech-insights-node/src/facts.ts +++ b/plugins/tech-insights-node/src/facts.ts @@ -15,6 +15,7 @@ */ import { DateTime, Duration, DurationLike } from 'luxon'; import { Config } from '@backstage/config'; +import { JsonValue } from '@backstage/types'; import { PluginEndpointDiscovery, TokenManager, @@ -55,6 +56,7 @@ export type TechInsightFact = { | string[] | boolean[] | DateTime[] + | JsonValue >; /** @@ -94,9 +96,16 @@ export type FactSchema = { * Type of the individual fact value * * Numbers are split into integers and floating point values. - * `set` indicates a collection of values + * `set` indicates a collection of values, `object` indicates JSON serializable value */ - type: 'integer' | 'float' | 'string' | 'boolean' | 'datetime' | 'set'; + type: + | 'integer' + | 'float' + | 'string' + | 'boolean' + | 'datetime' + | 'set' + | 'object'; /** * A description of this individual fact value diff --git a/plugins/tech-insights/CHANGELOG.md b/plugins/tech-insights/CHANGELOG.md index 70a3d2d3ce..867f639067 100644 --- a/plugins/tech-insights/CHANGELOG.md +++ b/plugins/tech-insights/CHANGELOG.md @@ -1,5 +1,37 @@ # @backstage/plugin-tech-insights +## 0.2.2 + +### Patch Changes + +- 09d2f4d179: Export TechInsightsClient so it may be extended by custom implementations +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## 0.2.2-next.1 + +### Patch Changes + +- 09d2f4d179: Export TechInsightsClient so it may be extended by custom implementations +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## 0.2.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + ## 0.2.1 ### Patch Changes diff --git a/plugins/tech-insights/api-report.md b/plugins/tech-insights/api-report.md index 3e099493b6..54884f12ea 100644 --- a/plugins/tech-insights/api-report.md +++ b/plugins/tech-insights/api-report.md @@ -10,6 +10,8 @@ import { BackstagePlugin } from '@backstage/core-plugin-api'; import { BulkCheckResponse } from '@backstage/plugin-tech-insights-common'; import { CheckResult } from '@backstage/plugin-tech-insights-common'; import { CompoundEntityRef } from '@backstage/catalog-model'; +import { DiscoveryApi } from '@backstage/core-plugin-api'; +import { IdentityApi } from '@backstage/core-plugin-api'; import { default as React_2 } from 'react'; import { RouteRef } from '@backstage/core-plugin-api'; @@ -78,6 +80,33 @@ export interface TechInsightsApi { // @public export const techInsightsApiRef: ApiRef; +// @public (undocumented) +export class TechInsightsClient implements TechInsightsApi { + constructor(options: { + discoveryApi: DiscoveryApi; + identityApi: IdentityApi; + }); + // (undocumented) + getAllChecks(): Promise; + // (undocumented) + getScorecardsDefinition( + type: string, + value: CheckResult[], + title?: string, + description?: string, + ): CheckResultRenderer | undefined; + // (undocumented) + runBulkChecks( + entities: CompoundEntityRef[], + checks?: Check[], + ): Promise; + // (undocumented) + runChecks( + entityParams: CompoundEntityRef, + checks?: string[], + ): Promise; +} + // @public (undocumented) export const techInsightsPlugin: BackstagePlugin< { diff --git a/plugins/tech-insights/package.json b/plugins/tech-insights/package.json index ab7727e8c2..0956db7cf3 100644 --- a/plugins/tech-insights/package.json +++ b/plugins/tech-insights/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-tech-insights", - "version": "0.2.1", + "version": "0.2.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -28,11 +28,11 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-model": "^1.0.2", - "@backstage/core-components": "^0.9.4", - "@backstage/core-plugin-api": "^1.0.2", + "@backstage/catalog-model": "^1.0.3", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", "@backstage/errors": "^1.0.0", - "@backstage/plugin-catalog-react": "^1.1.0", + "@backstage/plugin-catalog-react": "^1.1.1", "@backstage/plugin-tech-insights-common": "^0.2.4", "@backstage/theme": "^0.2.15", "@backstage/types": "^1.0.0", @@ -47,17 +47,17 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/core-app-api": "^1.0.2", - "@backstage/dev-utils": "^1.0.2", - "@backstage/test-utils": "^1.1.0", + "@backstage/cli": "^0.17.2", + "@backstage/core-app-api": "^1.0.3", + "@backstage/dev-utils": "^1.0.3", + "@backstage/test-utils": "^1.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", "@types/jest": "^26.0.7", "@types/node": "^16.11.26", "cross-fetch": "^3.1.5", - "msw": "^0.35.0" + "msw": "^0.42.0" }, "files": [ "dist" diff --git a/plugins/tech-insights/src/api/TechInsightsClient.ts b/plugins/tech-insights/src/api/TechInsightsClient.ts index 04de46dba9..aabb5a6297 100644 --- a/plugins/tech-insights/src/api/TechInsightsClient.ts +++ b/plugins/tech-insights/src/api/TechInsightsClient.ts @@ -29,16 +29,15 @@ import { defaultCheckResultRenderers, } from '../components/CheckResultRenderer'; -export type Options = { - discoveryApi: DiscoveryApi; - identityApi: IdentityApi; -}; - +/** @public */ export class TechInsightsClient implements TechInsightsApi { private readonly discoveryApi: DiscoveryApi; private readonly identityApi: IdentityApi; - constructor(options: Options) { + constructor(options: { + discoveryApi: DiscoveryApi; + identityApi: IdentityApi; + }) { this.discoveryApi = options.discoveryApi; this.identityApi = options.identityApi; } diff --git a/plugins/tech-insights/src/api/index.ts b/plugins/tech-insights/src/api/index.ts index bcd2575a52..9f5545e671 100644 --- a/plugins/tech-insights/src/api/index.ts +++ b/plugins/tech-insights/src/api/index.ts @@ -15,3 +15,4 @@ */ export * from './TechInsightsApi'; export * from './TechInsightsClient'; +export * from './types'; diff --git a/plugins/tech-insights/src/index.ts b/plugins/tech-insights/src/index.ts index bc7c2ce26c..97bdc95631 100644 --- a/plugins/tech-insights/src/index.ts +++ b/plugins/tech-insights/src/index.ts @@ -19,7 +19,6 @@ export { EntityTechInsightsScorecardCard, } from './plugin'; -export { techInsightsApiRef } from './api/TechInsightsApi'; -export type { TechInsightsApi } from './api/TechInsightsApi'; -export type { Check } from './api/types'; +export { techInsightsApiRef, TechInsightsClient } from './api'; +export type { TechInsightsApi, Check } from './api'; export type { CheckResultRenderer } from './components/CheckResultRenderer'; diff --git a/plugins/tech-radar/CHANGELOG.md b/plugins/tech-radar/CHANGELOG.md index 77411e298e..049a1b9c4c 100644 --- a/plugins/tech-radar/CHANGELOG.md +++ b/plugins/tech-radar/CHANGELOG.md @@ -1,5 +1,32 @@ # @backstage/plugin-tech-radar +## 0.5.13 + +### Patch Changes + +- bb31e5489c: Updated dependency `d3-force` to `^3.0.0`. + Updated dependency `@types/d3-force` to `^3.0.0`. +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + +## 0.5.13-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + +## 0.5.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.9.5-next.0 + ## 0.5.12 ### Patch Changes diff --git a/plugins/tech-radar/package.json b/plugins/tech-radar/package.json index 3a57c5bbdf..de2eab6fd1 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.5.12", + "version": "0.5.13", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -34,14 +34,14 @@ "start": "backstage-cli package start" }, "dependencies": { - "@backstage/core-components": "^0.9.4", - "@backstage/core-plugin-api": "^1.0.2", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", "@backstage/theme": "^0.2.15", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", "color": "^4.0.1", - "d3-force": "^2.0.1", + "d3-force": "^3.0.0", "prop-types": "^15.7.2", "react-use": "^17.2.4" }, @@ -49,20 +49,20 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/core-app-api": "^1.0.2", - "@backstage/dev-utils": "^1.0.2", - "@backstage/test-utils": "^1.1.0", + "@backstage/cli": "^0.17.2", + "@backstage/core-app-api": "^1.0.3", + "@backstage/dev-utils": "^1.0.3", + "@backstage/test-utils": "^1.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", "@types/color": "^3.0.1", - "@types/d3-force": "^2.1.1", + "@types/d3-force": "^3.0.0", "@types/jest": "^26.0.7", "@types/node": "^16.11.26", "@types/react": "^16.13.1 || ^17.0.0", "cross-fetch": "^3.1.5", - "msw": "^0.35.0" + "msw": "^0.42.0" }, "files": [ "dist" diff --git a/plugins/techdocs-addons-test-utils/CHANGELOG.md b/plugins/techdocs-addons-test-utils/CHANGELOG.md index a91a722cdb..aabdec8922 100644 --- a/plugins/techdocs-addons-test-utils/CHANGELOG.md +++ b/plugins/techdocs-addons-test-utils/CHANGELOG.md @@ -1,5 +1,49 @@ # @backstage/plugin-techdocs-addons-test-utils +## 1.0.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- d047d81295: Update default mock +- Updated dependencies + - @backstage/plugin-catalog@1.3.0 + - @backstage/plugin-techdocs@1.2.0 + - @backstage/plugin-search-react@0.2.1 + - @backstage/core-components@0.9.5 + - @backstage/core-app-api@1.0.3 + - @backstage/core-plugin-api@1.0.3 + - @backstage/integration-react@1.1.1 + - @backstage/test-utils@1.1.1 + - @backstage/plugin-techdocs-react@1.0.1 + +## 1.0.1-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-app-api@1.0.3-next.0 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/integration-react@1.1.1-next.1 + - @backstage/test-utils@1.1.1-next.0 + - @backstage/plugin-techdocs@1.1.2-next.1 + - @backstage/plugin-catalog@1.2.1-next.1 + - @backstage/plugin-search-react@0.2.1-next.0 + - @backstage/plugin-techdocs-react@1.0.1-next.1 + +## 1.0.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@1.2.1-next.0 + - @backstage/core-components@0.9.5-next.0 + - @backstage/plugin-techdocs@1.1.2-next.0 + - @backstage/plugin-techdocs-react@1.0.1-next.0 + - @backstage/integration-react@1.1.1-next.0 + ## 1.0.0 ### Major Changes diff --git a/plugins/techdocs-addons-test-utils/package.json b/plugins/techdocs-addons-test-utils/package.json index 8166bdc8f9..35d176566e 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.0", + "version": "1.0.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -32,15 +32,15 @@ "postpack": "backstage-cli package postpack" }, "dependencies": { - "@backstage/core-components": "^0.9.4", - "@backstage/core-app-api": "^1.0.2", - "@backstage/core-plugin-api": "^1.0.2", - "@backstage/integration-react": "^1.1.0", - "@backstage/plugin-catalog": "^1.2.0", - "@backstage/plugin-search-react": "^0.2.0", - "@backstage/plugin-techdocs": "^1.1.1", - "@backstage/plugin-techdocs-react": "^1.0.0", - "@backstage/test-utils": "^1.1.0", + "@backstage/core-components": "^0.9.5", + "@backstage/core-app-api": "^1.0.3", + "@backstage/core-plugin-api": "^1.0.3", + "@backstage/integration-react": "^1.1.1", + "@backstage/plugin-catalog": "^1.3.0", + "@backstage/plugin-search-react": "^0.2.1", + "@backstage/plugin-techdocs": "^1.2.0", + "@backstage/plugin-techdocs-react": "^1.0.1", + "@backstage/test-utils": "^1.1.1", "@backstage/theme": "^0.2.15", "@material-ui/core": "^4.9.13", "@material-ui/icons": "^4.9.1", @@ -56,14 +56,14 @@ "react-dom": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/dev-utils": "^1.0.2", + "@backstage/cli": "^0.17.2", + "@backstage/dev-utils": "^1.0.3", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", "@types/jest": "^26.0.7", "@types/node": "^16.11.26", - "msw": "^0.35.0", + "msw": "^0.42.0", "cross-fetch": "^3.1.5" }, "files": [ diff --git a/plugins/techdocs-addons-test-utils/src/test-utils.tsx b/plugins/techdocs-addons-test-utils/src/test-utils.tsx index 2c20199038..0d65e7e51e 100644 --- a/plugins/techdocs-addons-test-utils/src/test-utils.tsx +++ b/plugins/techdocs-addons-test-utils/src/test-utils.tsx @@ -82,7 +82,7 @@ type TechDocsAddonTesterOptions = { const defaultOptions: TechDocsAddonTesterOptions = { dom: <>, - entity: {}, + entity: { metadata: { name: '' } }, metadata: {}, componentId: 'docs', apis: [], diff --git a/plugins/techdocs-backend/CHANGELOG.md b/plugins/techdocs-backend/CHANGELOG.md index 1ecc97e1aa..861bf37bef 100644 --- a/plugins/techdocs-backend/CHANGELOG.md +++ b/plugins/techdocs-backend/CHANGELOG.md @@ -1,5 +1,179 @@ # @backstage/plugin-techdocs-backend +## 1.1.2 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- 61fba6e50b: In order to ensure a good, stable TechDocs user experience when running TechDocs with `techdocs.builder` set to `local`, the number of concurrent builds has been limited to 10. Any additional builds requested concurrently will be queued and handled as prior builds complete. In the unlikely event that you need to handle more concurrent builds, consider scaling out your TechDocs backend deployment or using the `external` option for `techdocs.builder`. +- 5d66d4ff67: Output logs from a TechDocs build to a logging transport in addition to existing + frontend event stream, for capturing these logs to other sources. + + This allows users to capture debugging information around why tech docs fail to build + without needing to rely on end users capturing information from their web browser. + + The most common use case is to log to the same place as the rest of the backend + application logs. + + Sample usage: + + ``` + import { DockerContainerRunner } from '@backstage/backend-common'; + import { + createRouter, + Generators, + Preparers, + Publisher, + } from '@backstage/plugin-techdocs-backend'; + import Docker from 'dockerode'; + import { Router } from 'express'; + import { PluginEnvironment } from '../types'; + + export default async function createPlugin( + env: PluginEnvironment, + ): Promise { + const preparers = await Preparers.fromConfig(env.config, { + logger: env.logger, + reader: env.reader, + }); + + const dockerClient = new Docker(); + const containerRunner = new DockerContainerRunner({ dockerClient }); + + const generators = await Generators.fromConfig(env.config, { + logger: env.logger, + containerRunner, + }); + + const publisher = await Publisher.fromConfig(env.config, { + logger: env.logger, + discovery: env.discovery, + }); + + await publisher.getReadiness(); + + return await createRouter({ + preparers, + generators, + publisher, + logger: env.logger, + // Passing a buildLogTransport as a parameter in createRouter will enable + // capturing build logs to a backend log stream + buildLogTransport: env.logger, + config: env.config, + discovery: env.discovery, + cache: env.cache, + }); + } + ``` + +- Updated dependencies + - @backstage/plugin-search-common@0.3.5 + - @backstage/plugin-catalog-common@1.0.3 + - @backstage/backend-common@0.14.0 + - @backstage/integration@1.2.1 + - @backstage/plugin-techdocs-node@1.1.2 + - @backstage/catalog-client@1.0.3 + - @backstage/plugin-permission-common@0.6.2 + - @backstage/catalog-model@1.0.3 + +## 1.1.2-next.2 + +### Patch Changes + +- 61fba6e50b: In order to ensure a good, stable TechDocs user experience when running TechDocs with `techdocs.builder` set to `local`, the number of concurrent builds has been limited to 10. Any additional builds requested concurrently will be queued and handled as prior builds complete. In the unlikely event that you need to handle more concurrent builds, consider scaling out your TechDocs backend deployment or using the `external` option for `techdocs.builder`. +- Updated dependencies + - @backstage/plugin-search-common@0.3.5-next.1 + - @backstage/plugin-catalog-common@1.0.3-next.1 + - @backstage/backend-common@0.14.0-next.2 + - @backstage/integration@1.2.1-next.2 + - @backstage/plugin-techdocs-node@1.1.2-next.2 + +## 1.1.2-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/backend-common@0.13.6-next.1 + - @backstage/catalog-client@1.0.3-next.0 + - @backstage/integration@1.2.1-next.1 + - @backstage/plugin-permission-common@0.6.2-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-common@1.0.3-next.0 + - @backstage/plugin-search-common@0.3.5-next.0 + - @backstage/plugin-techdocs-node@1.1.2-next.1 + +## 1.1.2-next.0 + +### Patch Changes + +- 5d66d4ff67: Output logs from a TechDocs build to a logging transport in addition to existing + frontend event stream, for capturing these logs to other sources. + + This allows users to capture debugging information around why tech docs fail to build + without needing to rely on end users capturing information from their web browser. + + The most common use case is to log to the same place as the rest of the backend + application logs. + + Sample usage: + + ``` + import { DockerContainerRunner } from '@backstage/backend-common'; + import { + createRouter, + Generators, + Preparers, + Publisher, + } from '@backstage/plugin-techdocs-backend'; + import Docker from 'dockerode'; + import { Router } from 'express'; + import { PluginEnvironment } from '../types'; + + export default async function createPlugin( + env: PluginEnvironment, + ): Promise { + const preparers = await Preparers.fromConfig(env.config, { + logger: env.logger, + reader: env.reader, + }); + + const dockerClient = new Docker(); + const containerRunner = new DockerContainerRunner({ dockerClient }); + + const generators = await Generators.fromConfig(env.config, { + logger: env.logger, + containerRunner, + }); + + const publisher = await Publisher.fromConfig(env.config, { + logger: env.logger, + discovery: env.discovery, + }); + + await publisher.getReadiness(); + + return await createRouter({ + preparers, + generators, + publisher, + logger: env.logger, + // Passing a buildLogTransport as a parameter in createRouter will enable + // capturing build logs to a backend log stream + buildLogTransport: env.logger, + config: env.config, + discovery: env.discovery, + cache: env.cache, + }); + } + ``` + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + - @backstage/integration@1.2.1-next.0 + - @backstage/plugin-techdocs-node@1.1.2-next.0 + ## 1.1.1 ### Patch Changes @@ -190,7 +364,7 @@ - 022507c860: A `DefaultTechDocsCollatorFactory`, which works with the new stream-based search indexing subsystem, is now available. The `DefaultTechDocsCollator` will continue to be available for those unable to upgrade to the stream-based - `@backstage/search-backend-node` (and related packages), however it is now + `@backstage/plugin-search-backend-node` (and related packages), however it is now marked as deprecated and will be removed in a future version. To upgrade this plugin and the search indexing subsystem in one go, check diff --git a/plugins/techdocs-backend/package.json b/plugins/techdocs-backend/package.json index a90a26cc9a..d7e9326fe5 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.1.1", + "version": "1.1.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -34,16 +34,16 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/backend-common": "^0.13.3", - "@backstage/catalog-client": "^1.0.2", - "@backstage/catalog-model": "^1.0.2", + "@backstage/backend-common": "^0.14.0", + "@backstage/catalog-client": "^1.0.3", + "@backstage/catalog-model": "^1.0.3", "@backstage/config": "^1.0.1", "@backstage/errors": "^1.0.0", - "@backstage/integration": "^1.2.0", - "@backstage/plugin-catalog-common": "^1.0.2", - "@backstage/plugin-permission-common": "^0.6.1", - "@backstage/plugin-search-common": "^0.3.4", - "@backstage/plugin-techdocs-node": "^1.1.1", + "@backstage/integration": "^1.2.1", + "@backstage/plugin-catalog-common": "^1.0.3", + "@backstage/plugin-permission-common": "^0.6.2", + "@backstage/plugin-search-common": "^0.3.5", + "@backstage/plugin-techdocs-node": "^1.1.2", "@types/express": "^4.17.6", "dockerode": "^3.3.1", "express": "^4.17.1", @@ -56,11 +56,11 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/backend-test-utils": "^0.1.24", - "@backstage/cli": "^0.17.1", - "@backstage/plugin-search-backend-node": "0.6.1", + "@backstage/backend-test-utils": "^0.1.25", + "@backstage/cli": "^0.17.2", + "@backstage/plugin-search-backend-node": "0.6.2", "@types/dockerode": "^3.3.0", - "msw": "^0.35.0", + "msw": "^0.42.0", "supertest": "^6.1.3" }, "files": [ diff --git a/plugins/techdocs-backend/src/search/DefaultTechDocsCollator.test.ts b/plugins/techdocs-backend/src/search/DefaultTechDocsCollator.test.ts index 684d5729a2..3f1a857c4d 100644 --- a/plugins/techdocs-backend/src/search/DefaultTechDocsCollator.test.ts +++ b/plugins/techdocs-backend/src/search/DefaultTechDocsCollator.test.ts @@ -74,144 +74,145 @@ const expectedEntities: Entity[] = [ }, ]; -describe('DefaultTechDocsCollator with legacyPathCasing configuration', () => { - let mockDiscoveryApi: jest.Mocked; - let mockTokenManager: jest.Mocked; - let collator: DefaultTechDocsCollator; - +describe('TechDocs Collator', () => { const worker = setupServer(); setupRequestMockHandlers(worker); - beforeEach(() => { - mockDiscoveryApi = { - getBaseUrl: jest.fn().mockResolvedValue('http://test-backend'), - getExternalBaseUrl: jest.fn(), - }; - mockTokenManager = { - getToken: jest.fn().mockResolvedValue({ token: '' }), - authenticate: jest.fn(), - }; - const mockConfig = new ConfigReader({ - techdocs: { - legacyUseCaseSensitiveTripletPaths: true, - }, - }); - collator = DefaultTechDocsCollator.fromConfig(mockConfig, { - discovery: mockDiscoveryApi, - tokenManager: mockTokenManager, - logger, - legacyPathCasing: true, - }); - worker.use( - rest.get( - 'http://test-backend/static/docs/default/Component/test-entity-with-docs/search/search_index.json', - (_, res, ctx) => res(ctx.status(200), ctx.json(mockSearchDocIndex)), - ), - rest.get('http://test-backend/entities', (_, res, ctx) => - res(ctx.status(200), ctx.json(expectedEntities)), - ), - ); - }); + describe('DefaultTechDocsCollator with legacyPathCasing configuration', () => { + let mockDiscoveryApi: jest.Mocked; + let mockTokenManager: jest.Mocked; + let collator: DefaultTechDocsCollator; - it('fetches from the configured catalog and tech docs services', async () => { - const documents = await collator.execute(); - expect(mockDiscoveryApi.getBaseUrl).toHaveBeenCalledWith('catalog'); - expect(mockDiscoveryApi.getBaseUrl).toHaveBeenCalledWith('techdocs'); - expect(documents).toHaveLength(mockSearchDocIndex.docs.length); - }); - - it('should create documents for each tech docs search index', async () => { - const documents = await collator.execute(); - const entity = expectedEntities[0]; - documents.forEach((document, idx) => { - expect(document).toMatchObject({ - title: mockSearchDocIndex.docs[idx].title, - location: `/docs/default/Component/${entity.metadata.name}/${mockSearchDocIndex.docs[idx].location}`, - text: mockSearchDocIndex.docs[idx].text, - namespace: 'default', - entityTitle: entity!.metadata.title, - componentType: entity!.spec!.type, - lifecycle: entity!.spec!.lifecycle, - owner: '', - kind: entity.kind, - name: entity.metadata.name, - }); - }); - }); -}); - -describe('DefaultTechDocsCollator', () => { - let mockDiscoveryApi: jest.Mocked; - let mockTokenManager: jest.Mocked; - let collator: DefaultTechDocsCollator; - - const worker = setupServer(); - setupRequestMockHandlers(worker); - beforeEach(() => { - mockDiscoveryApi = { - getBaseUrl: jest.fn().mockResolvedValue('http://test-backend'), - getExternalBaseUrl: jest.fn(), - }; - mockTokenManager = { - getToken: jest.fn().mockResolvedValue({ token: '' }), - authenticate: jest.fn(), - }; - collator = DefaultTechDocsCollator.fromConfig(new ConfigReader({}), { - discovery: mockDiscoveryApi, - tokenManager: mockTokenManager, - logger, - }); - - worker.use( - rest.get( - 'http://test-backend/static/docs/default/component/test-entity-with-docs/search/search_index.json', - (_, res, ctx) => res(ctx.status(200), ctx.json(mockSearchDocIndex)), - ), - rest.get('http://test-backend/entities', (_, res, ctx) => - res(ctx.status(200), ctx.json(expectedEntities)), - ), - ); - }); - - it('should create documents for each tech docs search index', async () => { - const documents = await collator.execute(); - const entity = expectedEntities[0]; - documents.forEach((document, idx) => { - expect(document).toMatchObject({ - title: mockSearchDocIndex.docs[idx].title, - location: `/docs/default/component/${entity.metadata.name}/${mockSearchDocIndex.docs[idx].location}`, - text: mockSearchDocIndex.docs[idx].text, - namespace: 'default', - entityTitle: entity!.metadata.title, - componentType: entity!.spec!.type, - lifecycle: entity!.spec!.lifecycle, - owner: '', - kind: entity.kind.toLocaleLowerCase('en-US'), - name: entity.metadata.name.toLocaleLowerCase('en-US'), - authorization: { - resourceRef: `component:default/${entity.metadata.name}`, + beforeEach(() => { + mockDiscoveryApi = { + getBaseUrl: jest.fn().mockResolvedValue('http://test-backend'), + getExternalBaseUrl: jest.fn(), + }; + mockTokenManager = { + getToken: jest.fn().mockResolvedValue({ token: '' }), + authenticate: jest.fn(), + }; + const mockConfig = new ConfigReader({ + techdocs: { + legacyUseCaseSensitiveTripletPaths: true, }, }); + collator = DefaultTechDocsCollator.fromConfig(mockConfig, { + discovery: mockDiscoveryApi, + tokenManager: mockTokenManager, + logger, + legacyPathCasing: true, + }); + + worker.use( + rest.get( + 'http://test-backend/static/docs/default/Component/test-entity-with-docs/search/search_index.json', + (_, res, ctx) => res(ctx.status(200), ctx.json(mockSearchDocIndex)), + ), + rest.get('http://test-backend/entities', (_, res, ctx) => + res(ctx.status(200), ctx.json(expectedEntities)), + ), + ); + }); + + it('fetches from the configured catalog and tech docs services', async () => { + const documents = await collator.execute(); + expect(mockDiscoveryApi.getBaseUrl).toHaveBeenCalledWith('catalog'); + expect(mockDiscoveryApi.getBaseUrl).toHaveBeenCalledWith('techdocs'); + expect(documents).toHaveLength(mockSearchDocIndex.docs.length); + }); + + it('should create documents for each tech docs search index', async () => { + const documents = await collator.execute(); + const entity = expectedEntities[0]; + documents.forEach((document, idx) => { + expect(document).toMatchObject({ + title: mockSearchDocIndex.docs[idx].title, + location: `/docs/default/Component/${entity.metadata.name}/${mockSearchDocIndex.docs[idx].location}`, + text: mockSearchDocIndex.docs[idx].text, + namespace: 'default', + entityTitle: entity!.metadata.title, + componentType: entity!.spec!.type, + lifecycle: entity!.spec!.lifecycle, + owner: '', + kind: entity.kind, + name: entity.metadata.name, + }); + }); }); }); - it('maps a returned entity with a custom locationTemplate', async () => { - const mockConfig = new ConfigReader({ - techdocs: { - legacyUseCaseSensitiveTripletPaths: true, - }, - }); - // Provide an alternate location template. - collator = DefaultTechDocsCollator.fromConfig(mockConfig, { - discovery: mockDiscoveryApi, - tokenManager: mockTokenManager, - locationTemplate: '/software/:name', - logger, + describe('DefaultTechDocsCollator', () => { + let mockDiscoveryApi: jest.Mocked; + let mockTokenManager: jest.Mocked; + let collator: DefaultTechDocsCollator; + + beforeEach(() => { + mockDiscoveryApi = { + getBaseUrl: jest.fn().mockResolvedValue('http://test-backend'), + getExternalBaseUrl: jest.fn(), + }; + mockTokenManager = { + getToken: jest.fn().mockResolvedValue({ token: '' }), + authenticate: jest.fn(), + }; + collator = DefaultTechDocsCollator.fromConfig(new ConfigReader({}), { + discovery: mockDiscoveryApi, + tokenManager: mockTokenManager, + logger, + }); + + worker.use( + rest.get( + 'http://test-backend/static/docs/default/component/test-entity-with-docs/search/search_index.json', + (_, res, ctx) => res(ctx.status(200), ctx.json(mockSearchDocIndex)), + ), + rest.get('http://test-backend/entities', (_, res, ctx) => + res(ctx.status(200), ctx.json(expectedEntities)), + ), + ); }); - const documents = await collator.execute(); - expect(documents[0]).toMatchObject({ - location: '/software/test-entity-with-docs', + it('should create documents for each tech docs search index', async () => { + const documents = await collator.execute(); + const entity = expectedEntities[0]; + documents.forEach((document, idx) => { + expect(document).toMatchObject({ + title: mockSearchDocIndex.docs[idx].title, + location: `/docs/default/component/${entity.metadata.name}/${mockSearchDocIndex.docs[idx].location}`, + text: mockSearchDocIndex.docs[idx].text, + namespace: 'default', + entityTitle: entity!.metadata.title, + componentType: entity!.spec!.type, + lifecycle: entity!.spec!.lifecycle, + owner: '', + kind: entity.kind.toLocaleLowerCase('en-US'), + name: entity.metadata.name.toLocaleLowerCase('en-US'), + authorization: { + resourceRef: `component:default/${entity.metadata.name}`, + }, + }); + }); + }); + + it('maps a returned entity with a custom locationTemplate', async () => { + const mockConfig = new ConfigReader({ + techdocs: { + legacyUseCaseSensitiveTripletPaths: true, + }, + }); + // Provide an alternate location template. + collator = DefaultTechDocsCollator.fromConfig(mockConfig, { + discovery: mockDiscoveryApi, + tokenManager: mockTokenManager, + locationTemplate: '/software/:name', + logger, + }); + + const documents = await collator.execute(); + expect(documents[0]).toMatchObject({ + location: '/software/test-entity-with-docs', + }); }); }); }); diff --git a/plugins/techdocs-backend/src/search/DefaultTechDocsCollator.ts b/plugins/techdocs-backend/src/search/DefaultTechDocsCollator.ts index bf647322cf..a76ea66729 100644 --- a/plugins/techdocs-backend/src/search/DefaultTechDocsCollator.ts +++ b/plugins/techdocs-backend/src/search/DefaultTechDocsCollator.ts @@ -69,7 +69,7 @@ type EntityInfo = { * A search collator responsible for gathering and transforming TechDocs documents. * * @public - * @deprecated Upgrade to a more recent `@backstage/search-backend-node` and + * @deprecated Upgrade to a more recent `@backstage/plugin-search-backend-node` and * use `DefaultTechDocsCollatorFactory` instead. */ export class DefaultTechDocsCollator { diff --git a/plugins/techdocs-backend/src/service/DocsSynchronizer.test.ts b/plugins/techdocs-backend/src/service/DocsSynchronizer.test.ts index fff35bedc0..0ab124c26d 100644 --- a/plugins/techdocs-backend/src/service/DocsSynchronizer.test.ts +++ b/plugins/techdocs-backend/src/service/DocsSynchronizer.test.ts @@ -159,6 +159,37 @@ describe('DocsSynchronizer', () => { expect(DocsBuilder.prototype.build).toBeCalledTimes(1); }); + it('should limit concurrent updates', async () => { + // Given a build implementation that runs long... + MockedDocsBuilder.prototype.build.mockImplementation( + () => new Promise(() => {}), + ); + (shouldCheckForUpdate as jest.Mock).mockReturnValue(true); + const entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + uid: '0', + name: 'test', + namespace: 'default', + }, + }; + + // When more than 10 syncs are attempted... + for (let i = 0; i < 12; i++) { + docsSynchronizer.doSync({ + responseHandler: mockResponseHandler, + entity, + preparers, + generators, + }); + } + + // Then still only 10 builds should have been triggered. + await new Promise(resolve => resolve()); + expect(DocsBuilder.prototype.build).toHaveBeenCalledTimes(10); + }); + it('should not check for an update too often', async () => { (shouldCheckForUpdate as jest.Mock).mockReturnValue(false); diff --git a/plugins/techdocs-backend/src/service/DocsSynchronizer.ts b/plugins/techdocs-backend/src/service/DocsSynchronizer.ts index 6c377381e0..647447a612 100644 --- a/plugins/techdocs-backend/src/service/DocsSynchronizer.ts +++ b/plugins/techdocs-backend/src/service/DocsSynchronizer.ts @@ -25,6 +25,7 @@ import { PublisherBase, } from '@backstage/plugin-techdocs-node'; import fetch from 'node-fetch'; +import pLimit, { Limit } from 'p-limit'; import { PassThrough } from 'stream'; import * as winston from 'winston'; import { TechDocsCache } from '../cache'; @@ -47,6 +48,7 @@ export class DocsSynchronizer { private readonly config: Config; private readonly scmIntegrations: ScmIntegrationRegistry; private readonly cache: TechDocsCache | undefined; + private readonly buildLimiter: Limit; constructor({ publisher, @@ -69,6 +71,9 @@ export class DocsSynchronizer { this.publisher = publisher; this.scmIntegrations = scmIntegrations; this.cache = cache; + + // Single host/process: limit concurrent builds up to 10 at a time. + this.buildLimiter = pLimit(10); } async doSync({ @@ -123,7 +128,7 @@ export class DocsSynchronizer { cache: this.cache, }); - const updated = await docsBuilder.build(); + const updated = await this.buildLimiter(() => docsBuilder.build()); if (!updated) { finish({ updated: false }); diff --git a/plugins/techdocs-module-addons-contrib/CHANGELOG.md b/plugins/techdocs-module-addons-contrib/CHANGELOG.md index 986c31fe75..4219157392 100644 --- a/plugins/techdocs-module-addons-contrib/CHANGELOG.md +++ b/plugins/techdocs-module-addons-contrib/CHANGELOG.md @@ -1,5 +1,41 @@ # @backstage/plugin-techdocs-module-addons-contrib +## 1.0.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- 6968b65ba1: Updated dependency `@react-hookz/web` to `^14.0.0`. +- Updated dependencies + - @backstage/core-components@0.9.5 + - @backstage/integration@1.2.1 + - @backstage/core-plugin-api@1.0.3 + - @backstage/integration-react@1.1.1 + - @backstage/plugin-techdocs-react@1.0.1 + +## 1.0.1-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/integration-react@1.1.1-next.1 + - @backstage/integration@1.2.1-next.1 + - @backstage/plugin-techdocs-react@1.0.1-next.1 + +## 1.0.1-next.0 + +### Patch Changes + +- 6968b65ba1: Updated dependency `@react-hookz/web` to `^14.0.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.0 + - @backstage/integration@1.2.1-next.0 + - @backstage/plugin-techdocs-react@1.0.1-next.0 + - @backstage/integration-react@1.1.1-next.0 + ## 1.0.0 ### Major Changes diff --git a/plugins/techdocs-module-addons-contrib/package.json b/plugins/techdocs-module-addons-contrib/package.json index 272fd48f07..92c0d8d4af 100644 --- a/plugins/techdocs-module-addons-contrib/package.json +++ b/plugins/techdocs-module-addons-contrib/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-techdocs-module-addons-contrib", "description": "Plugin module for contributed TechDocs Addons", - "version": "1.0.0", + "version": "1.0.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -34,11 +34,11 @@ "postpack": "backstage-cli package postpack" }, "dependencies": { - "@backstage/core-components": "^0.9.4", - "@backstage/core-plugin-api": "^1.0.2", - "@backstage/integration": "^1.2.0", - "@backstage/integration-react": "^1.1.0", - "@backstage/plugin-techdocs-react": "^1.0.0", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", + "@backstage/integration": "^1.2.1", + "@backstage/integration-react": "^1.1.1", + "@backstage/plugin-techdocs-react": "^1.0.1", "@backstage/theme": "^0.2.15", "@material-ui/core": "^4.9.13", "@material-ui/icons": "^4.9.1", @@ -51,18 +51,18 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/core-app-api": "^1.0.2", - "@backstage/dev-utils": "^1.0.2", - "@backstage/plugin-techdocs-addons-test-utils": "^1.0.0", - "@backstage/test-utils": "^1.1.0", + "@backstage/cli": "^0.17.2", + "@backstage/core-app-api": "^1.0.3", + "@backstage/dev-utils": "^1.0.3", + "@backstage/plugin-techdocs-addons-test-utils": "^1.0.1", + "@backstage/test-utils": "^1.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", "@types/jest": "^26.0.7", "@types/node": "^16.11.26", "@types/react": "^16.13.1 || ^17.0.0", - "msw": "^0.35.0", + "msw": "^0.42.0", "cross-fetch": "^3.1.5" }, "files": [ diff --git a/plugins/techdocs-node/CHANGELOG.md b/plugins/techdocs-node/CHANGELOG.md index 51b0c9554e..1902f80171 100644 --- a/plugins/techdocs-node/CHANGELOG.md +++ b/plugins/techdocs-node/CHANGELOG.md @@ -1,5 +1,44 @@ # @backstage/plugin-techdocs-node +## 1.1.2 + +### Patch Changes + +- f5283a42e2: Updated dependency `@google-cloud/storage` to `^6.0.0`. +- 2c048f8b90: Updated deprecated use of `express`' `res.redirect()` method when handling legacy path casing. +- Updated dependencies + - @backstage/plugin-search-common@0.3.5 + - @backstage/backend-common@0.14.0 + - @backstage/integration@1.2.1 + - @backstage/catalog-model@1.0.3 + +## 1.1.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-common@0.3.5-next.1 + - @backstage/backend-common@0.14.0-next.2 + - @backstage/integration@1.2.1-next.2 + +## 1.1.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.1 + - @backstage/integration@1.2.1-next.1 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-search-common@0.3.5-next.0 + +## 1.1.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + - @backstage/integration@1.2.1-next.0 + ## 1.1.1 ### Patch Changes diff --git a/plugins/techdocs-node/api-report.md b/plugins/techdocs-node/api-report.md index c584dec40c..cc9bc8eb51 100644 --- a/plugins/techdocs-node/api-report.md +++ b/plugins/techdocs-node/api-report.md @@ -73,12 +73,10 @@ export class Generators implements GeneratorBuilder { export const getDocFilesFromRepository: ( reader: UrlReader, entity: Entity, - opts?: - | { - etag?: string | undefined; - logger?: Logger | undefined; - } - | undefined, + opts?: { + etag?: string; + logger?: Logger; + }, ) => Promise; // @public diff --git a/plugins/techdocs-node/package.json b/plugins/techdocs-node/package.json index 5d9d442944..9cd737db80 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.1.1", + "version": "1.1.2", "main": "src/index.ts", "types": "src/index.ts", "private": false, @@ -42,13 +42,13 @@ "dependencies": { "@azure/identity": "^2.0.1", "@azure/storage-blob": "^12.5.0", - "@backstage/backend-common": "^0.13.3", - "@backstage/catalog-model": "^1.0.2", + "@backstage/backend-common": "^0.14.0", + "@backstage/catalog-model": "^1.0.3", "@backstage/config": "^1.0.1", "@backstage/errors": "^1.0.0", - "@backstage/integration": "^1.2.0", - "@backstage/plugin-search-common": "^0.3.4", - "@google-cloud/storage": "^5.6.0", + "@backstage/integration": "^1.2.1", + "@backstage/plugin-search-common": "^0.3.5", + "@google-cloud/storage": "^6.0.0", "@trendyol-js/openstack-swift-sdk": "^0.0.5", "@types/express": "^4.17.6", "aws-sdk": "^2.840.0", @@ -64,7 +64,7 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/cli": "^0.17.1", + "@backstage/cli": "^0.17.2", "@types/fs-extra": "^9.0.5", "@types/js-yaml": "^4.0.0", "@types/mime-types": "^2.1.0", diff --git a/plugins/techdocs-node/src/stages/publish/local.test.ts b/plugins/techdocs-node/src/stages/publish/local.test.ts index 761de03c5a..0119def16d 100644 --- a/plugins/techdocs-node/src/stages/publish/local.test.ts +++ b/plugins/techdocs-node/src/stages/publish/local.test.ts @@ -24,6 +24,7 @@ import request from 'supertest'; import mockFs from 'mock-fs'; import * as os from 'os'; import { LocalPublish } from './local'; +import path from 'path'; const createMockEntity = (annotations = {}, lowerCase = false) => { return { @@ -54,62 +55,107 @@ const resolvedDir = resolvePackagePath( ); describe('local publisher', () => { - it('should publish generated documentation dir', async () => { - mockFs({ - [tmpDir]: { - 'index.html': '', - }, + describe('publish', () => { + beforeEach(() => { + mockFs({ + [tmpDir]: { + 'index.html': '', + }, + }); }); - const mockConfig = new ConfigReader({}); - - const publisher = LocalPublish.fromConfig( - mockConfig, - logger, - testDiscovery, - ); - const mockEntity = createMockEntity(); - const lowerMockEntity = createMockEntity(undefined, true); - - await publisher.publish({ entity: mockEntity, directory: tmpDir }); - - expect(await publisher.hasDocsBeenGenerated(mockEntity)).toBe(true); - - // Lower/upper should be treated the same. - expect(await publisher.hasDocsBeenGenerated(lowerMockEntity)).toBe(true); - - mockFs.restore(); - }); - - it('should respect legacy casing', async () => { - mockFs({ - [tmpDir]: { - 'index.html': '', - }, + afterEach(() => { + mockFs.restore(); }); - const mockConfig = new ConfigReader({ - techdocs: { - legacyUseCaseSensitiveTripletPaths: true, - }, + it('should publish generated documentation dir', async () => { + const mockConfig = new ConfigReader({}); + + const publisher = LocalPublish.fromConfig( + mockConfig, + logger, + testDiscovery, + ); + const mockEntity = createMockEntity(); + const lowerMockEntity = createMockEntity(undefined, true); + + await publisher.publish({ entity: mockEntity, directory: tmpDir }); + + expect(await publisher.hasDocsBeenGenerated(mockEntity)).toBe(true); + + // Lower/upper should be treated the same. + expect(await publisher.hasDocsBeenGenerated(lowerMockEntity)).toBe(true); + + mockFs.restore(); }); - const publisher = LocalPublish.fromConfig( - mockConfig, - logger, - testDiscovery, - ); - const mockEntity = createMockEntity(); - const lowerMockEntity = createMockEntity(undefined, true); + it('should respect legacy casing', async () => { + const mockConfig = new ConfigReader({ + techdocs: { + legacyUseCaseSensitiveTripletPaths: true, + }, + }); - await publisher.publish({ entity: mockEntity, directory: tmpDir }); + const publisher = LocalPublish.fromConfig( + mockConfig, + logger, + testDiscovery, + ); + const mockEntity = createMockEntity(); + const lowerMockEntity = createMockEntity(undefined, true); - expect(await publisher.hasDocsBeenGenerated(mockEntity)).toBe(true); + await publisher.publish({ entity: mockEntity, directory: tmpDir }); - // Lower/upper should be treated differently. - expect(await publisher.hasDocsBeenGenerated(lowerMockEntity)).toBe(false); + expect(await publisher.hasDocsBeenGenerated(mockEntity)).toBe(true); - mockFs.restore(); + // Lower/upper should be treated differently. + expect(await publisher.hasDocsBeenGenerated(lowerMockEntity)).toBe(false); + + mockFs.restore(); + }); + + it('should throw with unsafe triplet', async () => { + const mockConfig = new ConfigReader({}); + const publisher = LocalPublish.fromConfig( + mockConfig, + logger, + testDiscovery, + ); + const mockEntity = { + ...createMockEntity(), + ...{ + kind: '..', + metadata: { name: '..', namespace: '..' }, + }, + }; + + await expect(() => + publisher.publish({ entity: mockEntity, directory: tmpDir }), + ).rejects.toThrowError('Unable to publish TechDocs site'); + }); + + it('should throw with unsafe name', async () => { + const mockConfig = new ConfigReader({}); + const publisher = LocalPublish.fromConfig( + mockConfig, + logger, + testDiscovery, + ); + const mockEntity = { + ...createMockEntity(), + ...{ + kind: 'component', + metadata: { + name: path.join('..', 'component', 'other-component'), + namespace: 'default', + }, + }, + }; + + await expect(() => + publisher.publish({ entity: mockEntity, directory: tmpDir }), + ).rejects.toThrowError('Unable to publish TechDocs site'); + }); }); describe('docsRouter', () => { diff --git a/plugins/techdocs-node/src/stages/publish/local.ts b/plugins/techdocs-node/src/stages/publish/local.ts index 4539d389df..11c6e6dd2b 100644 --- a/plugins/techdocs-node/src/stages/publish/local.ts +++ b/plugins/techdocs-node/src/stages/publish/local.ts @@ -16,8 +16,13 @@ import { PluginEndpointDiscovery, resolvePackagePath, + resolveSafeChildPath, } from '@backstage/backend-common'; -import { Entity, CompoundEntityRef } from '@backstage/catalog-model'; +import { + Entity, + CompoundEntityRef, + stringifyEntityRef, +} from '@backstage/catalog-model'; import { Config } from '@backstage/config'; import express from 'express'; import fs from 'fs-extra'; @@ -37,7 +42,7 @@ import { getHeadersForFileExtension, lowerCaseEntityTripletInStoragePath, } from './helpers'; -import { assertError } from '@backstage/errors'; +import { ForwardedError } from '@backstage/errors'; // TODO: Use a more persistent storage than node_modules or /tmp directory. // Make it configurable with techdocs.publisher.local.publishDirectory @@ -103,12 +108,22 @@ export class LocalPublish implements PublisherBase { directory, }: PublishRequest): Promise { const entityNamespace = entity.metadata.namespace ?? 'default'; + let publishDir: string; - const publishDir = this.staticEntityPathJoin( - entityNamespace, - entity.kind, - entity.metadata.name, - ); + try { + publishDir = this.staticEntityPathJoin( + entityNamespace, + entity.kind, + entity.metadata.name, + ); + } catch (error) { + throw new ForwardedError( + `Unable to publish TechDocs site for entity: ${stringifyEntityRef( + entity, + )}`, + error, + ); + } if (!fs.existsSync(publishDir)) { this.logger.info(`Could not find ${publishDir}, creating the directory.`); @@ -144,21 +159,31 @@ export class LocalPublish implements PublisherBase { async fetchTechDocsMetadata( entityName: CompoundEntityRef, ): Promise { - const metadataPath = this.staticEntityPathJoin( - entityName.namespace, - entityName.kind, - entityName.name, - 'techdocs_metadata.json', - ); + let metadataPath: string; + + try { + metadataPath = this.staticEntityPathJoin( + entityName.namespace, + entityName.kind, + entityName.name, + 'techdocs_metadata.json', + ); + } catch (err) { + throw new ForwardedError( + `Unexpected entity when fetching metadata: ${stringifyEntityRef( + entityName, + )}`, + err, + ); + } try { return await fs.readJson(metadataPath); } catch (err) { - assertError(err); - this.logger.error( + throw new ForwardedError( `Unable to read techdocs_metadata.json at ${metadataPath}. Error: ${err}`, + err, ); - throw new Error(err.message); } } @@ -195,7 +220,7 @@ export class LocalPublish implements PublisherBase { } // Otherwise, redirect to the new path. - return res.redirect(req.baseUrl + newPath, 301); + return res.redirect(301, req.baseUrl + newPath); }); router.use( @@ -217,18 +242,26 @@ export class LocalPublish implements PublisherBase { async hasDocsBeenGenerated(entity: Entity): Promise { const namespace = entity.metadata.namespace ?? 'default'; - const indexHtmlPath = this.staticEntityPathJoin( - namespace, - entity.kind, - entity.metadata.name, - 'index.html', - ); - // Check if the file exists try { + const indexHtmlPath = this.staticEntityPathJoin( + namespace, + entity.kind, + entity.metadata.name, + 'index.html', + ); + await fs.access(indexHtmlPath, fs.constants.F_OK); + return true; } catch (err) { + if (err.name === 'NotAllowedError') { + this.logger.error( + `Unexpected entity when checking if generated: ${stringifyEntityRef( + entity, + )}`, + ); + } return false; } } @@ -278,17 +311,25 @@ export class LocalPublish implements PublisherBase { * Utility wrapper around path.join(), used to control legacy case logic. */ protected staticEntityPathJoin(...allParts: string[]): string { - if (this.legacyPathCasing) { - const [namespace, kind, name, ...parts] = allParts; - return path.join(staticDocsDir, namespace, kind, name, ...parts); - } - const [namespace, kind, name, ...parts] = allParts; - return path.join( - staticDocsDir, - namespace.toLowerCase(), - kind.toLowerCase(), - name.toLowerCase(), - ...parts, - ); + let staticEntityPath = staticDocsDir; + + allParts + .map(part => part.split(path.sep)) + .flat() + .forEach((part, index) => { + // Respect legacy path casing when operating on namespace, kind, or name. + if (index < 3) { + staticEntityPath = resolveSafeChildPath( + staticEntityPath, + this.legacyPathCasing ? part : part.toLowerCase(), + ); + return; + } + + // Otherwise, respect the provided case. + staticEntityPath = resolveSafeChildPath(staticEntityPath, part); + }); + + return staticEntityPath; } } diff --git a/plugins/techdocs-react/CHANGELOG.md b/plugins/techdocs-react/CHANGELOG.md index 3a91eb1376..904eacff26 100644 --- a/plugins/techdocs-react/CHANGELOG.md +++ b/plugins/techdocs-react/CHANGELOG.md @@ -1,5 +1,40 @@ # @backstage/plugin-techdocs-react +## 1.0.1 + +### Patch Changes + +- 3b45ad701f: Creates a `TechDocsShadowDom` component that takes a tree of elements and an `onAppend` handler: + + - Calls the `onAppend` handler when appending the element tree to the shadow root; + - Also dispatches an event when styles are loaded to let transformers know that the computed styles are ready to be consumed. + +- Updated dependencies + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## 1.0.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + +## 1.0.1-next.0 + +### Patch Changes + +- 3b45ad701f: Creates a `TechDocsShadowDom` component that takes a tree of elements and an `onAppend` handler: + + - Calls the `onAppend` handler when appending the element tree to the shadow root; + - Also dispatches an event when styles are loaded to let transformers know that the computed styles are ready to be consumed. + +- Updated dependencies + - @backstage/core-components@0.9.5-next.0 + ## 1.0.0 ### Major Changes diff --git a/plugins/techdocs-react/package.json b/plugins/techdocs-react/package.json index 2d315b523c..f4a51bfe42 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.0.0", + "version": "1.0.1", "private": false, "publishConfig": { "access": "public", @@ -35,9 +35,9 @@ "start": "backstage-cli package start" }, "dependencies": { - "@backstage/catalog-model": "^1.0.2", - "@backstage/core-components": "^0.9.4", - "@backstage/core-plugin-api": "^1.0.2", + "@backstage/catalog-model": "^1.0.3", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", "@backstage/version-bridge": "^1.0.1", "@material-ui/core": "^4.12.2", "@material-ui/lab": "4.0.0-alpha.57", @@ -56,7 +56,7 @@ "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/react-hooks": "^8.0.0", - "@backstage/test-utils": "^1.1.0", + "@backstage/test-utils": "^1.1.1", "@backstage/theme": "^0.2.15" }, "files": [ diff --git a/plugins/techdocs-react/src/addons.tsx b/plugins/techdocs-react/src/addons.tsx index ce5b3eb5cd..a562007260 100644 --- a/plugins/techdocs-react/src/addons.tsx +++ b/plugins/techdocs-react/src/addons.tsx @@ -30,7 +30,7 @@ import { TechDocsAddonLocations, TechDocsAddonOptions } from './types'; export const TECHDOCS_ADDONS_KEY = 'techdocs.addons.addon.v1'; /** - * Marks the registry component. + * Marks the `` registry component. * @public */ export const TECHDOCS_ADDONS_WRAPPER_KEY = 'techdocs.addons.wrapper.v1'; diff --git a/plugins/techdocs/CHANGELOG.md b/plugins/techdocs/CHANGELOG.md index 42b2108706..91babd6fba 100644 --- a/plugins/techdocs/CHANGELOG.md +++ b/plugins/techdocs/CHANGELOG.md @@ -1,5 +1,87 @@ # @backstage/plugin-techdocs +## 1.2.0 + +### Minor Changes + +- fe7614ea54: Add an optional icon to the Catalog and TechDocs search results + +### Patch Changes + +- d047d81295: Use entity title as label in `TechDocsReaderPageHeader` if available +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- bff65e6958: Updated sidebar-related logic to use `` + `useSidebarPinState()` and/or `` + `useSidebarOpenState()` from `@backstage/core-components`. +- 915700f64f: In order to simplify analytics on top of the search experience in Backstage, the provided `<*ResultListItem />` component now captures a `discover` analytics event instead of a `click` event. This event includes the result rank as its `value` and, like a click, the URL/path clicked to as its `to` attribute. +- 881fbd7e8d: Fix `EntityTechdocsContent` component to use objects instead of `` elements, otherwise "outlet" will be null on sub-pages and add-ons won't render. +- 17c059dfd0: Restructures reader style transformations to improve code readability: + + - Extracts the style rules to separate files; + - Creates a hook that processes each rule; + - And creates another hook that returns a transformer responsible for injecting them into the head tag of a given element. + +- 3b45ad701f: Packages a set of tweaks to the TechDocs addons rendering process: + + - Prevents displaying sidebars until page styles are loaded and the sidebar position is updated; + - Prevents new sidebar locations from being created every time the reader page is rendered if these locations already exist; + - Centers the styles loaded event to avoid having multiple locations setting the opacity style in Shadow Dom causing the screen to flash multiple times. + +- 9b94ade898: Use entity title in `TechDocsSearch` placeholder if available. +- 816f7475ec: Convert `sanitizeDOM` transformer to hook as part of code readability improvements in dom file. +- 50ff56a80f: Change the `EntityDocsPage` path to be more specific and also add integration tests for `sub-routes` on this page. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/plugin-search-common@0.3.5 + - @backstage/plugin-search-react@0.2.1 + - @backstage/core-components@0.9.5 + - @backstage/integration@1.2.1 + - @backstage/core-plugin-api@1.0.3 + - @backstage/integration-react@1.1.1 + - @backstage/catalog-model@1.0.3 + - @backstage/plugin-techdocs-react@1.0.1 + +## 1.1.2-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- bff65e6958: Updated sidebar-related logic to use `` + `useSidebarPinState()` and/or `` + `useSidebarOpenState()` from `@backstage/core-components`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/integration-react@1.1.1-next.1 + - @backstage/integration@1.2.1-next.1 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + - @backstage/plugin-search-react@0.2.1-next.0 + - @backstage/plugin-techdocs-react@1.0.1-next.1 + - @backstage/plugin-search-common@0.3.5-next.0 + +## 1.1.2-next.0 + +### Patch Changes + +- 881fbd7e8d: Fix `EntityTechdocsContent` component to use objects instead of `` elements, otherwise "outlet" will be null on sub-pages and add-ons won't render. +- 17c059dfd0: Restructures reader style transformations to improve code readability: + + - Extracts the style rules to separate files; + - Creates a hook that processes each rule; + - And creates another hook that returns a transformer responsible for injecting them into the head tag of a given element. + +- 3b45ad701f: Packages a set of tweaks to the TechDocs addons rendering process: + + - Prevents displaying sidebars until page styles are loaded and the sidebar position is updated; + - Prevents new sidebar locations from being created every time the reader page is rendered if these locations already exist; + - Centers the styles loaded event to avoid having multiple locations setting the opacity style in Shadow Dom causing the screen to flash multiple times. + +- 816f7475ec: Convert `sanitizeDOM` transformer to hook as part of code readability improvements in dom file. +- 50ff56a80f: Change the `EntityDocsPage` path to be more specific and also add integration tests for `sub-routes` on this page. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + - @backstage/integration@1.2.1-next.0 + - @backstage/plugin-techdocs-react@1.0.1-next.0 + - @backstage/integration-react@1.1.1-next.0 + ## 1.1.1 ### Patch Changes diff --git a/plugins/techdocs/api-report.md b/plugins/techdocs/api-report.md index a8204bc9af..d4ec65ed53 100644 --- a/plugins/techdocs/api-report.md +++ b/plugins/techdocs/api-report.md @@ -111,7 +111,9 @@ export type DocsTableRow = { }; // @public -export const EmbeddedDocsRouter: (props: PropsWithChildren<{}>) => JSX.Element; +export const EmbeddedDocsRouter: ( + props: PropsWithChildren<{}>, +) => JSX.Element | null; // @public export const EntityListDocsGrid: () => JSX.Element; @@ -153,7 +155,7 @@ export type EntityListDocsTableProps = { // @public export const EntityTechdocsContent: (props: { children?: ReactNode; -}) => JSX.Element; +}) => JSX.Element | null; // @public export const isTechDocsAvailable: (entity: Entity) => boolean; @@ -378,6 +380,7 @@ export const TechDocsSearch: (props: TechDocsSearchProps) => JSX.Element; // @public export type TechDocsSearchProps = { entityId: CompoundEntityRef; + entityTitle?: string; debounceTime?: number; }; @@ -388,8 +391,10 @@ export const TechDocsSearchResultListItem: ( // @public export type TechDocsSearchResultListItemProps = { + icon?: ReactNode; result: any; highlight?: ResultHighlight; + rank?: number; lineClamp?: number; asListItem?: boolean; asLink?: boolean; diff --git a/plugins/techdocs/package.json b/plugins/techdocs/package.json index 58e9537ce9..45c6d98aec 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.1.1", + "version": "1.2.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -35,17 +35,17 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-model": "^1.0.2", + "@backstage/catalog-model": "^1.0.3", "@backstage/config": "^1.0.1", - "@backstage/core-components": "^0.9.4", - "@backstage/core-plugin-api": "^1.0.2", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", "@backstage/errors": "^1.0.0", - "@backstage/integration": "^1.2.0", - "@backstage/integration-react": "^1.1.0", - "@backstage/plugin-catalog-react": "^1.1.0", - "@backstage/plugin-search-common": "^0.3.4", - "@backstage/plugin-search-react": "^0.2.0", - "@backstage/plugin-techdocs-react": "^1.0.0", + "@backstage/integration": "^1.2.1", + "@backstage/integration-react": "^1.1.1", + "@backstage/plugin-catalog-react": "^1.1.1", + "@backstage/plugin-search-common": "^0.3.5", + "@backstage/plugin-search-react": "^0.2.1", + "@backstage/plugin-techdocs-react": "^1.0.1", "@backstage/theme": "^0.2.15", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -67,21 +67,21 @@ "react-dom": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/core-app-api": "^1.0.2", - "@backstage/dev-utils": "^1.0.2", - "@backstage/test-utils": "^1.1.0", + "@backstage/cli": "^0.17.2", + "@backstage/core-app-api": "^1.0.3", + "@backstage/dev-utils": "^1.0.3", + "@backstage/test-utils": "^1.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/react-hooks": "^8.0.0", "@testing-library/user-event": "^14.0.0", - "@types/event-source-polyfill": "^1.0.0", "@types/dompurify": "^2.2.2", + "@types/event-source-polyfill": "^1.0.0", "@types/jest": "^26.0.7", "@types/node": "^16.11.26", "canvas": "^2.6.1", "cross-fetch": "^3.1.5", - "msw": "^0.35.0" + "msw": "^0.42.0" }, "files": [ "dist", diff --git a/plugins/techdocs/src/Router.tsx b/plugins/techdocs/src/Router.tsx index de63838bc8..12b3ddf611 100644 --- a/plugins/techdocs/src/Router.tsx +++ b/plugins/techdocs/src/Router.tsx @@ -15,7 +15,7 @@ */ import React, { PropsWithChildren } from 'react'; -import { Route, Routes } from 'react-router-dom'; +import { Route, Routes, useRoutes } from 'react-router-dom'; import { Entity } from '@backstage/catalog-model'; import { useEntity } from '@backstage/plugin-catalog-react'; @@ -61,17 +61,25 @@ export const EmbeddedDocsRouter = (props: PropsWithChildren<{}>) => { const { children } = props; const { entity } = useEntity(); + // Using objects instead of elements, otherwise "outlet" will be null on sub-pages and add-ons won't render + const element = useRoutes([ + { + path: '/*', + element: , + children: [ + { + path: '/*', + element: children, + }, + ], + }, + ]); + const projectId = entity.metadata.annotations?.[TECHDOCS_ANNOTATION]; if (!projectId) { return ; } - return ( - - }> - {children} - - - ); + return element; }; diff --git a/plugins/techdocs/src/reader/components/TechDocsReaderPageContent/TechDocsReaderPageContent.tsx b/plugins/techdocs/src/reader/components/TechDocsReaderPageContent/TechDocsReaderPageContent.tsx index c31979b5f9..000a1ba275 100644 --- a/plugins/techdocs/src/reader/components/TechDocsReaderPageContent/TechDocsReaderPageContent.tsx +++ b/plugins/techdocs/src/reader/components/TechDocsReaderPageContent/TechDocsReaderPageContent.tsx @@ -114,7 +114,10 @@ export const TechDocsReaderPageContent = withTechDocsReaderProvider( {withSearch && ( - + )} diff --git a/plugins/techdocs/src/reader/components/TechDocsReaderPageContent/dom.tsx b/plugins/techdocs/src/reader/components/TechDocsReaderPageContent/dom.tsx index 87331c2e46..9f561bfcd3 100644 --- a/plugins/techdocs/src/reader/components/TechDocsReaderPageContent/dom.tsx +++ b/plugins/techdocs/src/reader/components/TechDocsReaderPageContent/dom.tsx @@ -14,16 +14,14 @@ * limitations under the License. */ -import { useContext, useCallback, useEffect, useState } from 'react'; +import { useCallback, useEffect, useState } from 'react'; import { useNavigate } from 'react-router-dom'; -import { Theme, useTheme, useMediaQuery } from '@material-ui/core'; -import { lighten, alpha } from '@material-ui/core/styles'; +import { useTheme, useMediaQuery } from '@material-ui/core'; import { BackstageTheme } from '@backstage/theme'; import { CompoundEntityRef } from '@backstage/catalog-model'; -import { useApi, configApiRef } from '@backstage/core-plugin-api'; -import { SidebarPinStateContext } from '@backstage/core-components'; +import { useApi } from '@backstage/core-plugin-api'; import { scmIntegrationsApiRef } from '@backstage/integration-react'; import { @@ -38,33 +36,19 @@ import { addGitFeedbackLink, addLinkClickListener, addSidebarToggle, - injectCss, onCssReady, removeMkdocsHeader, rewriteDocLinks, - sanitizeDOM, simplifyMkdocsFooter, scrollIntoAnchor, transform as transformer, copyToClipboard, + useSanitizerTransformer, + useStylesTransformer, } from '../../transformers'; const MOBILE_MEDIA_QUERY = 'screen and (max-width: 76.1875em)'; -type TypographyHeadings = Pick< - Theme['typography'], - 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' ->; - -type TypographyHeadingsKeys = keyof TypographyHeadings; - -const headings: TypographyHeadingsKeys[] = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6']; - -/** - * Sidebar pinned status to be used in computing CSS style injections - */ -const useSidebar = () => useContext(SidebarPinStateContext); - /** * Hook that encapsulates the behavior of getting raw HTML and applying * transforms to it in order to make it function at a basic level in the @@ -74,11 +58,11 @@ export const useTechDocsReaderDom = ( entityRef: CompoundEntityRef, ): Element | null => { const navigate = useNavigate(); - const sidebar = useSidebar(); const theme = useTheme(); const isMobileMedia = useMediaQuery(MOBILE_MEDIA_QUERY); + const sanitizerTransformer = useSanitizerTransformer(); + const stylesTransformer = useStylesTransformer(); - const configApi = useApi(configApiRef); const techdocsStorageApi = useApi(techdocsStorageApiRef); const scmIntegrationsApi = useApi(scmIntegrationsApiRef); @@ -146,7 +130,7 @@ export const useTechDocsReaderDom = ( const preRender = useCallback( (rawContent: string, contentPath: string) => transformer(rawContent, [ - sanitizeDOM(configApi.getOptionalConfig('techdocs.sanitizer')), + sanitizerTransformer, addBaseUrl({ techdocsStorageApi, entityId: entityRef, @@ -157,548 +141,15 @@ export const useTechDocsReaderDom = ( removeMkdocsHeader(), simplifyMkdocsFooter(), addGitFeedbackLink(scmIntegrationsApi), - injectCss({ - // Variables - css: ` - /* - As the MkDocs output is rendered in shadow DOM, the CSS variable definitions on the root selector are not applied. Instead, they have to be applied on :host. - As there is no way to transform the served main*.css yet (for example in the backend), we have to copy from main*.css and modify them. - */ - :host { - /* FONT */ - --md-default-fg-color: ${theme.palette.text.primary}; - --md-default-fg-color--light: ${theme.palette.text.secondary}; - --md-default-fg-color--lighter: ${lighten( - theme.palette.text.secondary, - 0.7, - )}; - --md-default-fg-color--lightest: ${lighten( - theme.palette.text.secondary, - 0.3, - )}; - - /* BACKGROUND */ - --md-default-bg-color:${theme.palette.background.default}; - --md-default-bg-color--light: ${theme.palette.background.paper}; - --md-default-bg-color--lighter: ${lighten( - theme.palette.background.paper, - 0.7, - )}; - --md-default-bg-color--lightest: ${lighten( - theme.palette.background.paper, - 0.3, - )}; - - /* PRIMARY */ - --md-primary-fg-color: ${theme.palette.primary.main}; - --md-primary-fg-color--light: ${theme.palette.primary.light}; - --md-primary-fg-color--dark: ${theme.palette.primary.dark}; - --md-primary-bg-color: ${theme.palette.primary.contrastText}; - --md-primary-bg-color--light: ${lighten( - theme.palette.primary.contrastText, - 0.7, - )}; - - /* ACCENT */ - --md-accent-fg-color: var(--md-primary-fg-color); - - /* SHADOW */ - --md-shadow-z1: ${theme.shadows[1]}; - --md-shadow-z2: ${theme.shadows[2]}; - --md-shadow-z3: ${theme.shadows[3]}; - - /* EXTENSIONS */ - --md-admonition-fg-color: var(--md-default-fg-color); - --md-admonition-bg-color: var(--md-default-bg-color); - /* Admonitions and others are using SVG masks to define icons. These masks are defined as CSS variables. */ - --md-admonition-icon--note: url('data:image/svg+xml;charset=utf-8,'); - --md-admonition-icon--abstract: url('data:image/svg+xml;charset=utf-8,'); - --md-admonition-icon--info: url('data:image/svg+xml;charset=utf-8,'); - --md-admonition-icon--tip: url('data:image/svg+xml;charset=utf-8,'); - --md-admonition-icon--success: url('data:image/svg+xml;charset=utf-8,'); - --md-admonition-icon--question: url('data:image/svg+xml;charset=utf-8,'); - --md-admonition-icon--warning: url('data:image/svg+xml;charset=utf-8,'); - --md-admonition-icon--failure: url('data:image/svg+xml;charset=utf-8,'); - --md-admonition-icon--danger: url('data:image/svg+xml;charset=utf-8,'); - --md-admonition-icon--bug: url('data:image/svg+xml;charset=utf-8,'); - --md-admonition-icon--example: url('data:image/svg+xml;charset=utf-8,'); - --md-admonition-icon--quote: url('data:image/svg+xml;charset=utf-8,'); - --md-footnotes-icon: url('data:image/svg+xml;charset=utf-8,'); - --md-details-icon: url('data:image/svg+xml;charset=utf-8,'); - --md-tasklist-icon: url('data:image/svg+xml;charset=utf-8,'); - --md-tasklist-icon--checked: url('data:image/svg+xml;charset=utf-8,'); - --md-nav-icon--prev: url('data:image/svg+xml;charset=utf-8,'); - --md-nav-icon--next: url('data:image/svg+xml;charset=utf-8,'); - --md-toc-icon: url('data:image/svg+xml;charset=utf-8,'); - --md-clipboard-icon: url('data:image/svg+xml;charset=utf-8,'); - --md-search-result-icon: url('data:image/svg+xml;charset=utf-8,'); - --md-source-forks-icon: url('data:image/svg+xml;charset=utf-8,'); - --md-source-repositories-icon: url('data:image/svg+xml;charset=utf-8,'); - --md-source-stars-icon: url('data:image/svg+xml;charset=utf-8,'); - --md-source-version-icon: url('data:image/svg+xml;charset=utf-8,'); - --md-version-icon: url('data:image/svg+xml;charset=utf-8,'); - } - - :host > * { - /* CODE */ - --md-code-fg-color: ${theme.palette.text.primary}; - --md-code-bg-color: ${theme.palette.background.paper}; - --md-code-hl-color: ${alpha(theme.palette.warning.main, 0.5)}; - --md-code-hl-keyword-color: ${ - theme.palette.type === 'dark' - ? theme.palette.primary.light - : theme.palette.primary.dark - }; - --md-code-hl-function-color: ${ - theme.palette.type === 'dark' - ? theme.palette.secondary.light - : theme.palette.secondary.dark - }; - --md-code-hl-string-color: ${ - theme.palette.type === 'dark' - ? theme.palette.success.light - : theme.palette.success.dark - }; - --md-code-hl-number-color: ${ - theme.palette.type === 'dark' - ? theme.palette.error.light - : theme.palette.error.dark - }; - --md-code-hl-constant-color: var(--md-code-hl-function-color); - --md-code-hl-special-color: var(--md-code-hl-function-color); - --md-code-hl-name-color: var(--md-code-fg-color); - --md-code-hl-comment-color: var(--md-default-fg-color--light); - --md-code-hl-generic-color: var(--md-default-fg-color--light); - --md-code-hl-variable-color: var(--md-default-fg-color--light); - --md-code-hl-operator-color: var(--md-default-fg-color--light); - --md-code-hl-punctuation-color: var(--md-default-fg-color--light); - - /* TYPESET */ - --md-typeset-font-size: 1rem; - --md-typeset-color: var(--md-default-fg-color); - --md-typeset-a-color: var(--md-accent-fg-color); - --md-typeset-table-color: ${theme.palette.text.primary}; - --md-typeset-del-color: ${ - theme.palette.type === 'dark' - ? alpha(theme.palette.error.dark, 0.5) - : alpha(theme.palette.error.light, 0.5) - }; - --md-typeset-ins-color: ${ - theme.palette.type === 'dark' - ? alpha(theme.palette.success.dark, 0.5) - : alpha(theme.palette.success.light, 0.5) - }; - --md-typeset-mark-color: ${ - theme.palette.type === 'dark' - ? alpha(theme.palette.warning.dark, 0.5) - : alpha(theme.palette.warning.light, 0.5) - }; - } - - @media screen and (max-width: 76.1875em) { - :host > * { - /* TYPESET */ - --md-typeset-font-size: .9rem; - } - } - - @media screen and (max-width: 600px) { - :host > * { - /* TYPESET */ - --md-typeset-font-size: .7rem; - } - } - `, - }), - injectCss({ - // Reset - css: ` - body { - --md-text-color: var(--md-default-fg-color); - --md-text-link-color: var(--md-accent-fg-color); - --md-text-font-family: ${theme.typography.fontFamily}; - font-family: var(--md-text-font-family); - background-color: unset; - } - `, - }), - injectCss({ - // Layout - css: ` - .md-grid { - max-width: 100%; - margin: 0; - } - - .md-nav { - font-size: calc(var(--md-typeset-font-size) * 0.9); - } - .md-nav__link { - display: flex; - align-items: center; - justify-content: space-between; - } - .md-nav__icon { - height: 20px !important; - width: 20px !important; - margin-left:${theme.spacing(1)}px; - } - .md-nav__icon svg { - margin: 0; - width: 20px !important; - height: 20px !important; - } - .md-nav__icon:after { - width: 20px !important; - height: 20px !important; - } - - .md-main__inner { - margin-top: 0; - } - - .md-sidebar { - bottom: 75px; - position: fixed; - width: 16rem; - overflow-y: auto; - overflow-x: hidden; - scrollbar-color: rgb(193, 193, 193) #eee; - scrollbar-width: thin; - } - .md-sidebar .md-sidebar__scrollwrap { - width: calc(16rem - 10px); - } - .md-sidebar--secondary { - right: ${theme.spacing(3)}px; - } - .md-sidebar::-webkit-scrollbar { - width: 5px; - } - .md-sidebar::-webkit-scrollbar-button { - width: 5px; - height: 5px; - } - .md-sidebar::-webkit-scrollbar-track { - background: #eee; - border: 1 px solid rgb(250, 250, 250); - box-shadow: 0px 0px 3px #dfdfdf inset; - border-radius: 3px; - } - .md-sidebar::-webkit-scrollbar-thumb { - width: 5px; - background: rgb(193, 193, 193); - border: transparent; - border-radius: 3px; - } - .md-sidebar::-webkit-scrollbar-thumb:hover { - background: rgb(125, 125, 125); - } - - .md-content { - max-width: calc(100% - 16rem * 2); - margin-left: 16rem; - margin-bottom: 50px; - } - - .md-footer { - position: fixed; - bottom: 0px; - } - .md-footer__title { - background-color: unset; - } - .md-footer-nav__link { - width: 16rem; - } - - .md-dialog { - background-color: unset; - } - - @media screen and (min-width: 76.25em) { - .md-sidebar { - height: auto; - } - } - - @media screen and (max-width: 76.1875em) { - .md-nav { - transition: none !important; - background-color: var(--md-default-bg-color) - } - .md-nav--primary .md-nav__title { - cursor: auto; - color: var(--md-default-fg-color); - font-weight: 700; - white-space: normal; - line-height: 1rem; - height: auto; - display: flex; - flex-flow: column; - row-gap: 1.6rem; - padding: 1.2rem .8rem .8rem; - background-color: var(--md-default-bg-color); - } - .md-nav--primary .md-nav__title~.md-nav__list { - box-shadow: none; - } - .md-nav--primary .md-nav__title ~ .md-nav__list > :first-child { - border-top: none; - } - .md-nav--primary .md-nav__title .md-nav__button { - display: none; - } - .md-nav--primary .md-nav__title .md-nav__icon { - color: var(--md-default-fg-color); - position: static; - height: auto; - margin: 0 0 0 -0.2rem; - } - .md-nav--primary > .md-nav__title [for="none"] { - padding-top: 0; - } - .md-nav--primary .md-nav__item { - border-top: none; - } - .md-nav--primary :is(.md-nav__title,.md-nav__item) { - font-size : var(--md-typeset-font-size); - } - .md-nav .md-source { - display: none; - } - - .md-sidebar { - height: 100%; - } - .md-sidebar--primary { - width: 12.1rem !important; - z-index: 200; - left: ${ - sidebar.isPinned - ? 'calc(-12.1rem + 242px)' - : 'calc(-12.1rem + 72px)' - } !important; - } - .md-sidebar--secondary:not([hidden]) { - display: none; - } - - .md-content { - max-width: 100%; - margin-left: 0; - } - - .md-header__button { - margin: 0.4rem 0; - margin-left: 0.4rem; - padding: 0; - } - - .md-overlay { - left: 0; - } - - .md-footer { - position: static; - padding-left: 0; - } - .md-footer-nav__link { - /* footer links begin to overlap at small sizes without setting width */ - width: 50%; - } - } - - @media screen and (max-width: 600px) { - .md-sidebar--primary { - left: -12.1rem !important; - width: 12.1rem; - } - } - `, - }), - injectCss({ - // Typeset - css: ` - .md-typeset { - font-size: var(--md-typeset-font-size); - } - - ${headings.reduce((style, heading) => { - const styles = theme.typography[heading]; - const { lineHeight, fontFamily, fontWeight, fontSize } = styles; - const calculate = (value: typeof fontSize) => { - let factor: number | string = 1; - if (typeof value === 'number') { - // 60% of the size defined because it is too big - factor = (value / 16) * 0.6; - } - if (typeof value === 'string') { - factor = value.replace('rem', ''); - } - return `calc(${factor} * var(--md-typeset-font-size))`; - }; - return style.concat(` - .md-typeset ${heading} { - color: var(--md-default-fg-color); - line-height: ${lineHeight}; - font-family: ${fontFamily}; - font-weight: ${fontWeight}; - font-size: ${calculate(fontSize)}; - } - `); - }, '')} - - .md-typeset .md-content__button { - color: var(--md-default-fg-color); - } - - .md-typeset hr { - border-bottom: 0.05rem dotted ${theme.palette.divider}; - } - - .md-typeset details { - font-size: var(--md-typeset-font-size) !important; - } - .md-typeset details summary { - padding-left: 2.5rem !important; - } - .md-typeset details summary:before, - .md-typeset details summary:after { - top: 50% !important; - width: 20px !important; - height: 20px !important; - transform: rotate(0deg) translateY(-50%) !important; - } - .md-typeset details[open] > summary:after { - transform: rotate(90deg) translateX(-50%) !important; - } - - .md-typeset blockquote { - color: var(--md-default-fg-color--light); - border-left: 0.2rem solid var(--md-default-fg-color--light); - } - - .md-typeset table:not([class]) { - font-size: var(--md-typeset-font-size); - border: 1px solid var(--md-default-fg-color); - border-bottom: none; - border-collapse: collapse; - } - .md-typeset table:not([class]) th { - font-weight: bold; - } - .md-typeset table:not([class]) td, .md-typeset table:not([class]) th { - border-bottom: 1px solid var(--md-default-fg-color); - } - - .md-typeset pre > code::-webkit-scrollbar-thumb { - background-color: hsla(0, 0%, 0%, 0.32); - } - .md-typeset pre > code::-webkit-scrollbar-thumb:hover { - background-color: hsla(0, 0%, 0%, 0.87); - } - `, - }), - injectCss({ - // Animations - css: ` - /* - Disable CSS animations on link colors as they lead to issues in dark mode. - The dark mode color theme is applied later and theirfore there is always an animation from light to dark mode when navigation between pages. - */ - .md-dialog, .md-nav__link, .md-footer__link, .md-typeset a, .md-typeset a::before, .md-typeset .headerlink { - transition: none; - } - `, - }), - injectCss({ - // Extensions - css: ` - /* HIGHLIGHT */ - .highlight .md-clipboard:after { - content: unset; - } - - .highlight .nx { - color: ${theme.palette.type === 'dark' ? '#ff53a3' : '#ec407a'}; - } - - /* CODE HILITE */ - .codehilite .gd { - background-color: ${ - theme.palette.type === 'dark' - ? 'rgba(248,81,73,0.65)' - : '#fdd' - }; - } - - .codehilite .gi { - background-color: ${ - theme.palette.type === 'dark' - ? 'rgba(46,160,67,0.65)' - : '#dfd' - }; - } - - /* TABBED */ - .tabbed-set>input:nth-child(1):checked~.tabbed-labels>:nth-child(1), - .tabbed-set>input:nth-child(2):checked~.tabbed-labels>:nth-child(2), - .tabbed-set>input:nth-child(3):checked~.tabbed-labels>:nth-child(3), - .tabbed-set>input:nth-child(4):checked~.tabbed-labels>:nth-child(4), - .tabbed-set>input:nth-child(5):checked~.tabbed-labels>:nth-child(5), - .tabbed-set>input:nth-child(6):checked~.tabbed-labels>:nth-child(6), - .tabbed-set>input:nth-child(7):checked~.tabbed-labels>:nth-child(7), - .tabbed-set>input:nth-child(8):checked~.tabbed-labels>:nth-child(8), - .tabbed-set>input:nth-child(9):checked~.tabbed-labels>:nth-child(9), - .tabbed-set>input:nth-child(10):checked~.tabbed-labels>:nth-child(10), - .tabbed-set>input:nth-child(11):checked~.tabbed-labels>:nth-child(11), - .tabbed-set>input:nth-child(12):checked~.tabbed-labels>:nth-child(12), - .tabbed-set>input:nth-child(13):checked~.tabbed-labels>:nth-child(13), - .tabbed-set>input:nth-child(14):checked~.tabbed-labels>:nth-child(14), - .tabbed-set>input:nth-child(15):checked~.tabbed-labels>:nth-child(15), - .tabbed-set>input:nth-child(16):checked~.tabbed-labels>:nth-child(16), - .tabbed-set>input:nth-child(17):checked~.tabbed-labels>:nth-child(17), - .tabbed-set>input:nth-child(18):checked~.tabbed-labels>:nth-child(18), - .tabbed-set>input:nth-child(19):checked~.tabbed-labels>:nth-child(19), - .tabbed-set>input:nth-child(20):checked~.tabbed-labels>:nth-child(20) { - color: var(--md-accent-fg-color); - border-color: var(--md-accent-fg-color); - } - - /* TASK-LIST */ - .task-list-control .task-list-indicator::before { - background-color: ${theme.palette.action.disabledBackground}; - } - .task-list-control [type="checkbox"]:checked + .task-list-indicator:before { - background-color: ${theme.palette.success.main}; - } - - /* ADMONITION */ - .admonition { - font-size: var(--md-typeset-font-size) !important; - } - .admonition .admonition-title { - padding-left: 2.5rem !important; - } - - .admonition .admonition-title:before { - top: 50% !important; - width: 20px !important; - height: 20px !important; - transform: translateY(-50%) !important; - } - `, - }), + stylesTransformer, ]), [ // only add dependencies that are in state or memorized variables to avoid unnecessary calls between re-renders entityRef, - theme, - sidebar, - configApi, scmIntegrationsApi, techdocsStorageApi, + sanitizerTransformer, + stylesTransformer, ], ); diff --git a/plugins/techdocs/src/reader/components/TechDocsReaderPageHeader/TechDocsReaderPageHeader.tsx b/plugins/techdocs/src/reader/components/TechDocsReaderPageHeader/TechDocsReaderPageHeader.tsx index 204ac835e4..8826154eee 100644 --- a/plugins/techdocs/src/reader/components/TechDocsReaderPageHeader/TechDocsReaderPageHeader.tsx +++ b/plugins/techdocs/src/reader/components/TechDocsReaderPageHeader/TechDocsReaderPageHeader.tsx @@ -91,7 +91,7 @@ export const TechDocsReaderPageHeader = ( }, [metadata, setTitle, setSubtitle]); const appTitle = configApi.getOptional('app.title') || 'Backstage'; - const tabTitle = [subtitle, title, appTitle].filter(Boolean).join(' | '); + const tabTitle = [title, subtitle, appTitle].filter(Boolean).join(' | '); const { locationMetadata, spec } = entityMetadata || {}; const lifecycle = spec?.lifecycle; @@ -110,6 +110,7 @@ export const TechDocsReaderPageHeader = ( } diff --git a/plugins/techdocs/src/reader/transformers/html/hooks/iframes.ts b/plugins/techdocs/src/reader/transformers/html/hooks/iframes.ts new file mode 100644 index 0000000000..25259dbc43 --- /dev/null +++ b/plugins/techdocs/src/reader/transformers/html/hooks/iframes.ts @@ -0,0 +1,50 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Checks whether a node is iframe or not. + * @param node - can be any element. + * @returns true when node is iframe. + */ +const isIframe = (node: Element) => node.nodeName === 'IFRAME'; + +/** + * Checks whether a iframe is safe or not. + * @param node - is an iframe element. + * @param hosts - list of allowed hosts. + * @returns true when iframe is included in hosts. + */ +const isSafe = (node: Element, hosts: string[]) => { + const src = node.getAttribute('src') || ''; + try { + const { host } = new URL(src); + return hosts.includes(host); + } catch { + return false; + } +}; + +/** + * Returns a function that removes unsafe iframe nodes. + * @param node - can be any element. + * @param hosts - list of allowed hosts. + */ +export const removeUnsafeIframes = (hosts: string[]) => (node: Element) => { + if (isIframe(node) && !isSafe(node, hosts)) { + node.remove(); + } + return node; +}; diff --git a/plugins/techdocs/src/reader/transformers/html/hooks/index.ts b/plugins/techdocs/src/reader/transformers/html/hooks/index.ts new file mode 100644 index 0000000000..a4356db2e9 --- /dev/null +++ b/plugins/techdocs/src/reader/transformers/html/hooks/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { removeUnsafeLinks } from './links'; +export { removeUnsafeIframes } from './iframes'; diff --git a/plugins/techdocs/src/reader/transformers/html/hooks/links.ts b/plugins/techdocs/src/reader/transformers/html/hooks/links.ts new file mode 100644 index 0000000000..38f775cfbe --- /dev/null +++ b/plugins/techdocs/src/reader/transformers/html/hooks/links.ts @@ -0,0 +1,51 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const MKDOCS_CSS = /main\.[A-Fa-f0-9]{8}\.min\.css$/; +const GOOGLE_FONTS = /^https:\/\/fonts\.googleapis\.com/; +const GSTATIC_FONTS = /^https:\/\/fonts\.gstatic\.com/; + +/** + * Checks whether a node is link or not. + * @param node - can be any element. + * @returns true when node is link. + */ +const isLink = (node: Element) => node.nodeName === 'LINK'; + +/** + * Checks whether a link is safe or not. + * @param node - is an link element. + * @returns true when link is mkdocs css, google fonts or gstatic fonts. + */ +const isSafe = (node: Element) => { + const href = node?.getAttribute('href') || ''; + const isMkdocsCss = href.match(MKDOCS_CSS); + const isGoogleFonts = href.match(GOOGLE_FONTS); + const isGstaticFonts = href.match(GSTATIC_FONTS); + return isMkdocsCss || isGoogleFonts || isGstaticFonts; +}; + +/** + * Function that removes unsafe link nodes. + * @param node - can be any element. + * @param hosts - list of allowed hosts. + */ +export const removeUnsafeLinks = (node: Element) => { + if (isLink(node) && !isSafe(node)) { + node.remove(); + } + return node; +}; diff --git a/plugins/techdocs/src/reader/transformers/html/index.ts b/plugins/techdocs/src/reader/transformers/html/index.ts new file mode 100644 index 0000000000..4b8bd06798 --- /dev/null +++ b/plugins/techdocs/src/reader/transformers/html/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { useSanitizerTransformer } from './transformer'; diff --git a/plugins/techdocs/src/reader/transformers/html/transformer.test.tsx b/plugins/techdocs/src/reader/transformers/html/transformer.test.tsx new file mode 100644 index 0000000000..dc4eeb85be --- /dev/null +++ b/plugins/techdocs/src/reader/transformers/html/transformer.test.tsx @@ -0,0 +1,84 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React, { FC } from 'react'; +import { renderHook } from '@testing-library/react-hooks'; + +import { ConfigReader } from '@backstage/core-app-api'; +import { ConfigApi, configApiRef } from '@backstage/core-plugin-api'; +import { TestApiProvider } from '@backstage/test-utils'; + +import { useSanitizerTransformer } from './transformer'; + +const configApiMock: ConfigApi = new ConfigReader({ + techdocs: { + sanitizer: { + allowedIframeHosts: ['docs.google.com'], + }, + }, +}); + +const wrapper: FC = ({ children }) => ( + + {children} + +); + +describe('Transformers > Html', () => { + it('should return a function that removes unsafe links from a given dom element', async () => { + const { result } = renderHook(() => useSanitizerTransformer(), { wrapper }); + + const dirtyDom = document.createElement('html'); + dirtyDom.innerHTML = ` + + + + + + + `; + const clearDom = await result.current(dirtyDom); // calling html transformer + + const links = Array.from( + clearDom.querySelectorAll('head > link'), + ); + expect(links).toHaveLength(3); + expect(links[0].href).toMatch('assets/stylesheets/main.50e68009.min.css'); + expect(links[1].href).toMatch('https://fonts.googleapis.com'); + expect(links[2].href).toMatch('https://fonts.gstatic.com'); + }); + + it('should return a function that removes unsafe iframes from a given dom element', async () => { + const { result } = renderHook(() => useSanitizerTransformer(), { wrapper }); + + const dirtyDom = document.createElement('html'); + dirtyDom.innerHTML = ` + + + + + + `; + const clearDom = await result.current(dirtyDom); // calling html transformer + + const iframes = Array.from( + clearDom.querySelectorAll('body > iframe'), + ); + + expect(iframes).toHaveLength(1); + expect(iframes[0].src).toMatch('docs.google.com'); + }); +}); diff --git a/plugins/techdocs/src/reader/transformers/html/transformer.ts b/plugins/techdocs/src/reader/transformers/html/transformer.ts new file mode 100644 index 0000000000..a197a2d4b7 --- /dev/null +++ b/plugins/techdocs/src/reader/transformers/html/transformer.ts @@ -0,0 +1,63 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import DOMPurify from 'dompurify'; +import { useMemo, useCallback } from 'react'; + +import { useApi, configApiRef } from '@backstage/core-plugin-api'; + +import { Transformer } from '../transformer'; +import { removeUnsafeLinks, removeUnsafeIframes } from './hooks'; + +/** + * Returns html sanitizer configuration + */ +const useSanitizerConfig = () => { + const configApi = useApi(configApiRef); + + return useMemo(() => { + return configApi.getOptionalConfig('techdocs.sanitizer'); + }, [configApi]); +}; + +/** + * Returns a transformer that sanitizes the dom's internal html. + */ +export const useSanitizerTransformer = (): Transformer => { + const config = useSanitizerConfig(); + + return useCallback( + async (dom: Element) => { + const hosts = config?.getOptionalStringArray('allowedIframeHosts'); + + DOMPurify.addHook('beforeSanitizeElements', removeUnsafeLinks); + const tags = ['link']; + + if (hosts) { + tags.push('iframe'); + DOMPurify.addHook('beforeSanitizeElements', removeUnsafeIframes(hosts)); + } + + return DOMPurify.sanitize(dom.innerHTML, { + ADD_TAGS: tags, + FORBID_TAGS: ['style'], + WHOLE_DOCUMENT: true, + RETURN_DOM: true, + }); + }, + [config], + ); +}; diff --git a/plugins/techdocs/src/reader/transformers/index.ts b/plugins/techdocs/src/reader/transformers/index.ts index dcd79180a7..dc3c43a584 100644 --- a/plugins/techdocs/src/reader/transformers/index.ts +++ b/plugins/techdocs/src/reader/transformers/index.ts @@ -14,6 +14,8 @@ * limitations under the License. */ +export * from './html'; +export * from './styles'; export * from './addBaseUrl'; export * from './addGitFeedbackLink'; export * from './addSidebarToggle'; @@ -23,7 +25,5 @@ export * from './copyToClipboard'; export * from './removeMkdocsHeader'; export * from './simplifyMkdocsFooter'; export * from './onCssReady'; -export * from './sanitizeDOM'; -export * from './injectCss'; export * from './scrollIntoAnchor'; export * from './transformer'; diff --git a/plugins/techdocs/src/reader/transformers/injectCss.test.ts b/plugins/techdocs/src/reader/transformers/injectCss.test.ts deleted file mode 100644 index 6d0eb8daa9..0000000000 --- a/plugins/techdocs/src/reader/transformers/injectCss.test.ts +++ /dev/null @@ -1,40 +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 { createTestShadowDom } from '../../test-utils'; -import { injectCss } from './injectCss'; - -describe('injectCss', () => { - it('should inject style with passed css in head', async () => { - const html = ` - - - - - `; - const injectedCss = '* {background-color: #fff}'; - - const shadowDom = await createTestShadowDom(html, { - preTransformers: [injectCss({ css: injectedCss })], - postTransformers: [], - }); - - const styleElement = shadowDom.querySelector('head > style'); - - expect(styleElement).toBeTruthy(); - expect(styleElement!.innerHTML).toEqual(injectedCss); - }); -}); diff --git a/plugins/techdocs/src/reader/transformers/sanitizeDOM.test.ts b/plugins/techdocs/src/reader/transformers/sanitizeDOM.test.ts deleted file mode 100644 index 697b01259c..0000000000 --- a/plugins/techdocs/src/reader/transformers/sanitizeDOM.test.ts +++ /dev/null @@ -1,254 +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 { ConfigReader } from '@backstage/config'; -import { createTestShadowDom, FIXTURES } from '../../test-utils'; -import { Transformer } from './index'; -import { sanitizeDOM } from './sanitizeDOM'; - -const injectMaliciousLink = (): Transformer => dom => { - const link = document.createElement('a'); - link.setAttribute('id', 'test-malicious-link'); - link.setAttribute('onclick', 'alert("Hello world");'); - dom.querySelector('body')?.appendChild(link); - return dom; -}; - -describe('sanitizeDOM', () => { - it('contains a script tag', async () => { - const shadowDom = await createTestShadowDom(FIXTURES.FIXTURE_STANDARD_PAGE); - - expect(shadowDom.querySelectorAll('script').length).toBeGreaterThan(0); - }); - - it('does not contain a script tag', async () => { - const shadowDom = await createTestShadowDom( - FIXTURES.FIXTURE_STANDARD_PAGE, - { - preTransformers: [sanitizeDOM()], - postTransformers: [], - }, - ); - - expect(shadowDom.querySelectorAll('script').length).toBe(0); - }); - - it('contains link with a onClick attribute', async () => { - const shadowDom = await createTestShadowDom( - FIXTURES.FIXTURE_STANDARD_PAGE, - { - preTransformers: [injectMaliciousLink()], - postTransformers: [], - }, - ); - - expect( - shadowDom.querySelector('#test-malicious-link')?.hasAttribute('onclick'), - ).toBeTruthy(); - }); - - it('does not contain link with a onClick attribute', async () => { - const shadowDom = await createTestShadowDom( - FIXTURES.FIXTURE_STANDARD_PAGE, - { - preTransformers: [sanitizeDOM()], - postTransformers: [], - }, - ); - - expect( - shadowDom.querySelector('#test-malicious-link')?.hasAttribute('onclick'), - ).toBeFalsy(); - }); - - it('removes style tags', async () => { - const html = ` - - - - - - - - `; - - const shadowDom = await createTestShadowDom(html, { - preTransformers: [sanitizeDOM()], - postTransformers: [], - }); - - expect(shadowDom.querySelectorAll('style').length).toEqual(0); - }); - - it('does not remove link tags', async () => { - const html = ` - - - - - - - - `; - - const shadowDom = await createTestShadowDom(html, { - preTransformers: [sanitizeDOM()], - postTransformers: [], - }); - - expect(shadowDom.querySelectorAll('link').length).toEqual(1); - }); - - it('render iframe where src host is in allowedIframeHosts', async () => { - const html = ` - - - - - - - - - - `; - const config = new ConfigReader({ - allowedIframeHosts: ['example.com'], - }); - const shadowDom = await createTestShadowDom(html, { - preTransformers: [sanitizeDOM(config)], - postTransformers: [], - }); - expect(shadowDom.querySelectorAll('link').length).toEqual(1); - expect(shadowDom.querySelectorAll('iframe').length).toEqual(1); - expect(shadowDom.querySelectorAll('iframe')[0].getAttribute('src')).toBe( - 'https://example.com?test=1', - ); - }); - - it('should remove all iframes without allowedIframeHosts', async () => { - const html = ` - - - - - - - - - - `; - const config = new ConfigReader({}); - const shadowDom = await createTestShadowDom(html, { - preTransformers: [sanitizeDOM(config)], - postTransformers: [], - }); - expect(shadowDom.querySelectorAll('link').length).toEqual(1); - expect(shadowDom.querySelectorAll('iframe').length).toEqual(0); - }); - - it('should remove iframe with invalid url in src', async () => { - const html = ` - - - - - - - - - `; - const config = new ConfigReader({ - allowedIframeHosts: ['example.com'], - }); - const shadowDom = await createTestShadowDom(html, { - preTransformers: [sanitizeDOM(config)], - postTransformers: [], - }); - expect(shadowDom.querySelectorAll('link').length).toEqual(1); - expect(shadowDom.querySelectorAll('iframe').length).toEqual(0); - }); - - test.each([ - { key: 'allow', value: '"camera \'none\'"', allowed: false }, - { key: 'allowfullscreen', value: true, allowed: false }, - { key: 'allowpaymentrequest', value: true, allowed: false }, - { key: 'height', value: true, allowed: true }, - { key: 'loading', value: "'lazy'", allowed: true }, - { key: 'name', value: "'example'", allowed: true }, - { key: 'referrerpolicy', value: "'no-referrer'", allowed: false }, - { key: 'sandbox', value: "'allow-forms'", allowed: false }, - { key: 'srcdoc', value: "'

Hello world!

'", allowed: false }, - { key: 'onload', value: "'alert(1)'", allowed: false }, - ])('check if the iframe has the attribute %p', async attr => { - const html = ` - - - - - - - - - `; - const config = new ConfigReader({ - allowedIframeHosts: ['example.com'], - }); - const shadowDom = await createTestShadowDom(html, { - preTransformers: [sanitizeDOM(config)], - postTransformers: [], - }); - expect(shadowDom.querySelectorAll('link').length).toEqual(1); - expect(shadowDom.querySelectorAll('iframe').length).toEqual(1); - expect(shadowDom.querySelectorAll('iframe')[0].hasAttribute(attr.key)).toBe( - attr.allowed, - ); - }); - - describe('safe head links', () => { - let shadowDom: ShadowRoot; - - beforeEach(async () => { - shadowDom = await createTestShadowDom(FIXTURES.FIXTURE_STANDARD_PAGE, { - preTransformers: [sanitizeDOM()], - postTransformers: [], - }); - }); - - it('should not sanitize the techdocs css', async () => { - const techdocsCss = shadowDom.querySelector( - 'link[href$="main.fe0cca5b.min.css"]', - ); - const rel = techdocsCss!.getAttribute('rel'); - expect(rel).toBe('stylesheet'); - }); - - it('should not sanitize google fonts', async () => { - const googleFonts = shadowDom.querySelector( - 'link[href^="https://fonts.googleapis.com"]', - ); - const rel = googleFonts!.getAttribute('rel'); - expect(rel).toBe('stylesheet'); - }); - - it('should not sanitize gstatic fonts', async () => { - const gstaticFonts = shadowDom.querySelector( - 'link[href^="https://fonts.gstatic.com"]', - ); - const rel = gstaticFonts!.getAttribute('rel'); - expect(rel).toBe('preconnect'); - }); - }); -}); diff --git a/plugins/techdocs/src/reader/transformers/sanitizeDOM.ts b/plugins/techdocs/src/reader/transformers/sanitizeDOM.ts deleted file mode 100644 index da4bd793be..0000000000 --- a/plugins/techdocs/src/reader/transformers/sanitizeDOM.ts +++ /dev/null @@ -1,87 +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. - */ - -const TECHDOCS_CSS = /main\.[A-Fa-f0-9]{8}\.min\.css$/; -const GOOGLE_FONTS = /^https:\/\/fonts\.googleapis\.com/; -const GSTATIC_FONTS = /^https:\/\/fonts\.gstatic\.com/; - -export const safeLinksHook = (node: Element) => { - if (node.nodeName && node.nodeName === 'LINK') { - const href = node.getAttribute('href') || ''; - if (href.match(TECHDOCS_CSS)) { - node.setAttribute('rel', 'stylesheet'); - } - if (href.match(GOOGLE_FONTS)) { - node.setAttribute('rel', 'stylesheet'); - } - if (href.match(GSTATIC_FONTS)) { - node.setAttribute('rel', 'preconnect'); - } - } - return node; -}; - -const filterIframeHook = (allowedIframeHosts: string[]) => (node: Element) => { - if (node.nodeName === 'IFRAME') { - const src = node.getAttribute('src'); - if (!src) { - node.remove(); - return node; - } - - try { - const srcUrl = new URL(src); - const isMatch = allowedIframeHosts.some(host => srcUrl.host === host); - if (!isMatch) { - node.remove(); - } - } catch (error) { - // eslint-disable-next-line no-console - console.warn(`Invalid iframe src, ${error}`); - node.remove(); - } - } - return node; -}; - -import { Config } from '@backstage/config'; -import DOMPurify from 'dompurify'; -import type { Transformer } from './transformer'; - -export const sanitizeDOM = (config?: Config): Transformer => { - const allowedIframeHosts = - config?.getOptionalStringArray('allowedIframeHosts') || []; - - return dom => { - DOMPurify.addHook('afterSanitizeAttributes', safeLinksHook); - const addTags = ['link']; - - if (allowedIframeHosts.length > 0) { - DOMPurify.addHook( - 'beforeSanitizeElements', - filterIframeHook(allowedIframeHosts), - ); - addTags.push('iframe'); - } - - return DOMPurify.sanitize(dom.innerHTML, { - ADD_TAGS: addTags, - FORBID_TAGS: ['style'], - WHOLE_DOCUMENT: true, - RETURN_DOM: true, - }); - }; -}; diff --git a/plugins/techdocs/src/reader/transformers/styles/index.ts b/plugins/techdocs/src/reader/transformers/styles/index.ts new file mode 100644 index 0000000000..50d5a6a368 --- /dev/null +++ b/plugins/techdocs/src/reader/transformers/styles/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { useStylesTransformer } from './transformer'; diff --git a/plugins/techdocs/src/reader/transformers/styles/rules/animations.ts b/plugins/techdocs/src/reader/transformers/styles/rules/animations.ts new file mode 100644 index 0000000000..0c50fb0633 --- /dev/null +++ b/plugins/techdocs/src/reader/transformers/styles/rules/animations.ts @@ -0,0 +1,26 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export default () => ` +/*================== Animations ==================*/ +/* + Disable CSS animations on link colors as they lead to issues in dark mode. + The dark mode color theme is applied later and theirfore there is always an animation from light to dark mode when navigation between pages. +*/ +.md-dialog, .md-nav__link, .md-footer__link, .md-typeset a, .md-typeset a::before, .md-typeset .headerlink { + transition: none; +} +`; diff --git a/plugins/techdocs/src/reader/transformers/styles/rules/extensions.ts b/plugins/techdocs/src/reader/transformers/styles/rules/extensions.ts new file mode 100644 index 0000000000..403858339e --- /dev/null +++ b/plugins/techdocs/src/reader/transformers/styles/rules/extensions.ts @@ -0,0 +1,91 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { RuleOptions } from './types'; + +export default ({ theme }: RuleOptions) => ` +/*================== Extensions ==================*/ + +/* HIGHLIGHT */ +.highlight .md-clipboard:after { + content: unset; +} + +.highlight .nx { + color: ${theme.palette.type === 'dark' ? '#ff53a3' : '#ec407a'}; +} + +/* CODE HILITE */ +.codehilite .gd { + background-color: ${ + theme.palette.type === 'dark' ? 'rgba(248,81,73,0.65)' : '#fdd' + }; +} + +.codehilite .gi { + background-color: ${ + theme.palette.type === 'dark' ? 'rgba(46,160,67,0.65)' : '#dfd' + }; +} + +/* TABBED */ +.tabbed-set>input:nth-child(1):checked~.tabbed-labels>:nth-child(1), +.tabbed-set>input:nth-child(2):checked~.tabbed-labels>:nth-child(2), +.tabbed-set>input:nth-child(3):checked~.tabbed-labels>:nth-child(3), +.tabbed-set>input:nth-child(4):checked~.tabbed-labels>:nth-child(4), +.tabbed-set>input:nth-child(5):checked~.tabbed-labels>:nth-child(5), +.tabbed-set>input:nth-child(6):checked~.tabbed-labels>:nth-child(6), +.tabbed-set>input:nth-child(7):checked~.tabbed-labels>:nth-child(7), +.tabbed-set>input:nth-child(8):checked~.tabbed-labels>:nth-child(8), +.tabbed-set>input:nth-child(9):checked~.tabbed-labels>:nth-child(9), +.tabbed-set>input:nth-child(10):checked~.tabbed-labels>:nth-child(10), +.tabbed-set>input:nth-child(11):checked~.tabbed-labels>:nth-child(11), +.tabbed-set>input:nth-child(12):checked~.tabbed-labels>:nth-child(12), +.tabbed-set>input:nth-child(13):checked~.tabbed-labels>:nth-child(13), +.tabbed-set>input:nth-child(14):checked~.tabbed-labels>:nth-child(14), +.tabbed-set>input:nth-child(15):checked~.tabbed-labels>:nth-child(15), +.tabbed-set>input:nth-child(16):checked~.tabbed-labels>:nth-child(16), +.tabbed-set>input:nth-child(17):checked~.tabbed-labels>:nth-child(17), +.tabbed-set>input:nth-child(18):checked~.tabbed-labels>:nth-child(18), +.tabbed-set>input:nth-child(19):checked~.tabbed-labels>:nth-child(19), +.tabbed-set>input:nth-child(20):checked~.tabbed-labels>:nth-child(20) { + color: var(--md-accent-fg-color); + border-color: var(--md-accent-fg-color); +} + +/* TASK-LIST */ +.task-list-control .task-list-indicator::before { + background-color: ${theme.palette.action.disabledBackground}; +} +.task-list-control [type="checkbox"]:checked + .task-list-indicator:before { + background-color: ${theme.palette.success.main}; +} + +/* ADMONITION */ +.admonition { + font-size: var(--md-typeset-font-size) !important; +} +.admonition .admonition-title { + padding-left: 2.5rem !important; +} + +.admonition .admonition-title:before { + top: 50% !important; + width: 20px !important; + height: 20px !important; + transform: translateY(-50%) !important; +} +`; diff --git a/plugins/techdocs/src/reader/transformers/styles/rules/index.ts b/plugins/techdocs/src/reader/transformers/styles/rules/index.ts new file mode 100644 index 0000000000..b41da508f0 --- /dev/null +++ b/plugins/techdocs/src/reader/transformers/styles/rules/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export * from './rules'; diff --git a/plugins/techdocs/src/reader/transformers/styles/rules/layout.ts b/plugins/techdocs/src/reader/transformers/styles/rules/layout.ts new file mode 100644 index 0000000000..2488504dd9 --- /dev/null +++ b/plugins/techdocs/src/reader/transformers/styles/rules/layout.ts @@ -0,0 +1,210 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { RuleOptions } from './types'; + +export default ({ theme, sidebar }: RuleOptions) => ` +/*================== Layout ==================*/ + +.md-grid { + max-width: 100%; + margin: 0; +} + +.md-nav { + font-size: calc(var(--md-typeset-font-size) * 0.9); +} +.md-nav__link { + display: flex; + align-items: center; + justify-content: space-between; +} +.md-nav__icon { + height: 20px !important; + width: 20px !important; + margin-left:${theme.spacing(1)}px; +} +.md-nav__icon svg { + margin: 0; + width: 20px !important; + height: 20px !important; +} +.md-nav__icon:after { + width: 20px !important; + height: 20px !important; +} + +.md-main__inner { + margin-top: 0; +} + +.md-sidebar { + bottom: 75px; + position: fixed; + width: 16rem; + overflow-y: auto; + overflow-x: hidden; + scrollbar-color: rgb(193, 193, 193) #eee; + scrollbar-width: thin; +} +.md-sidebar .md-sidebar__scrollwrap { + width: calc(16rem - 10px); +} +.md-sidebar--secondary { + right: ${theme.spacing(3)}px; +} +.md-sidebar::-webkit-scrollbar { + width: 5px; +} +.md-sidebar::-webkit-scrollbar-button { + width: 5px; + height: 5px; +} +.md-sidebar::-webkit-scrollbar-track { + background: #eee; + border: 1 px solid rgb(250, 250, 250); + box-shadow: 0px 0px 3px #dfdfdf inset; + border-radius: 3px; +} +.md-sidebar::-webkit-scrollbar-thumb { + width: 5px; + background: rgb(193, 193, 193); + border: transparent; + border-radius: 3px; +} +.md-sidebar::-webkit-scrollbar-thumb:hover { + background: rgb(125, 125, 125); +} + +.md-content { + max-width: calc(100% - 16rem * 2); + margin-left: 16rem; + margin-bottom: 50px; +} + +.md-footer { + position: fixed; + bottom: 0px; +} +.md-footer__title { + background-color: unset; +} +.md-footer-nav__link { + width: 16rem; +} + +.md-dialog { + background-color: unset; +} + +@media screen and (min-width: 76.25em) { + .md-sidebar { + height: auto; + } +} + +@media screen and (max-width: 76.1875em) { + .md-nav { + transition: none !important; + background-color: var(--md-default-bg-color) + } + .md-nav--primary .md-nav__title { + cursor: auto; + color: var(--md-default-fg-color); + font-weight: 700; + white-space: normal; + line-height: 1rem; + height: auto; + display: flex; + flex-flow: column; + row-gap: 1.6rem; + padding: 1.2rem .8rem .8rem; + background-color: var(--md-default-bg-color); + } + .md-nav--primary .md-nav__title~.md-nav__list { + box-shadow: none; + } + .md-nav--primary .md-nav__title ~ .md-nav__list > :first-child { + border-top: none; + } + .md-nav--primary .md-nav__title .md-nav__button { + display: none; + } + .md-nav--primary .md-nav__title .md-nav__icon { + color: var(--md-default-fg-color); + position: static; + height: auto; + margin: 0 0 0 -0.2rem; + } + .md-nav--primary > .md-nav__title [for="none"] { + padding-top: 0; + } + .md-nav--primary .md-nav__item { + border-top: none; + } + .md-nav--primary :is(.md-nav__title,.md-nav__item) { + font-size : var(--md-typeset-font-size); + } + .md-nav .md-source { + display: none; + } + + .md-sidebar { + height: 100%; + } + .md-sidebar--primary { + width: 12.1rem !important; + z-index: 200; + left: ${ + sidebar.isPinned ? 'calc(-12.1rem + 242px)' : 'calc(-12.1rem + 72px)' + } !important; + } + .md-sidebar--secondary:not([hidden]) { + display: none; + } + + .md-content { + max-width: 100%; + margin-left: 0; + } + + .md-header__button { + margin: 0.4rem 0; + margin-left: 0.4rem; + padding: 0; + } + + .md-overlay { + left: 0; + } + + .md-footer { + position: static; + padding-left: 0; + } + .md-footer-nav__link { + /* footer links begin to overlap at small sizes without setting width */ + width: 50%; + } +} + +@media screen and (max-width: 600px) { + .md-sidebar--primary { + left: -12.1rem !important; + width: 12.1rem; + } +} +`; diff --git a/plugins/techdocs/src/reader/transformers/styles/rules/reset.ts b/plugins/techdocs/src/reader/transformers/styles/rules/reset.ts new file mode 100644 index 0000000000..16868460c3 --- /dev/null +++ b/plugins/techdocs/src/reader/transformers/styles/rules/reset.ts @@ -0,0 +1,29 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { RuleOptions } from './types'; + +export default ({ theme }: RuleOptions) => ` +/*================== Reset ==================*/ + +body { + --md-text-color: var(--md-default-fg-color); + --md-text-link-color: var(--md-accent-fg-color); + --md-text-font-family: ${theme.typography.fontFamily}; + font-family: var(--md-text-font-family); + background-color: unset; +} +`; diff --git a/plugins/techdocs/src/reader/transformers/styles/rules/rules.ts b/plugins/techdocs/src/reader/transformers/styles/rules/rules.ts new file mode 100644 index 0000000000..917793733e --- /dev/null +++ b/plugins/techdocs/src/reader/transformers/styles/rules/rules.ts @@ -0,0 +1,38 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { default as variables } from './variables'; +import { default as reset } from './reset'; +import { default as layout } from './layout'; +import { default as typeset } from './typeset'; +import { default as animations } from './animations'; +import { default as extensions } from './extensions'; + +/** + * A list of style rules that will be applied to an element in the order they were added. + * + * @remarks + * The order of items is important, which means that a rule can override any other rule previously added to the list, + * i.e. the rules will be applied from the first added to the last added. + */ +export const rules = [ + variables, + reset, + layout, + typeset, + animations, + extensions, +]; diff --git a/plugins/techdocs/src/reader/transformers/styles/rules/types.ts b/plugins/techdocs/src/reader/transformers/styles/rules/types.ts new file mode 100644 index 0000000000..6419ef8e39 --- /dev/null +++ b/plugins/techdocs/src/reader/transformers/styles/rules/types.ts @@ -0,0 +1,39 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { BackstageTheme } from '@backstage/theme'; + +/** + * A Backstage sidebar object that contains properties such as its pin state. + */ +type BackstageSidebar = { + /** Tracks whether the user pinned the sidebar or not. */ + isPinned: boolean; +}; + +/** + * A dependencies object injected into rules by the style processor. + */ +export type RuleOptions = { + /** + * A Backstage theme object that contains the application's design tokens. + */ + theme: BackstageTheme; + /** + * A Backstage sidebar, see {@link BackstageSidebar} for more details. + */ + sidebar: BackstageSidebar; +}; diff --git a/plugins/techdocs/src/reader/transformers/styles/rules/typeset.ts b/plugins/techdocs/src/reader/transformers/styles/rules/typeset.ts new file mode 100644 index 0000000000..e24b61da83 --- /dev/null +++ b/plugins/techdocs/src/reader/transformers/styles/rules/typeset.ts @@ -0,0 +1,109 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { RuleOptions } from './types'; + +type TypographyHeadings = Pick< + RuleOptions['theme']['typography'], + 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' +>; + +type TypographyHeadingsKeys = keyof TypographyHeadings; + +const headings: TypographyHeadingsKeys[] = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6']; + +export default ({ theme }: RuleOptions) => ` +/*================== Typeset ==================*/ + +.md-typeset { + font-size: var(--md-typeset-font-size); +} + +${headings.reduce((style, heading) => { + const styles = theme.typography[heading]; + const { lineHeight, fontFamily, fontWeight, fontSize } = styles; + const calculate = (value: typeof fontSize) => { + let factor: number | string = 1; + if (typeof value === 'number') { + // 60% of the size defined because it is too big + factor = (value / 16) * 0.6; + } + if (typeof value === 'string') { + factor = value.replace('rem', ''); + } + return `calc(${factor} * var(--md-typeset-font-size))`; + }; + return style.concat(` + .md-typeset ${heading} { + color: var(--md-default-fg-color); + line-height: ${lineHeight}; + font-family: ${fontFamily}; + font-weight: ${fontWeight}; + font-size: ${calculate(fontSize)}; + } + `); +}, '')} + +.md-typeset .md-content__button { + color: var(--md-default-fg-color); +} + +.md-typeset hr { + border-bottom: 0.05rem dotted ${theme.palette.divider}; +} + +.md-typeset details { + font-size: var(--md-typeset-font-size) !important; +} +.md-typeset details summary { + padding-left: 2.5rem !important; +} +.md-typeset details summary:before, +.md-typeset details summary:after { + top: 50% !important; + width: 20px !important; + height: 20px !important; + transform: rotate(0deg) translateY(-50%) !important; +} +.md-typeset details[open] > summary:after { + transform: rotate(90deg) translateX(-50%) !important; +} + +.md-typeset blockquote { + color: var(--md-default-fg-color--light); + border-left: 0.2rem solid var(--md-default-fg-color--light); +} + +.md-typeset table:not([class]) { + font-size: var(--md-typeset-font-size); + border: 1px solid var(--md-default-fg-color); + border-bottom: none; + border-collapse: collapse; +} +.md-typeset table:not([class]) th { + font-weight: bold; +} +.md-typeset table:not([class]) td, .md-typeset table:not([class]) th { + border-bottom: 1px solid var(--md-default-fg-color); +} + +.md-typeset pre > code::-webkit-scrollbar-thumb { + background-color: hsla(0, 0%, 0%, 0.32); +} +.md-typeset pre > code::-webkit-scrollbar-thumb:hover { + background-color: hsla(0, 0%, 0%, 0.87); +} +`; diff --git a/plugins/techdocs/src/reader/transformers/styles/rules/variables.ts b/plugins/techdocs/src/reader/transformers/styles/rules/variables.ts new file mode 100644 index 0000000000..908cb39faf --- /dev/null +++ b/plugins/techdocs/src/reader/transformers/styles/rules/variables.ts @@ -0,0 +1,168 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { RuleOptions } from './types'; +import { alpha, lighten } from '@material-ui/core'; + +export default ({ theme }: RuleOptions) => ` +/*================== Variables ==================*/ +/* + As the MkDocs output is rendered in shadow DOM, the CSS variable definitions on the root selector are not applied. Instead, they have to be applied on :host. + As there is no way to transform the served main*.css yet (for example in the backend), we have to copy from main*.css and modify them. +*/ + +:host { + /* FONT */ + --md-default-fg-color: ${theme.palette.text.primary}; + --md-default-fg-color--light: ${theme.palette.text.secondary}; + --md-default-fg-color--lighter: ${lighten(theme.palette.text.secondary, 0.7)}; + --md-default-fg-color--lightest: ${lighten( + theme.palette.text.secondary, + 0.3, + )}; + + /* BACKGROUND */ + --md-default-bg-color:${theme.palette.background.default}; + --md-default-bg-color--light: ${theme.palette.background.paper}; + --md-default-bg-color--lighter: ${lighten( + theme.palette.background.paper, + 0.7, + )}; + --md-default-bg-color--lightest: ${lighten( + theme.palette.background.paper, + 0.3, + )}; + + /* PRIMARY */ + --md-primary-fg-color: ${theme.palette.primary.main}; + --md-primary-fg-color--light: ${theme.palette.primary.light}; + --md-primary-fg-color--dark: ${theme.palette.primary.dark}; + --md-primary-bg-color: ${theme.palette.primary.contrastText}; + --md-primary-bg-color--light: ${lighten( + theme.palette.primary.contrastText, + 0.7, + )}; + + /* ACCENT */ + --md-accent-fg-color: var(--md-primary-fg-color); + + /* SHADOW */ + --md-shadow-z1: ${theme.shadows[1]}; + --md-shadow-z2: ${theme.shadows[2]}; + --md-shadow-z3: ${theme.shadows[3]}; + + /* EXTENSIONS */ + --md-admonition-fg-color: var(--md-default-fg-color); + --md-admonition-bg-color: var(--md-default-bg-color); + /* Admonitions and others are using SVG masks to define icons. These masks are defined as CSS variables. */ + --md-admonition-icon--note: url('data:image/svg+xml;charset=utf-8,'); + --md-admonition-icon--abstract: url('data:image/svg+xml;charset=utf-8,'); + --md-admonition-icon--info: url('data:image/svg+xml;charset=utf-8,'); + --md-admonition-icon--tip: url('data:image/svg+xml;charset=utf-8,'); + --md-admonition-icon--success: url('data:image/svg+xml;charset=utf-8,'); + --md-admonition-icon--question: url('data:image/svg+xml;charset=utf-8,'); + --md-admonition-icon--warning: url('data:image/svg+xml;charset=utf-8,'); + --md-admonition-icon--failure: url('data:image/svg+xml;charset=utf-8,'); + --md-admonition-icon--danger: url('data:image/svg+xml;charset=utf-8,'); + --md-admonition-icon--bug: url('data:image/svg+xml;charset=utf-8,'); + --md-admonition-icon--example: url('data:image/svg+xml;charset=utf-8,'); + --md-admonition-icon--quote: url('data:image/svg+xml;charset=utf-8,'); + --md-footnotes-icon: url('data:image/svg+xml;charset=utf-8,'); + --md-details-icon: url('data:image/svg+xml;charset=utf-8,'); + --md-tasklist-icon: url('data:image/svg+xml;charset=utf-8,'); + --md-tasklist-icon--checked: url('data:image/svg+xml;charset=utf-8,'); + --md-nav-icon--prev: url('data:image/svg+xml;charset=utf-8,'); + --md-nav-icon--next: url('data:image/svg+xml;charset=utf-8,'); + --md-toc-icon: url('data:image/svg+xml;charset=utf-8,'); + --md-clipboard-icon: url('data:image/svg+xml;charset=utf-8,'); + --md-search-result-icon: url('data:image/svg+xml;charset=utf-8,'); + --md-source-forks-icon: url('data:image/svg+xml;charset=utf-8,'); + --md-source-repositories-icon: url('data:image/svg+xml;charset=utf-8,'); + --md-source-stars-icon: url('data:image/svg+xml;charset=utf-8,'); + --md-source-version-icon: url('data:image/svg+xml;charset=utf-8,'); + --md-version-icon: url('data:image/svg+xml;charset=utf-8,'); +} + +:host > * { + /* CODE */ + --md-code-fg-color: ${theme.palette.text.primary}; + --md-code-bg-color: ${theme.palette.background.paper}; + --md-code-hl-color: ${alpha(theme.palette.warning.main, 0.5)}; + --md-code-hl-keyword-color: ${ + theme.palette.type === 'dark' + ? theme.palette.primary.light + : theme.palette.primary.dark + }; + --md-code-hl-function-color: ${ + theme.palette.type === 'dark' + ? theme.palette.secondary.light + : theme.palette.secondary.dark + }; + --md-code-hl-string-color: ${ + theme.palette.type === 'dark' + ? theme.palette.success.light + : theme.palette.success.dark + }; + --md-code-hl-number-color: ${ + theme.palette.type === 'dark' + ? theme.palette.error.light + : theme.palette.error.dark + }; + --md-code-hl-constant-color: var(--md-code-hl-function-color); + --md-code-hl-special-color: var(--md-code-hl-function-color); + --md-code-hl-name-color: var(--md-code-fg-color); + --md-code-hl-comment-color: var(--md-default-fg-color--light); + --md-code-hl-generic-color: var(--md-default-fg-color--light); + --md-code-hl-variable-color: var(--md-default-fg-color--light); + --md-code-hl-operator-color: var(--md-default-fg-color--light); + --md-code-hl-punctuation-color: var(--md-default-fg-color--light); + + /* TYPESET */ + --md-typeset-font-size: 1rem; + --md-typeset-color: var(--md-default-fg-color); + --md-typeset-a-color: var(--md-accent-fg-color); + --md-typeset-table-color: ${theme.palette.text.primary}; + --md-typeset-del-color: ${ + theme.palette.type === 'dark' + ? alpha(theme.palette.error.dark, 0.5) + : alpha(theme.palette.error.light, 0.5) + }; + --md-typeset-ins-color: ${ + theme.palette.type === 'dark' + ? alpha(theme.palette.success.dark, 0.5) + : alpha(theme.palette.success.light, 0.5) + }; + --md-typeset-mark-color: ${ + theme.palette.type === 'dark' + ? alpha(theme.palette.warning.dark, 0.5) + : alpha(theme.palette.warning.light, 0.5) + }; +} + +@media screen and (max-width: 76.1875em) { + :host > * { + /* TYPESET */ + --md-typeset-font-size: .9rem; + } +} + +@media screen and (max-width: 600px) { + :host > * { + /* TYPESET */ + --md-typeset-font-size: .7rem; + } +} +`; diff --git a/plugins/techdocs/src/reader/transformers/styles/transformer.test.ts b/plugins/techdocs/src/reader/transformers/styles/transformer.test.ts new file mode 100644 index 0000000000..84ba16d6e6 --- /dev/null +++ b/plugins/techdocs/src/reader/transformers/styles/transformer.test.ts @@ -0,0 +1,48 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { renderHook } from '@testing-library/react-hooks'; +import { useStylesTransformer } from './transformer'; + +describe('Transformers > Styles', () => { + it('should return a function that injects all styles into a given dom element', () => { + const { result } = renderHook(() => useStylesTransformer()); + + const dom = document.createElement('html'); + dom.innerHTML = ''; + result.current(dom); // calling styles transformer + + const style = dom.querySelector('head > style'); + expect(style).toHaveTextContent( + '/*================== Variables ==================*/', + ); + expect(style).toHaveTextContent( + '/*================== Reset ==================*/', + ); + expect(style).toHaveTextContent( + '/*================== Layout ==================*/', + ); + expect(style).toHaveTextContent( + '/*================== Typeset ==================*/', + ); + expect(style).toHaveTextContent( + '/*================== Animations ==================*/', + ); + expect(style).toHaveTextContent( + '/*================== Extensions ==================*/', + ); + }); +}); diff --git a/plugins/techdocs/src/reader/transformers/styles/transformer.ts b/plugins/techdocs/src/reader/transformers/styles/transformer.ts new file mode 100644 index 0000000000..04a6d476af --- /dev/null +++ b/plugins/techdocs/src/reader/transformers/styles/transformer.ts @@ -0,0 +1,61 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { useCallback, useMemo } from 'react'; + +import { useTheme } from '@material-ui/core'; + +import { useSidebarPinState } from '@backstage/core-components'; +import { BackstageTheme } from '@backstage/theme'; + +import { Transformer } from '../transformer'; +import { rules } from './rules'; + +/** + * Sidebar pinned state to be used in computing style injections. + */ +const useSidebar = () => useSidebarPinState(); + +/** + * Process all rules and concatenate their definitions into a single style. + * @returns a string containing all processed style definitions. + */ +const useRuleStyles = () => { + const sidebar = useSidebar(); + const theme = useTheme(); + + return useMemo(() => { + const options = { theme, sidebar }; + return rules.reduce((styles, rule) => styles + rule(options), ''); + }, [theme, sidebar]); +}; + +/** + * Returns a transformer that inserts all style rules into the given element's head tag. + */ +export const useStylesTransformer = (): Transformer => { + const styles = useRuleStyles(); + + return useCallback( + (dom: Element) => { + dom + .getElementsByTagName('head')[0] + .insertAdjacentHTML('beforeend', ``); + return dom; + }, + [styles], + ); +}; diff --git a/plugins/techdocs/src/search/components/TechDocsSearch.tsx b/plugins/techdocs/src/search/components/TechDocsSearch.tsx index 509c991353..647520af8c 100644 --- a/plugins/techdocs/src/search/components/TechDocsSearch.tsx +++ b/plugins/techdocs/src/search/components/TechDocsSearch.tsx @@ -46,6 +46,7 @@ const useStyles = makeStyles({ */ export type TechDocsSearchProps = { entityId: CompoundEntityRef; + entityTitle?: string; debounceTime?: number; }; @@ -64,7 +65,7 @@ type TechDocsSearchResult = { }; const TechDocsSearchBar = (props: TechDocsSearchProps) => { - const { entityId, debounceTime = 150 } = props; + const { entityId, entityTitle, debounceTime = 150 } = props; const [open, setOpen] = useState(false); const navigate = useNavigate(); const { @@ -160,7 +161,7 @@ const TechDocsSearchBar = (props: TechDocsSearchProps) => { data-testid="techdocs-search-bar-input" variant="outlined" fullWidth - placeholder={`Search ${entityId.name} docs`} + placeholder={`Search ${entityTitle || entityId.name} docs`} value={value} onChange={handleQuery} InputProps={{ diff --git a/plugins/techdocs/src/search/components/TechDocsSearchResultListItem.tsx b/plugins/techdocs/src/search/components/TechDocsSearchResultListItem.tsx index 6181e05235..430165bf8b 100644 --- a/plugins/techdocs/src/search/components/TechDocsSearchResultListItem.tsx +++ b/plugins/techdocs/src/search/components/TechDocsSearchResultListItem.tsx @@ -14,9 +14,16 @@ * limitations under the License. */ -import React, { PropsWithChildren } from 'react'; -import { Divider, ListItem, ListItemText, makeStyles } from '@material-ui/core'; +import React, { PropsWithChildren, ReactNode } from 'react'; +import { + Divider, + ListItem, + ListItemIcon, + ListItemText, + makeStyles, +} from '@material-ui/core'; import { Link } from '@backstage/core-components'; +import { useAnalytics } from '@backstage/core-plugin-api'; import { ResultHighlight } from '@backstage/plugin-search-common'; import { HighlightedSearchResultText } from '@backstage/plugin-search-react'; @@ -36,8 +43,10 @@ const useStyles = makeStyles({ * @public */ export type TechDocsSearchResultListItemProps = { + icon?: ReactNode; result: any; highlight?: ResultHighlight; + rank?: number; lineClamp?: number; asListItem?: boolean; asLink?: boolean; @@ -55,12 +64,23 @@ export const TechDocsSearchResultListItem = ( const { result, highlight, + rank, lineClamp = 5, asListItem = true, asLink = true, title, + icon, } = props; const classes = useStyles(); + + const analytics = useAnalytics(); + const handleClick = () => { + analytics.captureEvent('discover', result.title, { + attributes: { to: result.location }, + value: rank, + }); + }; + const TextItem = () => { const resultTitle = highlight?.fields.title ? ( ) => - asLink ? {children} : <>{children}; + asLink ? ( + + {children} + + ) : ( + <>{children} + ); const ListItemWrapper = ({ children }: PropsWithChildren<{}>) => asListItem ? ( <> - - {children} + + {icon && {icon}} +
{children}
diff --git a/plugins/todo-backend/CHANGELOG.md b/plugins/todo-backend/CHANGELOG.md index 09455f37b0..8fc6adbfcb 100644 --- a/plugins/todo-backend/CHANGELOG.md +++ b/plugins/todo-backend/CHANGELOG.md @@ -1,5 +1,43 @@ # @backstage/plugin-todo-backend +## 0.1.30 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/backend-common@0.14.0 + - @backstage/integration@1.2.1 + - @backstage/catalog-client@1.0.3 + - @backstage/catalog-model@1.0.3 + +## 0.1.30-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.14.0-next.2 + - @backstage/integration@1.2.1-next.2 + +## 0.1.30-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/backend-common@0.13.6-next.1 + - @backstage/catalog-client@1.0.3-next.0 + - @backstage/integration@1.2.1-next.1 + - @backstage/catalog-model@1.0.3-next.0 + +## 0.1.30-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + - @backstage/integration@1.2.1-next.0 + ## 0.1.29 ### Patch Changes diff --git a/plugins/todo-backend/package.json b/plugins/todo-backend/package.json index ef510eea1f..235e05a93b 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.1.29", + "version": "0.1.30", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -29,12 +29,12 @@ "start": "backstage-cli package start" }, "dependencies": { - "@backstage/backend-common": "^0.13.3", - "@backstage/catalog-client": "^1.0.2", - "@backstage/catalog-model": "^1.0.2", + "@backstage/backend-common": "^0.14.0", + "@backstage/catalog-client": "^1.0.3", + "@backstage/catalog-model": "^1.0.3", "@backstage/config": "^1.0.1", "@backstage/errors": "^1.0.0", - "@backstage/integration": "^1.2.0", + "@backstage/integration": "^1.2.1", "@types/express": "^4.17.6", "express": "^4.17.1", "express-promise-router": "^4.1.0", @@ -43,9 +43,9 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", + "@backstage/cli": "^0.17.2", "@types/supertest": "^2.0.8", - "msw": "^0.35.0", + "msw": "^0.42.0", "supertest": "^6.1.3" }, "files": [ diff --git a/plugins/todo/CHANGELOG.md b/plugins/todo/CHANGELOG.md index 42ca99b4e4..77eda64a68 100644 --- a/plugins/todo/CHANGELOG.md +++ b/plugins/todo/CHANGELOG.md @@ -1,5 +1,35 @@ # @backstage/plugin-todo +## 0.2.8 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 + +## 0.2.8-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + +## 0.2.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + ## 0.2.7 ### Patch Changes diff --git a/plugins/todo/package.json b/plugins/todo/package.json index 1a605a0842..7fbf79cd3a 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.7", + "version": "0.2.8", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -30,11 +30,11 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-model": "^1.0.2", - "@backstage/core-components": "^0.9.4", - "@backstage/core-plugin-api": "^1.0.2", + "@backstage/catalog-model": "^1.0.3", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", "@backstage/errors": "^1.0.0", - "@backstage/plugin-catalog-react": "^1.1.0", + "@backstage/plugin-catalog-react": "^1.1.1", "@backstage/theme": "^0.2.15", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -45,17 +45,17 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/core-app-api": "^1.0.2", - "@backstage/dev-utils": "^1.0.2", - "@backstage/test-utils": "^1.1.0", + "@backstage/cli": "^0.17.2", + "@backstage/core-app-api": "^1.0.3", + "@backstage/dev-utils": "^1.0.3", + "@backstage/test-utils": "^1.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", "@types/jest": "^26.0.7", "@types/node": "^16.11.26", "cross-fetch": "^3.1.5", - "msw": "^0.35.0", + "msw": "^0.42.0", "react-router": "6.0.0-beta.0" }, "files": [ diff --git a/plugins/user-settings/CHANGELOG.md b/plugins/user-settings/CHANGELOG.md index 4f6ca582fd..81cc4a891f 100644 --- a/plugins/user-settings/CHANGELOG.md +++ b/plugins/user-settings/CHANGELOG.md @@ -1,5 +1,33 @@ # @backstage/plugin-user-settings +## 0.4.5 + +### Patch Changes + +- 9d2d6a0cea: Added new `` to show the result of the `identityApi.getBackstageIdentity()` call to help debug ownership issues. The new card has been added to the user settings page. +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- bff65e6958: Updated sidebar-related logic to use `` + `useSidebarPinState()` and/or `` + `useSidebarOpenState()` from `@backstage/core-components`. +- Updated dependencies + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + +## 0.4.5-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- bff65e6958: Updated sidebar-related logic to use `` + `useSidebarPinState()` and/or `` + `useSidebarOpenState()` from `@backstage/core-components`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + +## 0.4.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.9.5-next.0 + ## 0.4.4 ### Patch Changes diff --git a/plugins/user-settings/api-report.md b/plugins/user-settings/api-report.md index 136b23bd71..a38b37846a 100644 --- a/plugins/user-settings/api-report.md +++ b/plugins/user-settings/api-report.md @@ -7,6 +7,7 @@ import { ApiRef } from '@backstage/core-plugin-api'; import { BackstagePlugin } from '@backstage/core-plugin-api'; +import { BackstageUserIdentity } from '@backstage/core-plugin-api'; import { IconComponent } from '@backstage/core-plugin-api'; import { ProfileInfo } from '@backstage/core-plugin-api'; import { PropsWithChildren } from 'react'; @@ -70,6 +71,11 @@ export const UserSettingsFeatureFlags: () => JSX.Element; // @public (undocumented) export const UserSettingsGeneral: () => JSX.Element; +// Warning: (ae-missing-release-tag) "UserSettingsIdentityCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const UserSettingsIdentityCard: () => JSX.Element; + // Warning: (ae-missing-release-tag) "UserSettingsMenu" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -129,9 +135,17 @@ export const UserSettingsThemeToggle: () => JSX.Element; // Warning: (ae-missing-release-tag) "useUserProfile" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const useUserProfile: () => { - profile: ProfileInfo; - displayName: string; - loading: boolean; -}; +export const useUserProfile: () => + | { + profile: ProfileInfo; + displayName: string; + loading: boolean; + backstageIdentity?: undefined; + } + | { + profile: ProfileInfo; + backstageIdentity: BackstageUserIdentity; + displayName: string; + loading: false; + }; ``` diff --git a/plugins/user-settings/package.json b/plugins/user-settings/package.json index adb9e1850b..aa4335856e 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.4.4", + "version": "0.4.5", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -34,8 +34,8 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/core-components": "^0.9.4", - "@backstage/core-plugin-api": "^1.0.2", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", "@backstage/theme": "^0.2.15", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -48,17 +48,17 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/core-app-api": "^1.0.2", - "@backstage/dev-utils": "^1.0.2", - "@backstage/test-utils": "^1.1.0", + "@backstage/cli": "^0.17.2", + "@backstage/core-app-api": "^1.0.3", + "@backstage/dev-utils": "^1.0.3", + "@backstage/test-utils": "^1.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", "@types/jest": "^26.0.7", "@types/node": "^16.11.26", "cross-fetch": "^3.1.5", - "msw": "^0.35.0" + "msw": "^0.42.0" }, "files": [ "dist" diff --git a/plugins/user-settings/src/components/General/UserSettingsAppearanceCard.tsx b/plugins/user-settings/src/components/General/UserSettingsAppearanceCard.tsx index 15ab93c6d0..cd55218db5 100644 --- a/plugins/user-settings/src/components/General/UserSettingsAppearanceCard.tsx +++ b/plugins/user-settings/src/components/General/UserSettingsAppearanceCard.tsx @@ -13,14 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { InfoCard, SidebarPinStateContext } from '@backstage/core-components'; +import { InfoCard, useSidebarPinState } from '@backstage/core-components'; import { List } from '@material-ui/core'; -import React, { useContext } from 'react'; +import React from 'react'; import { UserSettingsPinToggle } from './UserSettingsPinToggle'; import { UserSettingsThemeToggle } from './UserSettingsThemeToggle'; export const UserSettingsAppearanceCard = () => { - const { isMobile } = useContext(SidebarPinStateContext); + const { isMobile } = useSidebarPinState(); return ( diff --git a/plugins/user-settings/src/components/General/UserSettingsGeneral.tsx b/plugins/user-settings/src/components/General/UserSettingsGeneral.tsx index 6705997d1c..db7fdd7be6 100644 --- a/plugins/user-settings/src/components/General/UserSettingsGeneral.tsx +++ b/plugins/user-settings/src/components/General/UserSettingsGeneral.tsx @@ -17,6 +17,7 @@ import { Grid } from '@material-ui/core'; import React from 'react'; import { UserSettingsProfileCard } from './UserSettingsProfileCard'; import { UserSettingsAppearanceCard } from './UserSettingsAppearanceCard'; +import { UserSettingsIdentityCard } from './UserSettingsIdentityCard'; export const UserSettingsGeneral = () => { return ( @@ -27,6 +28,9 @@ export const UserSettingsGeneral = () => { + + +
); }; diff --git a/plugins/user-settings/src/components/General/UserSettingsIdentityCard.test.tsx b/plugins/user-settings/src/components/General/UserSettingsIdentityCard.test.tsx new file mode 100644 index 0000000000..19c3eb2182 --- /dev/null +++ b/plugins/user-settings/src/components/General/UserSettingsIdentityCard.test.tsx @@ -0,0 +1,53 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + renderWithEffects, + wrapInTestApp, + TestApiRegistry, +} from '@backstage/test-utils'; +import { screen } from '@testing-library/react'; +import React from 'react'; +import { UserSettingsIdentityCard } from './UserSettingsIdentityCard'; +import { ApiProvider } from '@backstage/core-app-api'; +import { identityApiRef } from '@backstage/core-plugin-api'; + +const apiRegistry = TestApiRegistry.from([ + identityApiRef, + { + getProfileInfo: jest.fn(async () => ({})), + getBackstageIdentity: jest.fn(async () => ({ + type: 'user' as const, + userEntityRef: 'foo:bar/foobar', + ownershipEntityRefs: ['test-ownership'], + })), + }, +]); + +describe('', () => { + it('displays an identity card', async () => { + await renderWithEffects( + wrapInTestApp( + + + , + ), + ); + + expect(screen.getByText('test-ownership')).toBeInTheDocument(); + expect(screen.getByText('foo:bar/foobar')).toBeInTheDocument(); + }); +}); diff --git a/plugins/user-settings/src/components/General/UserSettingsIdentityCard.tsx b/plugins/user-settings/src/components/General/UserSettingsIdentityCard.tsx new file mode 100644 index 0000000000..908cda1423 --- /dev/null +++ b/plugins/user-settings/src/components/General/UserSettingsIdentityCard.tsx @@ -0,0 +1,52 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { InfoCard } from '@backstage/core-components'; +import React from 'react'; +import { useUserProfile } from '../useUserProfileInfo'; +import Chip from '@material-ui/core/Chip'; +import Grid from '@material-ui/core/Grid'; +import Typography from '@material-ui/core/Typography'; + +export const UserSettingsIdentityCard = () => { + const { backstageIdentity } = useUserProfile(); + + return ( + + + + + + + User Entity:{' '} + + + + Ownership Entities:{' '} + {backstageIdentity?.ownershipEntityRefs.map(it => ( + + ))} + + + + + + + ); +}; diff --git a/plugins/user-settings/src/components/General/UserSettingsPinToggle.test.tsx b/plugins/user-settings/src/components/General/UserSettingsPinToggle.test.tsx index b63ac85b10..ef8046a54e 100644 --- a/plugins/user-settings/src/components/General/UserSettingsPinToggle.test.tsx +++ b/plugins/user-settings/src/components/General/UserSettingsPinToggle.test.tsx @@ -18,14 +18,14 @@ import { renderWithEffects, wrapInTestApp } from '@backstage/test-utils'; import { fireEvent } from '@testing-library/react'; import React from 'react'; import { UserSettingsPinToggle } from './UserSettingsPinToggle'; -import { SidebarPinStateContext } from '@backstage/core-components'; +import { SidebarPinStateProvider } from '@backstage/core-components'; describe('', () => { it('toggles the pin sidebar button', async () => { const mockToggleFn = jest.fn(); const rendered = await renderWithEffects( wrapInTestApp( - ', () => { }} > - , +
, ), ); expect(rendered.getByText('Pin Sidebar')).toBeInTheDocument(); diff --git a/plugins/user-settings/src/components/General/UserSettingsPinToggle.tsx b/plugins/user-settings/src/components/General/UserSettingsPinToggle.tsx index 4d71df8113..d218787c05 100644 --- a/plugins/user-settings/src/components/General/UserSettingsPinToggle.tsx +++ b/plugins/user-settings/src/components/General/UserSettingsPinToggle.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import React, { useContext } from 'react'; +import React from 'react'; import { ListItem, ListItemSecondaryAction, @@ -22,12 +22,10 @@ import { Switch, Tooltip, } from '@material-ui/core'; -import { SidebarPinStateContext } from '@backstage/core-components'; +import { useSidebarPinState } from '@backstage/core-components'; export const UserSettingsPinToggle = () => { - const { isPinned, toggleSidebarPinState } = useContext( - SidebarPinStateContext, - ); + const { isPinned, toggleSidebarPinState } = useSidebarPinState(); return ( diff --git a/plugins/user-settings/src/components/General/index.ts b/plugins/user-settings/src/components/General/index.ts index 80a9c75d89..ff4d1210c4 100644 --- a/plugins/user-settings/src/components/General/index.ts +++ b/plugins/user-settings/src/components/General/index.ts @@ -21,3 +21,4 @@ export { UserSettingsSignInAvatar } from './UserSettingsSignInAvatar'; export { UserSettingsAppearanceCard } from './UserSettingsAppearanceCard'; export { UserSettingsThemeToggle } from './UserSettingsThemeToggle'; export { UserSettingsPinToggle } from './UserSettingsPinToggle'; +export { UserSettingsIdentityCard } from './UserSettingsIdentityCard'; diff --git a/plugins/user-settings/src/components/SettingsPage.tsx b/plugins/user-settings/src/components/SettingsPage.tsx index 8942235fda..dcaf62d435 100644 --- a/plugins/user-settings/src/components/SettingsPage.tsx +++ b/plugins/user-settings/src/components/SettingsPage.tsx @@ -17,10 +17,10 @@ import { Header, Page, - SidebarPinStateContext, TabbedLayout, + useSidebarPinState, } from '@backstage/core-components'; -import React, { useContext } from 'react'; +import React from 'react'; import { useOutlet } from 'react-router'; import { useElementFilter } from '@backstage/core-plugin-api'; import { UserSettingsAuthProviders } from './AuthProviders'; @@ -33,7 +33,7 @@ type Props = { }; export const SettingsPage = ({ providerSettings }: Props) => { - const { isMobile } = useContext(SidebarPinStateContext); + const { isMobile } = useSidebarPinState(); const outlet = useOutlet(); const tabs = useElementFilter(outlet, elements => diff --git a/plugins/user-settings/src/components/useUserProfileInfo.ts b/plugins/user-settings/src/components/useUserProfileInfo.ts index f893520cd2..c168fc12b4 100644 --- a/plugins/user-settings/src/components/useUserProfileInfo.ts +++ b/plugins/user-settings/src/components/useUserProfileInfo.ts @@ -53,6 +53,7 @@ export const useUserProfile = () => { return { profile: value!.profile, + backstageIdentity: value!.identity, displayName: value!.profile.displayName ?? value!.identity.userEntityRef, loading, }; diff --git a/plugins/vault-backend/.eslintrc.js b/plugins/vault-backend/.eslintrc.js new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/plugins/vault-backend/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/vault-backend/CHANGELOG.md b/plugins/vault-backend/CHANGELOG.md new file mode 100644 index 0000000000..302b27afa8 --- /dev/null +++ b/plugins/vault-backend/CHANGELOG.md @@ -0,0 +1,14 @@ +# @backstage/plugin-vault-backend + +## 0.1.0 + +### Minor Changes + +- 7c310a5bc2: First implementation for the backend vault plugin. For more information refer to its `README.md`. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-tasks@0.3.2 + - @backstage/backend-common@0.14.0 + - @backstage/backend-test-utils@0.1.25 diff --git a/plugins/vault-backend/README.md b/plugins/vault-backend/README.md new file mode 100644 index 0000000000..d7d227607d --- /dev/null +++ b/plugins/vault-backend/README.md @@ -0,0 +1,144 @@ +# @backstage/plugin-vault-backend + +A backend for [Vault](https://www.vaultproject.io/), this plugin adds a few routes that are used by the frontend plugin to fetch the information from Vault. + +## Introduction + +Vault is an identity-based secrets and encryption management system. A secret is anything that you want to tightly control access to, such as API encryption keys, passwords, or certificates. Vault provides encryption services that are gated by authentication and authorization methods. + +This plugins allows you to view all the available secrets at a certain location, and redirect you to the official UI so backstage can rely on LIST permissions, which is safer. + +## Getting started + +To get started, first you need a running instance of Vault. You can follow [this tutorial](https://learn.hashicorp.com/tutorials/vault/getting-started-intro?in=vault/getting-started) to install vault and start your server locally. + +1. When your Vault instance is up and running, then you will need to install the plugin into your app: + + ```bash + # From your Backstage root directory + yarn add --cwd packages/backend @backstage/plugin-vault-backend + ``` + +2. Create a file in `src/plugins/vault.ts` and add a reference to it in `src/index.ts`: + + ```typescript + // In packages/backend/src/plugins/vault.ts + import { createRouter } from '@backstage/plugin-vault-backend'; + import { Router } from 'express'; + import { PluginEnvironment } from '../types'; + + export default async function createPlugin( + env: PluginEnvironment, + ): Promise { + return await createRouter({ + logger: env.logger, + config: env.config, + scheduler: env.scheduler, + }); + } + ``` + + ```diff + diff --git a/packages/backend/src/index.ts b/packages/backend/src/index.ts + index f2b14b2..2c64f47 100644 + --- a/packages/backend/src/index.ts + +++ b/packages/backend/src/index.ts + @@ -22,6 +22,7 @@ import { Config } from '@backstage/config'; + import app from './plugins/app'; + +import vault from './plugins/vault'; + import scaffolder from './plugins/scaffolder'; + @@ -56,6 +57,7 @@ async function main() { + const authEnv = useHotMemoize(module, () => createEnv('auth')); + + const vaultEnv = useHotMemoize(module, () => createEnv('vault')); + const proxyEnv = useHotMemoize(module, () => createEnv('proxy')); + @@ -63,6 +65,7 @@ async function main() { + + const apiRouter = Router(); + apiRouter.use('/catalog', await catalog(catalogEnv)); + + apiRouter.use('/vault', await vault(vaultEnv)); + apiRouter.use('/scaffolder', await scaffolder(scaffolderEnv)); + ``` + +3. Add some extra configurations in your [`app-config.yaml`](https://github.com/backstage/backstage/blob/master/app-config.yaml). + + ```yaml + vault: + baseUrl: http://your-vault-url + token: + secretEngine: 'customSecretEngine' # Optional. By default it uses 'secrets' + kvVersion: # Optional. The K/V version that your instance is using. The available options are '1' or '2' + ``` + +4. Get a `VAULT_TOKEN` with **LIST** permissions, as it's enough for the plugin. You can check [this tutorial](https://learn.hashicorp.com/tutorials/vault/tokens) for more info. + +5. If you also want to use the `renew` functionality, you need to attach the following block to your custom policy, so that Backstage can perform a token-renew: + ``` + # Allow tokens to renew themselves + path "auth/token/renew-self" { + capabilities = ["update"] + } + ``` + +## Integration with the Catalog + +The plugin can be integrated into each Component in the catalog. To allow listing the available secrets a new annotation must be added to the `catalog-info.yaml`: + +```yaml +apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + # ... + annotations: + vault.io/secrets-path: path/to/secrets +``` + +The path is relative to your secrets engine folder. So if you want to get the secrets for backstage and you have the following directory structure: + + . + โ”œโ”€โ”€ ... + โ”œโ”€โ”€ secrets # Your secret engine name (usually it is `secrets`) + โ”‚ โ”œโ”€โ”€ test # Folder with test secrets + โ”‚ โ”‚ โ”œโ”€โ”€ backstage # In this folder there are secrets for Backstage + โ”‚ โ”œโ”€โ”€ other # Other folder with more secrets inside + โ”‚ โ””โ”€โ”€ folder # And another folder + โ””โ”€โ”€ ... + +You will set the `vault.io/secret-path` to `test/backstage`. If the folder `backstage` contains other sub-folders, the plugin will fetch the secrets inside them and adapt the **View** and **Edit** URLs to point to the correct place. + +## Renew token + +In a secure Vault instance, it's usual that the tokens are refreshed after some time. In order to always have a valid token to fetch the secrets, it might be necessary to execute a renew action after some time. By default this is deactivated, but it can be easily activated and configured to be executed periodically (hourly by default, but customizable by the user). In order to do that, modify your `src/plugins/vault.ts` file to look like this one: + +```typescript +import { VaultBuilder } from '@backstage/plugin-vault-backend'; +import { Router } from 'express'; +import { PluginEnvironment } from '../types'; + +export default async function createPlugin( + env: PluginEnvironment, +): Promise { + const builder = await VaultBuilder.createBuilder({ + logger: env.logger, + config: env.config, + scheduler: env.scheduler, + }).enableTokenRenew( + env.scheduler.createScheduledTaskRunner({ + frequency: { minutes: 10 }, + timeout: { minutes: 1 }, + }), + ); + + const { router } = builder.build(); + + return router; +} +``` + +## Features + +- List the secrets present in a certain path +- Open a link to view the secret +- Open a link to edit the secret +- Renew the token automatically with a defined periodicity + +The secrets cannot be edited/viewed from within Backstage to make it more secure. Backstage will only have permissions to LIST data from Vault or to renew its own token if that is needed. And the user who wants to edit/view a certain secret needs the correct permissions to do so. diff --git a/plugins/vault-backend/api-report.md b/plugins/vault-backend/api-report.md new file mode 100644 index 0000000000..9f738b15e9 --- /dev/null +++ b/plugins/vault-backend/api-report.md @@ -0,0 +1,78 @@ +## API Report File for "@backstage/plugin-vault-backend" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import { Config } from '@backstage/config'; +import express from 'express'; +import { Logger } from 'winston'; +import { PluginTaskScheduler } from '@backstage/backend-tasks'; +import { TaskRunner } from '@backstage/backend-tasks'; + +// @public +export function createRouter(options: RouterOptions): express.Router; + +// @public +export interface RouterOptions { + // (undocumented) + config: Config; + // (undocumented) + logger: Logger; + // (undocumented) + scheduler: PluginTaskScheduler; +} + +// @public +export interface VaultApi { + getFrontendSecretsUrl(): string; + listSecrets(secretPath: string): Promise; + renewToken?(): Promise; +} + +// @public +export class VaultBuilder { + constructor(env: VaultEnvironment); + build(): VaultBuilderReturn; + protected buildRouter(vaultClient: VaultClient): express.Router; + static createBuilder(env: VaultEnvironment): VaultBuilder; + enableTokenRenew(schedule?: TaskRunner): Promise; + // (undocumented) + protected readonly env: VaultEnvironment; + setVaultClient(vaultClient: VaultClient): this; +} + +// @public +export type VaultBuilderReturn = { + router: express.Router; +}; + +// @public +export class VaultClient implements VaultApi { + constructor({ config }: { config: Config }); + // (undocumented) + getFrontendSecretsUrl(): string; + // (undocumented) + listSecrets(secretPath: string): Promise; + // (undocumented) + renewToken(): Promise; +} + +// @public +export interface VaultEnvironment { + // (undocumented) + config: Config; + // (undocumented) + logger: Logger; + // (undocumented) + scheduler: PluginTaskScheduler; +} + +// @public +export type VaultSecret = { + name: string; + showUrl: string; + editUrl: string; +}; + +// (No @packageDocumentation comment for this package) +``` diff --git a/plugins/vault-backend/config.d.ts b/plugins/vault-backend/config.d.ts new file mode 100644 index 0000000000..c7aac4db9d --- /dev/null +++ b/plugins/vault-backend/config.d.ts @@ -0,0 +1,42 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** Configuration for the Vault plugin */ +export interface Config { + vault?: { + /** + * The baseUrl for your Vault instance. + * @visibility frontend + */ + baseUrl: string; + + /** + * The token used by Backstage to access Vault. + * @visibility secret + */ + token: string; + + /** + * The secret engine name where in vault. Defaults to `secrets`. + */ + secretEngine?: string; + + /** + * The version of the K/V API. Defaults to `2`. + */ + kvVersion?: 1 | 2; + }; +} diff --git a/plugins/vault-backend/package.json b/plugins/vault-backend/package.json new file mode 100644 index 0000000000..d0e3990aee --- /dev/null +++ b/plugins/vault-backend/package.json @@ -0,0 +1,65 @@ +{ + "name": "@backstage/plugin-vault-backend", + "description": "A Backstage backend plugin that integrates towards Vault", + "version": "0.1.0", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "private": false, + "publishConfig": { + "access": "public", + "main": "dist/index.cjs.js", + "types": "dist/index.d.ts" + }, + "backstage": { + "role": "backend-plugin" + }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/vault" + }, + "keywords": [ + "backstage", + "vault" + ], + "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": "^0.14.0", + "@backstage/backend-tasks": "^0.3.2", + "@backstage/backend-test-utils": "^0.1.25", + "@backstage/config": "^1.0.1", + "@backstage/errors": "^1.0.0", + "@types/express": "*", + "compression": "^1.7.4", + "cors": "^2.8.5", + "cross-fetch": "^3.1.5", + "express": "^4.17.1", + "express-promise-router": "^4.1.0", + "helmet": "^5.0.2", + "p-limit": "^3.1.0", + "winston": "^3.7.2", + "yn": "^5.0.0" + }, + "devDependencies": { + "@backstage/cli": "^0.17.2", + "@types/compression": "^1.7.2", + "@types/supertest": "^2.0.8", + "msw": "^0.42.0", + "supertest": "^6.1.6" + }, + "files": [ + "dist", + "config.d.ts" + ], + "configSchema": "config.d.ts" +} diff --git a/plugins/vault-backend/src/config/config.test.ts b/plugins/vault-backend/src/config/config.test.ts new file mode 100644 index 0000000000..7cfdd8f468 --- /dev/null +++ b/plugins/vault-backend/src/config/config.test.ts @@ -0,0 +1,67 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ConfigReader } from '@backstage/config'; +import { getVaultConfig } from './config'; + +describe('GetVaultConfig', () => { + it('fails by missing keys', () => { + expect(() => getVaultConfig(new ConfigReader({}))).toThrow(); + expect(() => + getVaultConfig( + new ConfigReader({ + vault: {}, + }), + ), + ).toThrow(); + }); + + it('loads default params', () => { + const config = new ConfigReader({ + vault: { + baseUrl: 'http://www.example.com', + token: '123', + }, + }); + + const vaultConfig = getVaultConfig(config); + expect(vaultConfig).toStrictEqual({ + baseUrl: 'http://www.example.com', + token: '123', + kvVersion: 2, + secretEngine: 'secrets', + }); + }); + + it('loads custom params', () => { + const config = new ConfigReader({ + vault: { + baseUrl: 'http://www.example.com', + token: '123', + kvVersion: 1, + secretEngine: 'test', + }, + }); + + const vaultConfig = getVaultConfig(config); + expect(vaultConfig).toStrictEqual({ + baseUrl: 'http://www.example.com', + token: '123', + kvVersion: 1, + secretEngine: 'test', + }); + }); +}); diff --git a/plugins/vault-backend/src/config/config.ts b/plugins/vault-backend/src/config/config.ts new file mode 100644 index 0000000000..4920c73fed --- /dev/null +++ b/plugins/vault-backend/src/config/config.ts @@ -0,0 +1,60 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Config } from '@backstage/config'; + +/** + * The configuration needed for the vault-backend plugin + * + * @public + */ +export interface VaultConfig { + /** + * The baseUrl for your Vault instance. + */ + baseUrl: string; + + /** + * The token used by Backstage to access Vault. + */ + token: string; + + /** + * The secret engine name where in vault. Defaults to `secrets`. + */ + secretEngine: string; + + /** + * The version of the K/V API. Defaults to `2`. + */ + kvVersion: number; +} + +/** + * Extract the Vault config from a config object + * + * @public + * + * @param config - The config object to extract from + */ +export function getVaultConfig(config: Config): VaultConfig { + return { + baseUrl: config.getString('vault.baseUrl'), + token: config.getString('vault.token'), + kvVersion: config.getOptionalNumber('vault.kvVersion') ?? 2, + secretEngine: config.getOptionalString('vault.secretEngine') ?? 'secrets', + }; +} diff --git a/plugins/vault-backend/src/config/index.ts b/plugins/vault-backend/src/config/index.ts new file mode 100644 index 0000000000..a7eab2d9f0 --- /dev/null +++ b/plugins/vault-backend/src/config/index.ts @@ -0,0 +1,32 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export * from './config'; diff --git a/plugins/vault-backend/src/index.ts b/plugins/vault-backend/src/index.ts new file mode 100644 index 0000000000..4c74fd051f --- /dev/null +++ b/plugins/vault-backend/src/index.ts @@ -0,0 +1,19 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export * from './service/router'; +export * from './service/VaultBuilder'; +export * from './service/vaultApi'; diff --git a/plugins/vault-backend/src/run.ts b/plugins/vault-backend/src/run.ts new file mode 100644 index 0000000000..0a3ed2b7f0 --- /dev/null +++ b/plugins/vault-backend/src/run.ts @@ -0,0 +1,33 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { getRootLogger } from '@backstage/backend-common'; +import yn from 'yn'; +import { startStandaloneServer } from './service/standaloneServer'; + +const port = process.env.PLUGIN_PORT ? Number(process.env.PLUGIN_PORT) : 7007; +const enableCors = yn(process.env.PLUGIN_CORS, { default: false }); +const logger = getRootLogger(); + +startStandaloneServer({ port, enableCors, logger }).catch(err => { + logger.error(err); + process.exit(1); +}); + +process.on('SIGINT', () => { + logger.info('CTRL+C pressed; exiting.'); + process.exit(0); +}); diff --git a/plugins/vault-backend/src/service/VaultBuilder.tsx b/plugins/vault-backend/src/service/VaultBuilder.tsx new file mode 100644 index 0000000000..67260f45ca --- /dev/null +++ b/plugins/vault-backend/src/service/VaultBuilder.tsx @@ -0,0 +1,150 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Config } from '@backstage/config'; +import { InputError } from '@backstage/errors'; +import { Logger } from 'winston'; +import express, { Router } from 'express'; +import { VaultClient } from './vaultApi'; +import { TaskRunner, PluginTaskScheduler } from '@backstage/backend-tasks'; + +/** + * Environment values needed by the VaultBuilder + * @public + */ +export interface VaultEnvironment { + logger: Logger; + config: Config; + scheduler: PluginTaskScheduler; +} + +/** + * The object returned by the VaultBuilder.build() function + * @public + */ +export type VaultBuilderReturn = { + router: express.Router; +}; + +/** + * Implementation for Vault. It creates the routing and initializes the backend + * to allow the use of the Vault's frontend plugin. + * @public + */ +export class VaultBuilder { + private vaultClient?: VaultClient; + + /** + * Creates a new instance of the VaultBuilder. + * + * @param env - The backstage environment + * @returns A new instance of the VaultBuilder + */ + static createBuilder(env: VaultEnvironment) { + return new VaultBuilder(env); + } + constructor(protected readonly env: VaultEnvironment) {} + + /** + * Builds the routes for Vault. + * + * @returns The router configured for Vault + */ + public build(): VaultBuilderReturn { + const { logger, config } = this.env; + + logger.info('Initializing Vault backend'); + + if (!config.has('vault')) { + logger.warn( + 'Failed to initialize Vault backend: vault config is missing', + ); + return { + router: Router(), + }; + } + + this.vaultClient = this.vaultClient ?? new VaultClient(this.env); + + const router = this.buildRouter(this.vaultClient); + + return { + router: router, + }; + } + + /** + * Overwrites the current vault client. + * + * @param vaultClient - The new Vault client + * @returns + */ + public setVaultClient(vaultClient: VaultClient) { + this.vaultClient = vaultClient; + return this; + } + + /** + * Enables the token renewal for Vault. + * + * @param schedule - The TaskRunner used to schedule the renewal, if not set, renewing hourly + * @returns + */ + public async enableTokenRenew(schedule?: TaskRunner) { + const taskRunner = schedule + ? schedule + : this.env.scheduler.createScheduledTaskRunner({ + frequency: { hours: 1 }, + timeout: { hours: 1 }, + }); + await taskRunner.run({ + id: 'refresh-vault-token', + fn: async () => { + this.env.logger.info('Renewing Vault token'); + const vaultClient = this.vaultClient ?? new VaultClient(this.env); + await vaultClient.renewToken(); + }, + }); + return this; + } + + /** + * Builds the backend routes for Vault. + * + * @param vaultClient - The Vault client used to list the secrets. + * @returns The generated backend router + */ + protected buildRouter(vaultClient: VaultClient): express.Router { + const router = Router(); + router.use(express.json()); + + router.get('/health', (_, res) => { + res.json({ status: 'ok' }); + }); + + router.get('/v1/secrets/:path', async (req, res) => { + const { path } = req.params; + if (typeof path !== 'string') { + throw new InputError(`Invalid path: ${path}`); + } + + const secrets = await vaultClient.listSecrets(path); + res.json({ items: secrets }); + }); + + return router; + } +} diff --git a/plugins/vault-backend/src/service/router.test.ts b/plugins/vault-backend/src/service/router.test.ts new file mode 100644 index 0000000000..97997b30ef --- /dev/null +++ b/plugins/vault-backend/src/service/router.test.ts @@ -0,0 +1,67 @@ +/* + * 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 { getVoidLogger, DatabaseManager } from '@backstage/backend-common'; +import { ConfigReader } from '@backstage/config'; +import express from 'express'; +import request from 'supertest'; +import { TestDatabases } from '@backstage/backend-test-utils'; +import { TaskScheduler } from '@backstage/backend-tasks'; + +import { createRouter } from './router'; + +describe('createRouter', () => { + let app: express.Express; + const databases = TestDatabases.create({ + ids: ['POSTGRES_13', 'POSTGRES_9', 'SQLITE_3'], + }); + + beforeAll(async () => { + const knex = await databases.init('SQLITE_3'); + const databaseManager: Partial = { + forPlugin: (_: string) => ({ + getClient: async () => knex, + }), + }; + const manager = databaseManager as DatabaseManager; + const scheduler = new TaskScheduler(manager, getVoidLogger()); + + const router = createRouter({ + logger: getVoidLogger(), + config: new ConfigReader({ + vault: { + baseUrl: 'https://www.example.com', + token: '1234567890', + }, + }), + scheduler: scheduler.forPlugin('vault'), + }); + app = express().use(router); + }); + + beforeEach(() => { + jest.resetAllMocks(); + }); + + describe('GET /health', () => { + it('returns ok', async () => { + const response = await request(app).get('/health'); + + expect(response.status).toEqual(200); + expect(response.body).toEqual({ status: 'ok' }); + }); + }); +}); diff --git a/plugins/vault-backend/src/service/router.ts b/plugins/vault-backend/src/service/router.ts new file mode 100644 index 0000000000..45e2ca8c86 --- /dev/null +++ b/plugins/vault-backend/src/service/router.ts @@ -0,0 +1,44 @@ +/* + * 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 { PluginTaskScheduler } from '@backstage/backend-tasks'; +import { Config } from '@backstage/config'; +import express from 'express'; +import { Logger } from 'winston'; +import { VaultBuilder } from './VaultBuilder'; + +/** + * Options for the router creation. + * @public + */ +export interface RouterOptions { + logger: Logger; + config: Config; + scheduler: PluginTaskScheduler; +} + +/** + * Creates the routes used for Vault. + * + * @param options - The options used for the Vault's router creation + * @returns The router for Vault + * @public + */ +export function createRouter(options: RouterOptions): express.Router { + const { router } = VaultBuilder.createBuilder(options).build(); + + return router; +} diff --git a/plugins/vault-backend/src/service/standaloneApplication.ts b/plugins/vault-backend/src/service/standaloneApplication.ts new file mode 100644 index 0000000000..bcec8ca419 --- /dev/null +++ b/plugins/vault-backend/src/service/standaloneApplication.ts @@ -0,0 +1,72 @@ +/* + * 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 { + errorHandler, + notFoundHandler, + requestLoggingHandler, + DatabaseManager, + getVoidLogger, +} from '@backstage/backend-common'; +import compression from 'compression'; +import cors from 'cors'; +import express from 'express'; +import helmet from 'helmet'; +import { Logger } from 'winston'; +import { createRouter } from './router'; +import { ConfigReader } from '@backstage/config'; +import { TestDatabases } from '@backstage/backend-test-utils'; +import { TaskScheduler } from '@backstage/backend-tasks'; + +export interface ApplicationOptions { + enableCors: boolean; + logger: Logger; +} + +export async function createStandaloneApplication( + options: ApplicationOptions, +): Promise { + const { enableCors, logger } = options; + const config = new ConfigReader({}); + const app = express(); + + const databases = TestDatabases.create({ + ids: ['POSTGRES_13', 'POSTGRES_9', 'SQLITE_3'], + }); + const knex = await databases.init('SQLITE_3'); + const databaseManager: Partial = { + forPlugin: (_: string) => ({ + getClient: async () => knex, + }), + }; + const manager = databaseManager as DatabaseManager; + const scheduler = new TaskScheduler(manager, getVoidLogger()).forPlugin( + 'vault', + ); + + app.use(helmet()); + if (enableCors) { + app.use(cors()); + } + app.use(compression()); + app.use(express.json()); + app.use(requestLoggingHandler()); + app.use('/', createRouter({ logger, config, scheduler })); + app.use(notFoundHandler()); + app.use(errorHandler()); + + return app; +} diff --git a/plugins/vault-backend/src/service/standaloneServer.ts b/plugins/vault-backend/src/service/standaloneServer.ts new file mode 100644 index 0000000000..8d42b1d4c8 --- /dev/null +++ b/plugins/vault-backend/src/service/standaloneServer.ts @@ -0,0 +1,50 @@ +/* + * 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 { Server } from 'http'; +import { Logger } from 'winston'; +import { createStandaloneApplication } from './standaloneApplication'; + +export interface ServerOptions { + port: number; + enableCors: boolean; + logger: Logger; +} + +export async function startStandaloneServer( + options: ServerOptions, +): Promise { + const logger = options.logger.child({ service: 'vault-backend' }); + + logger.debug('Creating application...'); + const app = await createStandaloneApplication({ + enableCors: options.enableCors, + logger, + }); + + logger.debug('Starting application server...'); + return await new Promise((resolve, reject) => { + const server = app.listen(options.port, (err?: Error) => { + if (err) { + reject(err); + return; + } + + logger.info(`Listening on port ${options.port}`); + resolve(server); + }); + }); +} diff --git a/plugins/vault-backend/src/service/vaultApi.test.ts b/plugins/vault-backend/src/service/vaultApi.test.ts new file mode 100644 index 0000000000..e80703c99f --- /dev/null +++ b/plugins/vault-backend/src/service/vaultApi.test.ts @@ -0,0 +1,104 @@ +/* + * 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 { setupRequestMockHandlers } from '@backstage/backend-test-utils'; +import { rest } from 'msw'; +import { setupServer } from 'msw/node'; +import { VaultSecret, VaultClient, VaultSecretList } from './vaultApi'; +import { ConfigReader } from '@backstage/config'; + +describe('VaultApi', () => { + const server = setupServer(); + setupRequestMockHandlers(server); + + const mockBaseUrl = 'https://api-vault.com'; + const config = new ConfigReader({ + vault: { + baseUrl: mockBaseUrl, + token: '1234567890', + }, + }); + + const mockListResult: VaultSecretList = { + data: { + keys: ['secret::one', 'secret::two'], + }, + }; + const mockListResultEmpty: VaultSecretList = { + data: { + keys: [], + }, + }; + + const mockSecretsResult: VaultSecret[] = [ + { + name: 'secret::one', + editUrl: `${mockBaseUrl}/ui/vault/secrets/secrets/edit/test/success/secret::one`, + showUrl: `${mockBaseUrl}/ui/vault/secrets/secrets/show/test/success/secret::one`, + }, + { + name: 'secret::two', + editUrl: `${mockBaseUrl}/ui/vault/secrets/secrets/edit/test/success/secret::two`, + showUrl: `${mockBaseUrl}/ui/vault/secrets/secrets/show/test/success/secret::two`, + }, + ]; + + const setupHandlers = () => { + server.use( + rest.get( + `${mockBaseUrl}/v1/secrets/metadata/test/success`, + (_, res, ctx) => { + return res(ctx.json(mockListResult)); + }, + ), + rest.get( + `${mockBaseUrl}/v1/secrets/metadata/test/error`, + (_, res, ctx) => { + return res(ctx.json(mockListResultEmpty)); + }, + ), + rest.post(`${mockBaseUrl}/v1/auth/token/renew-self`, (_, res, ctx) => { + return res(ctx.json({ auth: { client_token: '0987654321' } })); + }), + ); + }; + + it('should return secrets', async () => { + setupHandlers(); + const api = new VaultClient({ config }); + const secrets = await api.listSecrets('test/success'); + expect(secrets).toEqual(mockSecretsResult); + }); + + it('should return empty secret list', async () => { + setupHandlers(); + const api = new VaultClient({ config }); + const secrets = await api.listSecrets('test/error'); + expect(secrets).toEqual([]); + }); + + it('should return success token renew', async () => { + setupHandlers(); + const api = new VaultClient({ config }); + expect(await api.renewToken()).toBe(undefined); + }); + + it('should render frontend url', () => { + const api = new VaultClient({ config }); + const url = api.getFrontendSecretsUrl(); + expect(url).toEqual(`${mockBaseUrl}/ui/vault/secrets/secrets`); + }); +}); diff --git a/plugins/vault-backend/src/service/vaultApi.ts b/plugins/vault-backend/src/service/vaultApi.ts new file mode 100644 index 0000000000..14607a694f --- /dev/null +++ b/plugins/vault-backend/src/service/vaultApi.ts @@ -0,0 +1,156 @@ +/* + * 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 { Config } from '@backstage/config'; +import { NotFoundError } from '@backstage/errors'; +import fetch from 'cross-fetch'; +import plimit from 'p-limit'; +import { getVaultConfig, VaultConfig } from '../config'; + +/** + * Object received as a response from the Vault API when fetching secrets + * @internal + */ +export type VaultSecretList = { + data: { + keys: string[]; + }; +}; + +/** + * Object containing the secret name and some links + * @public + */ +export type VaultSecret = { + name: string; + showUrl: string; + editUrl: string; +}; + +/** + * Object received as response when the token is renewed using the Vault API + */ +type RenewTokenResponse = { + auth: { + client_token: string; + }; +}; + +/** + * Interface for the Vault API + * @public + */ +export interface VaultApi { + /** + * Returns the URL to acces the Vault UI with the defined config. + */ + getFrontendSecretsUrl(): string; + /** + * Returns a list of secrets used to show in a table. + * @param secretPath - The path where the secrets are stored in Vault + */ + listSecrets(secretPath: string): Promise; + /** + * Optional, to renew the token used to list the secrets. Throws an + * error if the token renewal went wrong. + */ + renewToken?(): Promise; +} + +/** + * Implementation of the Vault API to list secrets and renew the token if necessary + * @public + */ +export class VaultClient implements VaultApi { + private vaultConfig: VaultConfig; + private readonly limit = plimit(5); + + constructor({ config }: { config: Config }) { + this.vaultConfig = getVaultConfig(config); + } + + private async callApi( + path: string, + query: { [key in string]: any }, + method: string = 'GET', + ): Promise { + const url = new URL(path, this.vaultConfig.baseUrl); + const response = await fetch( + `${url.toString()}?${new URLSearchParams(query).toString()}`, + { + method, + headers: { + Accept: 'application/json', + 'X-Vault-Token': this.vaultConfig.token, + }, + }, + ); + if (response.ok) { + return (await response.json()) as T; + } else if (response.status === 404) { + throw new NotFoundError(`No secrets found in path '${path}'`); + } + throw new Error( + `Unexpected error while fetching secrets from path '${path}'`, + ); + } + + getFrontendSecretsUrl(): string { + return `${this.vaultConfig.baseUrl}/ui/vault/secrets/${this.vaultConfig.secretEngine}`; + } + + async listSecrets(secretPath: string): Promise { + const listUrl = + this.vaultConfig.kvVersion === 2 + ? `v1/${this.vaultConfig.secretEngine}/metadata/${secretPath}` + : `v1/${this.vaultConfig.secretEngine}/${secretPath}`; + const result = await this.limit(() => + this.callApi(listUrl, { list: true }), + ); + + const secrets: VaultSecret[] = []; + + await Promise.all( + result.data.keys.map(async secret => { + if (secret.endsWith('/')) { + secrets.push( + ...(await this.limit(() => + this.listSecrets(`${secretPath}/${secret.slice(0, -1)}`), + )), + ); + } else { + secrets.push({ + name: secret, + editUrl: `${this.vaultConfig.baseUrl}/ui/vault/secrets/${this.vaultConfig.secretEngine}/edit/${secretPath}/${secret}`, + showUrl: `${this.vaultConfig.baseUrl}/ui/vault/secrets/${this.vaultConfig.secretEngine}/show/${secretPath}/${secret}`, + }); + } + }), + ); + + return secrets; + } + + async renewToken(): Promise { + const result = await this.callApi( + 'v1/auth/token/renew-self', + {}, + 'POST', + ); + + this.vaultConfig.token = result.auth.client_token; + } +} diff --git a/packages/techdocs-common/src/index.ts b/plugins/vault-backend/src/setupTests.ts similarity index 92% rename from packages/techdocs-common/src/index.ts rename to plugins/vault-backend/src/setupTests.ts index a881eef5ea..d3232290a7 100644 --- a/packages/techdocs-common/src/index.ts +++ b/plugins/vault-backend/src/setupTests.ts @@ -14,4 +14,4 @@ * limitations under the License. */ -export * from '@backstage/plugin-techdocs-node'; +export {}; diff --git a/plugins/vault/.eslintrc.js b/plugins/vault/.eslintrc.js new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/plugins/vault/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/vault/CHANGELOG.md b/plugins/vault/CHANGELOG.md new file mode 100644 index 0000000000..205b537dd4 --- /dev/null +++ b/plugins/vault/CHANGELOG.md @@ -0,0 +1,15 @@ +# @backstage/plugin-vault + +## 0.1.0 + +### Minor Changes + +- 7c310a5bc2: First implementation of the frontend vault plugin. For more information refer to its `README.md`. + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + - @backstage/catalog-model@1.0.3 diff --git a/plugins/vault/README.md b/plugins/vault/README.md new file mode 100644 index 0000000000..824293632f --- /dev/null +++ b/plugins/vault/README.md @@ -0,0 +1,96 @@ +# @backstage/plugin-vault + +A frontend for [Vault](https://www.vaultproject.io/), this plugin allows you to display a list of secrets in a certain path inside your vault instance. There are also some useful links to edit and/or view them using the official UI. + +![Screenshot of the vault plugin table](images/vault-table.png) + +## Introduction + +Vault is an identity-based secrets and encryption management system. A secret is anything that you want to tightly control access to, such as API encryption keys, passwords, or certificates. Vault provides encryption services that are gated by authentication and authorization methods. + +This plugins allows you to view all the available secrets at a certain location, and redirect you to the official UI so backstage can rely on LIST permissions, which is safer. + +## Getting started + +To get started, first you need a running instance of Vault. You can follow [this tutorial](https://learn.hashicorp.com/tutorials/vault/getting-started-intro?in=vault/getting-started) to install vault and start your server locally. + +1. When your Vault instance is up and running, then you will need to install the plugin into your app: + + ```bash + # From your Backstage root directory + yarn add --cwd packages/app @backstage/plugin-vault + ``` + +2. Add the Vault card to the overview tab on the EntityPage: + + ```typescript + // In packages/app/src/components/catalog/EntityPage.tsx + import { EntityVaultCard } from '@backstage/plugin-vault'; + + const overviewContent = ( + + {/* ...other content */} + + + + ); + ``` + +3. Add some extra configurations in your [`app-config.yaml`](https://github.com/backstage/backstage/blob/master/app-config.yaml). + + ```yaml + vault: + baseUrl: http://your-vault-url + token: + secretEngine: 'customSecretEngine' # Optional. By default it uses 'secrets' + kvVersion: # Optional. The K/V version that your instance is using. The available options are '1' or '2' + ``` + +4. Get a `VAULT_TOKEN` with **LIST** permissions, as it's enough for the plugin. You can check [this tutorial](https://learn.hashicorp.com/tutorials/vault/tokens) for more info. + +5. If you also want to use the `renew` functionality, you need to attach the following block to your custom policy, so that Backstage can perform a token-renew: + ``` + # Allow tokens to renew themselves + path "auth/token/renew-self" { + capabilities = ["update"] + } + ``` + +## Integration with the Catalog + +The plugin can be integrated into each Component in the catalog. To allow listing the available secrets a new annotation must be added to the `catalog-info.yaml`: + +```yaml +apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + # ... + annotations: + vault.io/secrets-path: path/to/secrets +``` + +The path is relative to your secrets engine folder. So if you want to get the secrets for backstage and you have the following directory structure: + + . + โ”œโ”€โ”€ ... + โ”œโ”€โ”€ secrets # Your secret engine name (usually it is `secrets`) + โ”‚ โ”œโ”€โ”€ test # Folder with test secrets + โ”‚ โ”‚ โ”œโ”€โ”€ backstage # In this folder there are secrets for Backstage + โ”‚ โ”œโ”€โ”€ other # Other folder with more secrets inside + โ”‚ โ””โ”€โ”€ folder # And another folder + โ””โ”€โ”€ ... + +You will set the `vault.io/secret-path` to `test/backstage`. If the folder `backstage` contains other sub-folders, the plugin will fetch the secrets inside them and adapt the **View** and **Edit** URLs to point to the correct place. + +If the annotation is missing for a certain component, then the card will show some information to the user: + +![Screenshot of the vault plugin with missing annotation](images/annotation-missing.png) + +## Features + +- List the secrets present in a certain path +- Open a link to view the secret +- Open a link to edit the secret +- Renew the token automatically with a defined periodicity + +The secrets cannot be edited/viewed from within Backstage to make it more secure. Backstage will only have permissions to LIST data from Vault. And the user who wants to edit/view a certain secret needs the correct permissions to do so. diff --git a/plugins/vault/api-report.md b/plugins/vault/api-report.md new file mode 100644 index 0000000000..f6fa8d1269 --- /dev/null +++ b/plugins/vault/api-report.md @@ -0,0 +1,40 @@ +## API Report File for "@backstage/plugin-vault" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +/// + +import { ApiRef } from '@backstage/core-plugin-api'; +import { BackstagePlugin } from '@backstage/core-plugin-api'; +import { Entity } from '@backstage/catalog-model'; + +// @public +export const EntityVaultCard: () => JSX.Element; + +// @public +export function isVaultAvailable(entity: Entity): boolean; + +// @public (undocumented) +export const VAULT_SECRET_PATH_ANNOTATION = 'vault.io/secrets-path'; + +// @public +export interface VaultApi { + listSecrets(secretPath: string): Promise; +} + +// @public (undocumented) +export const vaultApiRef: ApiRef; + +// @public +export const vaultPlugin: BackstagePlugin<{}, {}>; + +// @public +export type VaultSecret = { + name: string; + showUrl: string; + editUrl: string; +}; + +// (No @packageDocumentation comment for this package) +``` diff --git a/plugins/vault/dev/index.tsx b/plugins/vault/dev/index.tsx new file mode 100644 index 0000000000..f5056cd057 --- /dev/null +++ b/plugins/vault/dev/index.tsx @@ -0,0 +1,84 @@ +/* + * 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 { Entity } from '@backstage/catalog-model'; +import { Content, Header, HeaderLabel, Page } from '@backstage/core-components'; +import { createDevApp } from '@backstage/dev-utils'; +import { EntityProvider } from '@backstage/plugin-catalog-react'; +import { TestApiProvider } from '@backstage/test-utils'; +import { Box, Typography } from '@material-ui/core'; +import SomeIcon from '@material-ui/icons/Storage'; +import React from 'react'; +import { VaultApi, vaultApiRef } from '../src/api'; +import { EntityVaultCard } from '../src/components/EntityVaultCard'; +import { EntityVaultTable } from '../src/components/EntityVaultTable'; +import { VAULT_SECRET_PATH_ANNOTATION } from '../src/constants'; +import { vaultPlugin } from '../src/plugin'; + +const entity: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'backstage', + annotations: { [VAULT_SECRET_PATH_ANNOTATION]: 'a/b' }, + }, + spec: { + type: 'service', + }, +}; + +const mockedApi: VaultApi = { + async listSecrets() { + return [ + { + name: 'a::b', + editUrl: 'https://example.com', + showUrl: 'https://example.com', + }, + { + name: 'c::d', + editUrl: 'https://example.com', + showUrl: 'https://example.com', + }, + ]; + }, +}; + +createDevApp() + .registerPlugin(vaultPlugin) + .addPage({ + element: ( + + + +
+ +
+ + As a card + + + As a table + + +
+
+
+ ), + title: 'Vault', + icon: SomeIcon, + }) + .render(); diff --git a/plugins/vault/images/annotation-missing.png b/plugins/vault/images/annotation-missing.png new file mode 100644 index 0000000000..1d0be0c99b Binary files /dev/null and b/plugins/vault/images/annotation-missing.png differ diff --git a/plugins/vault/images/vault-table.png b/plugins/vault/images/vault-table.png new file mode 100644 index 0000000000..18a4b22dc5 Binary files /dev/null and b/plugins/vault/images/vault-table.png differ diff --git a/plugins/vault/package.json b/plugins/vault/package.json new file mode 100644 index 0000000000..c4b447eaca --- /dev/null +++ b/plugins/vault/package.json @@ -0,0 +1,67 @@ +{ + "name": "@backstage/plugin-vault", + "description": "A Backstage plugin that integrates towards Vault", + "version": "0.1.0", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "private": false, + "publishConfig": { + "access": "public", + "main": "dist/index.esm.js", + "types": "dist/index.d.ts" + }, + "backstage": { + "role": "frontend-plugin" + }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/vault" + }, + "keywords": [ + "backstage", + "vault" + ], + "scripts": { + "start": "backstage-cli package start", + "build": "backstage-cli package build", + "lint": "backstage-cli package lint", + "test": "backstage-cli package test", + "clean": "backstage-cli package clean", + "prepack": "backstage-cli package prepack", + "postpack": "backstage-cli package postpack" + }, + "dependencies": { + "@backstage/catalog-model": "^1.0.3", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", + "@backstage/errors": "^1.0.0", + "@backstage/plugin-catalog-react": "^1.1.1", + "@backstage/theme": "^0.2.15", + "@material-ui/core": "^4.12.2", + "@material-ui/icons": "^4.9.1", + "@material-ui/lab": "^4.0.0-alpha.57", + "react-use": "^17.2.4" + }, + "peerDependencies": { + "react": "^16.13.1 || ^17.0.0" + }, + "devDependencies": { + "@backstage/cli": "^0.17.2", + "@backstage/core-app-api": "^1.0.3", + "@backstage/dev-utils": "^1.0.3", + "@backstage/test-utils": "^1.1.1", + "@testing-library/jest-dom": "^5.10.1", + "@testing-library/react": "^12.1.3", + "@testing-library/user-event": "^14.0.0", + "@types/jest": "*", + "@types/node": "*", + "cross-fetch": "^3.1.5", + "msw": "^0.42.0" + }, + "files": [ + "dist" + ] +} diff --git a/plugins/vault/src/api.test.ts b/plugins/vault/src/api.test.ts new file mode 100644 index 0000000000..3733d8be0f --- /dev/null +++ b/plugins/vault/src/api.test.ts @@ -0,0 +1,81 @@ +/* + * 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 { setupRequestMockHandlers } from '@backstage/test-utils'; +import { rest } from 'msw'; +import { setupServer } from 'msw/node'; +import { VaultSecret, VaultClient } from './api'; +import { UrlPatternDiscovery } from '@backstage/core-app-api'; + +describe('api', () => { + const server = setupServer(); + setupRequestMockHandlers(server); + + const mockBaseUrl = 'https://api-vault.com/api/vault'; + const discoveryApi = UrlPatternDiscovery.compile(mockBaseUrl); + + const mockSecretsResult: { items: VaultSecret[] } = { + items: [ + { + name: 'secret::one', + editUrl: `${mockBaseUrl}/ui/vault/secrets/secrets/edit/test/success/secret::one`, + showUrl: `${mockBaseUrl}/ui/vault/secrets/secrets/show/test/success/secret::one`, + }, + { + name: 'secret::two', + editUrl: `${mockBaseUrl}/ui/vault/secrets/secrets/edit/test/success/secret::two`, + showUrl: `${mockBaseUrl}/ui/vault/secrets/secrets/show/test/success/secret::two`, + }, + ], + }; + + const setupHandlers = () => { + server.use( + rest.get(`${mockBaseUrl}/v1/secrets/:path`, (req, res, ctx) => { + const { path } = req.params; + if (path === 'test/success') { + return res(ctx.json(mockSecretsResult)); + } else if (path === 'test/error') { + return res(ctx.json({ items: [] })); + } + return res(ctx.status(400)); + }), + rest.get(`${mockBaseUrl}/v1/secrets/`, (_req, res, ctx) => { + return res(ctx.json(mockSecretsResult)); + }), + ); + }; + + it('should return secrets', async () => { + setupHandlers(); + const api = new VaultClient({ discoveryApi }); + expect(await api.listSecrets('test/success')).toEqual( + mockSecretsResult.items, + ); + }); + + it('should return empty secret list', async () => { + setupHandlers(); + const api = new VaultClient({ discoveryApi }); + expect(await api.listSecrets('test/error')).toEqual([]); + }); + + it('should return all the secrets if no path defined', async () => { + setupHandlers(); + const api = new VaultClient({ discoveryApi }); + expect(await api.listSecrets('')).toEqual(mockSecretsResult.items); + }); +}); diff --git a/plugins/vault/src/api.ts b/plugins/vault/src/api.ts new file mode 100644 index 0000000000..6c17e34d9a --- /dev/null +++ b/plugins/vault/src/api.ts @@ -0,0 +1,89 @@ +/* + * 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 { DiscoveryApi, createApiRef } from '@backstage/core-plugin-api'; +import { NotFoundError } from '@backstage/errors'; + +/** + * @public + */ +export const vaultApiRef = createApiRef({ + id: 'plugin.vault.service', +}); + +/** + * Object containing the secret name and some links. + * @public + */ +export type VaultSecret = { + name: string; + showUrl: string; + editUrl: string; +}; + +/** + * Interface for the VaultApi. + * @public + */ +export interface VaultApi { + /** + * Returns a list of secrets used to show in a table. + * @param secretPath - The path where the secrets are stored in Vault + */ + listSecrets(secretPath: string): Promise; +} + +/** + * Default implementation of the VaultApi. + * @public + */ +export class VaultClient implements VaultApi { + private readonly discoveryApi: DiscoveryApi; + + constructor({ discoveryApi }: { discoveryApi: DiscoveryApi }) { + this.discoveryApi = discoveryApi; + } + + private async callApi( + path: string, + query: { [key in string]: any }, + ): Promise { + const apiUrl = `${await this.discoveryApi.getBaseUrl('vault')}`; + const response = await fetch( + `${apiUrl}/${path}?${new URLSearchParams(query).toString()}`, + { + headers: { + Accept: 'application/json', + }, + }, + ); + if (response.ok) { + return (await response.json()) as T; + } else if (response.status === 404) { + throw new NotFoundError(`No secrets found in path '${path}'`); + } + throw new Error( + `Unexpected error while fetching secrets from path '${path}'`, + ); + } + + async listSecrets(secretPath: string): Promise { + const result = await this.callApi<{ items: VaultSecret[] }>( + `v1/secrets/${encodeURIComponent(secretPath)}`, + {}, + ); + return result.items; + } +} diff --git a/plugins/vault/src/components/EntityVaultCard/EntityVaultCard.test.tsx b/plugins/vault/src/components/EntityVaultCard/EntityVaultCard.test.tsx new file mode 100644 index 0000000000..f46aa1e4c2 --- /dev/null +++ b/plugins/vault/src/components/EntityVaultCard/EntityVaultCard.test.tsx @@ -0,0 +1,52 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import { setupServer } from 'msw/node'; +import { setupRequestMockHandlers } from '@backstage/test-utils'; +import { ComponentEntity } from '@backstage/catalog-model'; +import { render } from '@testing-library/react'; +import { EntityVaultCard } from './EntityVaultCard'; +import { EntityProvider } from '@backstage/plugin-catalog-react'; + +describe('EntityVaultCard', () => { + const server = setupServer(); + setupRequestMockHandlers(server); + const entityAnnotationMissing: ComponentEntity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'test', + description: 'This is the description', + }, + spec: { + lifecycle: 'production', + owner: 'owner', + type: 'service', + }, + }; + + it('should render missing entity annotation', async () => { + const rendered = render( + + + , + ); + expect( + rendered.getByText(/Add the annotation to your component YAML/), + ).toBeInTheDocument(); + }); +}); diff --git a/plugins/vault/src/components/EntityVaultCard/EntityVaultCard.tsx b/plugins/vault/src/components/EntityVaultCard/EntityVaultCard.tsx new file mode 100644 index 0000000000..ae8e28cb01 --- /dev/null +++ b/plugins/vault/src/components/EntityVaultCard/EntityVaultCard.tsx @@ -0,0 +1,33 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import { useEntity } from '@backstage/plugin-catalog-react'; +import { isVaultAvailable } from '../../conditions'; +import { VAULT_SECRET_PATH_ANNOTATION } from '../../constants'; +import { EntityVaultTable } from '../EntityVaultTable'; +import { MissingAnnotationEmptyState } from '@backstage/core-components'; + +export const EntityVaultCard = () => { + const { entity } = useEntity(); + + if (isVaultAvailable(entity)) { + return ; + } + return ( + + ); +}; diff --git a/plugins/vault/src/components/EntityVaultCard/index.ts b/plugins/vault/src/components/EntityVaultCard/index.ts new file mode 100644 index 0000000000..0b91f6dde4 --- /dev/null +++ b/plugins/vault/src/components/EntityVaultCard/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export { EntityVaultCard } from './EntityVaultCard'; diff --git a/plugins/vault/src/components/EntityVaultTable/EntityVaultTable.test.tsx b/plugins/vault/src/components/EntityVaultTable/EntityVaultTable.test.tsx new file mode 100644 index 0000000000..f3d7748b3e --- /dev/null +++ b/plugins/vault/src/components/EntityVaultTable/EntityVaultTable.test.tsx @@ -0,0 +1,129 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import { setupServer } from 'msw/node'; +import { + setupRequestMockHandlers, + TestApiRegistry, +} from '@backstage/test-utils'; +import { ComponentEntity } from '@backstage/catalog-model'; +import { renderInTestApp } from '@backstage/test-utils'; +import { EntityVaultTable } from './EntityVaultTable'; +import { ApiProvider, UrlPatternDiscovery } from '@backstage/core-app-api'; +import { VaultSecret, vaultApiRef, VaultClient } from '../../api'; +import { rest } from 'msw'; + +describe('EntityVaultTable', () => { + const server = setupServer(); + setupRequestMockHandlers(server); + let apis: TestApiRegistry; + const mockBaseUrl = 'https://api-vault.com/api/vault'; + const discoveryApi = UrlPatternDiscovery.compile(mockBaseUrl); + + const entityOk: ComponentEntity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'test', + description: 'This is the description', + annotations: { + 'vault.io/secrets-path': 'test/success', + }, + }, + spec: { + lifecycle: 'production', + owner: 'owner', + type: 'service', + }, + }; + + const entityNotOk: ComponentEntity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'test', + description: 'This is the description', + annotations: { + 'vault.io/secrets-path': 'test/error', + }, + }, + spec: { + lifecycle: 'production', + owner: 'owner', + type: 'service', + }, + }; + + const mockSecretsResult: { items: VaultSecret[] } = { + items: [ + { + name: 'secret::one', + editUrl: `${mockBaseUrl}/ui/vault/secrets/secrets/edit/test/success/secret::one`, + showUrl: `${mockBaseUrl}/ui/vault/secrets/secrets/show/test/success/secret::one`, + }, + { + name: 'secret::two', + editUrl: `${mockBaseUrl}/ui/vault/secrets/secrets/edit/test/success/secret::two`, + showUrl: `${mockBaseUrl}/ui/vault/secrets/secrets/show/test/success/secret::two`, + }, + ], + }; + + const setupHandlers = () => { + server.use( + rest.get(`${mockBaseUrl}/v1/secrets/:path`, (req, res, ctx) => { + const { path } = req.params; + if (path === 'test/success') { + return res(ctx.json(mockSecretsResult)); + } else if (path === 'test/error') { + return res(ctx.json([])); + } + return res(ctx.status(400)); + }), + ); + }; + + beforeEach(() => { + apis = TestApiRegistry.from([ + vaultApiRef, + new VaultClient({ discoveryApi }), + ]); + }); + + it('should render secrets', async () => { + setupHandlers(); + const rendered = await renderInTestApp( + + + , + ); + + expect(await rendered.findAllByText(/secret::one/)).toBeDefined(); + expect(await rendered.findAllByText(/secret::two/)).toBeDefined(); + }); + + it('should render no secrets found', async () => { + setupHandlers(); + const rendered = await renderInTestApp( + + + , + ); + + expect(rendered.getByText(/No secrets found/)).toBeInTheDocument(); + }); +}); diff --git a/plugins/vault/src/components/EntityVaultTable/EntityVaultTable.tsx b/plugins/vault/src/components/EntityVaultTable/EntityVaultTable.tsx new file mode 100644 index 0000000000..8311dea2aa --- /dev/null +++ b/plugins/vault/src/components/EntityVaultTable/EntityVaultTable.tsx @@ -0,0 +1,105 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React from 'react'; +import { Entity } from '@backstage/catalog-model'; +import { Link, Table, TableColumn } from '@backstage/core-components'; +import { useApi } from '@backstage/core-plugin-api'; +import { Box, Typography } from '@material-ui/core'; +import Edit from '@material-ui/icons/Edit'; +import Visibility from '@material-ui/icons/Visibility'; +import Alert from '@material-ui/lab/Alert'; +import useAsync from 'react-use/lib/useAsync'; +import { VaultSecret, vaultApiRef } from '../../api'; +import { VAULT_SECRET_PATH_ANNOTATION } from '../../constants'; + +export const vaultSecretPath = (entity: Entity) => { + const secretPath = + entity.metadata.annotations?.[VAULT_SECRET_PATH_ANNOTATION]; + + return { secretPath }; +}; + +export const EntityVaultTable = ({ entity }: { entity: Entity }) => { + const vaultApi = useApi(vaultApiRef); + const { secretPath } = vaultSecretPath(entity); + if (!secretPath) { + throw Error( + `The secret path is undefined. Please, define the annotation ${VAULT_SECRET_PATH_ANNOTATION}`, + ); + } + const { value, loading, error } = useAsync(async (): Promise< + VaultSecret[] + > => { + return vaultApi.listSecrets(secretPath); + }, []); + + const columns: TableColumn[] = [ + { title: 'Secret', field: 'secret', highlight: true }, + { title: 'View URL', field: 'view', width: '10%' }, + { title: 'Edit URL', field: 'edit', width: '10%' }, + ]; + + const data = (value || []).map(secret => { + return { + secret: secret.name, + view: ( + + + + ), + edit: ( + + + + ), + }; + }); + + if (error) { + return {error.message}; + } + + return ( +
+ + No secrets found for {entity.metadata.name} in {secretPath} + + + } + /> + ); +}; diff --git a/plugins/vault/src/components/EntityVaultTable/index.tsx b/plugins/vault/src/components/EntityVaultTable/index.tsx new file mode 100644 index 0000000000..eeb8440b5c --- /dev/null +++ b/plugins/vault/src/components/EntityVaultTable/index.tsx @@ -0,0 +1,16 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export { EntityVaultTable } from './EntityVaultTable'; diff --git a/plugins/vault/src/conditions.ts b/plugins/vault/src/conditions.ts new file mode 100644 index 0000000000..091c7117c2 --- /dev/null +++ b/plugins/vault/src/conditions.ts @@ -0,0 +1,29 @@ +/* + * 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 { Entity } from '@backstage/catalog-model'; +import { VAULT_SECRET_PATH_ANNOTATION } from './constants'; + +/** + * Checks if an entity contains the annotation for Vault. + * @param entity - The entity to check if the annotation exists + * @returns If the annotation exists or not + * @public + */ +export function isVaultAvailable(entity: Entity): boolean { + return Boolean( + entity.metadata.annotations?.hasOwnProperty(VAULT_SECRET_PATH_ANNOTATION), + ); +} diff --git a/plugins/vault/src/constants.ts b/plugins/vault/src/constants.ts new file mode 100644 index 0000000000..0d99c261d4 --- /dev/null +++ b/plugins/vault/src/constants.ts @@ -0,0 +1,20 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @public + */ +export const VAULT_SECRET_PATH_ANNOTATION = 'vault.io/secrets-path'; diff --git a/plugins/vault/src/index.ts b/plugins/vault/src/index.ts new file mode 100644 index 0000000000..f69a787963 --- /dev/null +++ b/plugins/vault/src/index.ts @@ -0,0 +1,20 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export { vaultPlugin, EntityVaultCard } from './plugin'; +export { isVaultAvailable } from './conditions'; +export { VAULT_SECRET_PATH_ANNOTATION } from './constants'; +export { vaultApiRef } from './api'; +export type { VaultApi, VaultSecret } from './api'; diff --git a/plugins/vault/src/plugin.test.ts b/plugins/vault/src/plugin.test.ts new file mode 100644 index 0000000000..b6c5a9c9a0 --- /dev/null +++ b/plugins/vault/src/plugin.test.ts @@ -0,0 +1,22 @@ +/* + * 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 { vaultPlugin } from './plugin'; + +describe('vault', () => { + it('should export plugin', () => { + expect(vaultPlugin).toBeDefined(); + }); +}); diff --git a/plugins/vault/src/plugin.ts b/plugins/vault/src/plugin.ts new file mode 100644 index 0000000000..8275505abb --- /dev/null +++ b/plugins/vault/src/plugin.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 { + createApiFactory, + createComponentExtension, + createPlugin, + DiscoveryApi, + discoveryApiRef, +} from '@backstage/core-plugin-api'; + +import { VaultClient, vaultApiRef } from './api'; + +/** + * The vault plugin. + * @public + */ +export const vaultPlugin = createPlugin({ + id: 'vault', + apis: [ + createApiFactory({ + api: vaultApiRef, + deps: { discoveryApi: discoveryApiRef }, + factory: ({ discoveryApi }: { discoveryApi: DiscoveryApi }) => + new VaultClient({ + discoveryApi, + }), + }), + ], +}); + +/** + * Card used to show the list of Vault secrets. + * @public + */ +export const EntityVaultCard = vaultPlugin.provide( + createComponentExtension({ + name: 'EntityVaultCard', + component: { + lazy: () => + import('./components/EntityVaultCard').then(m => m.EntityVaultCard), + }, + }), +); diff --git a/plugins/vault/src/routes.ts b/plugins/vault/src/routes.ts new file mode 100644 index 0000000000..40743b524f --- /dev/null +++ b/plugins/vault/src/routes.ts @@ -0,0 +1,20 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { createRouteRef } from '@backstage/core-plugin-api'; + +export const rootRouteRef = createRouteRef({ + id: 'vault', +}); diff --git a/plugins/vault/src/setupTests.ts b/plugins/vault/src/setupTests.ts new file mode 100644 index 0000000000..b201a9c83e --- /dev/null +++ b/plugins/vault/src/setupTests.ts @@ -0,0 +1,17 @@ +/* + * 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 '@testing-library/jest-dom'; +import 'cross-fetch/polyfill'; diff --git a/plugins/xcmetrics/CHANGELOG.md b/plugins/xcmetrics/CHANGELOG.md index c631e9c286..01d99a138f 100644 --- a/plugins/xcmetrics/CHANGELOG.md +++ b/plugins/xcmetrics/CHANGELOG.md @@ -1,5 +1,30 @@ # @backstage/plugin-xcmetrics +## 0.2.26 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5 + - @backstage/core-plugin-api@1.0.3 + +## 0.2.26-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + +## 0.2.26-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.9.5-next.0 + ## 0.2.25 ### Patch Changes diff --git a/plugins/xcmetrics/package.json b/plugins/xcmetrics/package.json index 602493fcc8..1c93a9c8cb 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.25", + "version": "0.2.26", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -24,8 +24,8 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/core-components": "^0.9.4", - "@backstage/core-plugin-api": "^1.0.2", + "@backstage/core-components": "^0.9.5", + "@backstage/core-plugin-api": "^1.0.3", "@backstage/errors": "^1.0.0", "@backstage/theme": "^0.2.15", "@material-ui/core": "^4.12.2", @@ -40,10 +40,10 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.17.1", - "@backstage/core-app-api": "^1.0.2", - "@backstage/dev-utils": "^1.0.2", - "@backstage/test-utils": "^1.1.0", + "@backstage/cli": "^0.17.2", + "@backstage/core-app-api": "^1.0.3", + "@backstage/dev-utils": "^1.0.3", + "@backstage/test-utils": "^1.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", @@ -51,7 +51,7 @@ "@types/luxon": "^2.0.4", "@types/node": "^16.11.26", "cross-fetch": "^3.1.5", - "msw": "^0.35.0" + "msw": "^0.42.0" }, "files": [ "dist" diff --git a/scripts/api-extractor.ts b/scripts/api-extractor.ts index ae74848af6..2ca7aef17f 100644 --- a/scripts/api-extractor.ts +++ b/scripts/api-extractor.ts @@ -40,7 +40,7 @@ import { TSDocTagSyntaxKind, } from '@microsoft/tsdoc'; import { TSDocConfigFile } from '@microsoft/tsdoc-config'; -import { ApiPackage, ApiModel } from '@microsoft/api-extractor-model'; +import { ApiPackage, ApiModel, ApiItem } from '@microsoft/api-extractor-model'; import { IMarkdownDocumenterOptions, MarkdownDocumenter, @@ -209,69 +209,69 @@ const SKIPPED_PACKAGES = [ join('packages', 'techdocs-cli'), ]; -const NO_WARNING_PACKAGES = [ - 'packages/app-defaults', - 'packages/backend-common', - 'packages/backend-tasks', - 'packages/backend-test-utils', - 'packages/catalog-client', - 'packages/cli-common', - 'packages/config', - 'packages/config-loader', - 'packages/core-app-api', - 'packages/core-plugin-api', - 'packages/dev-utils', - 'packages/errors', - 'packages/integration', - 'packages/integration-react', - 'packages/search-common', - 'packages/techdocs-common', - 'packages/test-utils', - 'packages/theme', - 'packages/types', - 'packages/release-manifests', - 'packages/version-bridge', - 'plugins/auth-node', - 'plugins/catalog-backend', - 'plugins/catalog-backend-module-aws', - 'plugins/catalog-backend-module-azure', - 'plugins/catalog-backend-module-bitbucket', - 'plugins/catalog-backend-module-github', - 'plugins/catalog-backend-module-gitlab', - 'plugins/catalog-backend-module-ldap', - 'plugins/catalog-backend-module-msgraph', - 'plugins/catalog-common', - 'plugins/catalog-graph', - 'plugins/catalog-react', - 'plugins/codescene', - 'plugins/graphiql', - 'plugins/org', - 'plugins/periskop', - 'plugins/periskop-backend', - 'plugins/permission-backend', - 'plugins/permission-common', - 'plugins/permission-node', - 'plugins/permission-react', - 'plugins/scaffolder-backend-module-cookiecutter', - 'plugins/scaffolder-backend-module-rails', - 'plugins/scaffolder-backend-module-yeoman', - 'plugins/scaffolder-common', - 'plugins/search-backend-node', - 'plugins/search-common', - 'plugins/search-react', - 'plugins/techdocs', - 'plugins/techdocs-addons-test-utils', - 'plugins/techdocs-backend', - 'plugins/techdocs-module-addons-contrib', - 'plugins/techdocs-node', - 'plugins/techdocs-react', - 'plugins/tech-insights', - 'plugins/tech-insights-backend', - 'plugins/tech-insights-backend-module-jsonfc', - 'plugins/tech-insights-common', - 'plugins/tech-insights-node', - 'plugins/todo', - 'plugins/todo-backend', +const ALLOW_WARNINGS = [ + 'packages/core-components', + 'plugins/allure', + 'plugins/apache-airflow', + 'plugins/api-docs', + 'plugins/app-backend', + 'plugins/auth-backend', + 'plugins/azure-devops', + 'plugins/azure-devops-backend', + 'plugins/azure-devops-common', + 'plugins/badges', + 'plugins/badges-backend', + 'plugins/bazaar', + 'plugins/bazaar-backend', + 'plugins/bitrise', + 'plugins/catalog', + 'plugins/catalog-graphql', + 'plugins/catalog-import', + 'plugins/cicd-statistics', + 'plugins/circleci', + 'plugins/cloudbuild', + 'plugins/code-climate', + 'plugins/code-coverage', + 'plugins/code-coverage-backend', + 'plugins/config-schema', + 'plugins/cost-insights', + 'plugins/dynatrace', + 'plugins/explore', + 'plugins/explore-react', + 'plugins/firehydrant', + 'plugins/fossa', + 'plugins/gcalendar', + 'plugins/gcp-projects', + 'plugins/git-release-manager', + 'plugins/github-actions', + 'plugins/github-deployments', + 'plugins/github-pull-requests-board', + 'plugins/gitops-profiles', + 'plugins/graphql-backend', + 'plugins/home', + 'plugins/ilert', + 'plugins/jenkins', + 'plugins/jenkins-backend', + 'plugins/kafka', + 'plugins/kafka-backend', + 'plugins/kubernetes', + 'plugins/kubernetes-backend', + 'plugins/kubernetes-common', + 'plugins/lighthouse', + 'plugins/newrelic', + 'plugins/newrelic-dashboard', + 'plugins/pagerduty', + 'plugins/proxy-backend', + 'plugins/rollbar', + 'plugins/rollbar-backend', + 'plugins/search-backend-module-pg', + 'plugins/sentry', + 'plugins/shortcuts', + 'plugins/sonarqube', + 'plugins/splunk-on-call', + 'plugins/tech-radar', + 'plugins/user-settings', + 'plugins/xcmetrics', ]; async function resolvePackagePath( @@ -580,7 +580,7 @@ async function runApiExtraction({ } const warningCountAfter = await countApiReportWarnings(projectFolder); - if (NO_WARNING_PACKAGES.includes(packageDir) && warningCountAfter > 0) { + if (warningCountAfter > 0 && !ALLOW_WARNINGS.includes(packageDir)) { throw new Error( `The API Report for ${packageDir} is not allowed to have warnings`, ); @@ -939,6 +939,16 @@ async function buildDocs({ this._markdownEmitter = new CustomCustomMarkdownEmitter(newModel); } + private _getFilenameForApiItem(apiItem: ApiItem): string { + const filename: string = super._getFilenameForApiItem(apiItem); + + if (filename.includes('.html.')) { + return filename.replace(/\.html\./g, '._html.'); + } + + return filename; + } + // We don't really get many chances to modify the generated AST // so we hook in wherever we can. In this case we add the front matter // just before writing the breadcrumbs at the top. diff --git a/storybook/.storybook/main.js b/storybook/.storybook/main.js index f493bd2de6..d2e22abee7 100644 --- a/storybook/.storybook/main.js +++ b/storybook/.storybook/main.js @@ -9,6 +9,7 @@ const BACKSTAGE_CORE_STORIES = [ 'packages/app', 'plugins/org', 'plugins/search', + 'plugins/search-react', 'plugins/home', 'plugins/stack-overflow', ]; diff --git a/storybook/yarn.lock b/storybook/yarn.lock index 8c6380c291..39d0e2725f 100644 --- a/storybook/yarn.lock +++ b/storybook/yarn.lock @@ -3,11 +3,12 @@ "@ampproject/remapping@^2.1.0": - version "2.1.2" - resolved "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.1.2.tgz#4edca94973ded9630d20101cd8559cedb8d8bd34" - integrity sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg== + version "2.2.0" + resolved "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d" + integrity sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w== dependencies: - "@jridgewell/trace-mapping" "^0.3.0" + "@jridgewell/gen-mapping" "^0.1.0" + "@jridgewell/trace-mapping" "^0.3.9" "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.16.7", "@babel/code-frame@^7.5.5", "@babel/code-frame@^7.8.3": version "7.16.7" @@ -16,10 +17,10 @@ dependencies: "@babel/highlight" "^7.16.7" -"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.16.8", "@babel/compat-data@^7.17.0", "@babel/compat-data@^7.17.7": - version "7.17.7" - resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.17.7.tgz#078d8b833fbbcc95286613be8c716cef2b519fa2" - integrity sha512-p8pdE6j0a29TNGebNm7NzYZWB3xVZJBZ7XGs42uAKzQo8VQ3F0By/cQCtUEABwIqw5zo6WA4NbmxsfzADzMKnQ== +"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.17.10": + version "7.18.5" + resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.18.5.tgz#acac0c839e317038c73137fbb6ef71a1d6238471" + integrity sha512-BxhE40PVCBxVEJsSBhB6UWyAuqJRxGsAw8BdHMJ3AKGydcwuWW4kOO3HmqBQAdcq/OP+/DlTVxLvsCzRTnZuGg== "@babel/core@7.12.9": version "7.12.9" @@ -44,34 +45,34 @@ source-map "^0.5.0" "@babel/core@^7.12.10", "@babel/core@^7.7.5": - version "7.17.9" - resolved "https://registry.npmjs.org/@babel/core/-/core-7.17.9.tgz#6bae81a06d95f4d0dec5bb9d74bbc1f58babdcfe" - integrity sha512-5ug+SfZCpDAkVp9SFIZAzlW18rlzsOcJGaetCjkySnrXXDUw9AR8cDUm1iByTmdWM6yxX6/zycaV76w3YTF2gw== + version "7.18.5" + resolved "https://registry.npmjs.org/@babel/core/-/core-7.18.5.tgz#c597fa680e58d571c28dda9827669c78cdd7f000" + integrity sha512-MGY8vg3DxMnctw0LdvSEojOsumc70g0t18gNyUdAZqB1Rpd1Bqo/svHGvt+UJ6JcGX+DIekGFDxxIWofBxLCnQ== dependencies: "@ampproject/remapping" "^2.1.0" "@babel/code-frame" "^7.16.7" - "@babel/generator" "^7.17.9" - "@babel/helper-compilation-targets" "^7.17.7" - "@babel/helper-module-transforms" "^7.17.7" - "@babel/helpers" "^7.17.9" - "@babel/parser" "^7.17.9" + "@babel/generator" "^7.18.2" + "@babel/helper-compilation-targets" "^7.18.2" + "@babel/helper-module-transforms" "^7.18.0" + "@babel/helpers" "^7.18.2" + "@babel/parser" "^7.18.5" "@babel/template" "^7.16.7" - "@babel/traverse" "^7.17.9" - "@babel/types" "^7.17.0" + "@babel/traverse" "^7.18.5" + "@babel/types" "^7.18.4" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" json5 "^2.2.1" semver "^6.3.0" -"@babel/generator@^7.12.11", "@babel/generator@^7.12.5", "@babel/generator@^7.17.9": - version "7.17.9" - resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.17.9.tgz#f4af9fd38fa8de143c29fce3f71852406fc1e2fc" - integrity sha512-rAdDousTwxbIxbz5I7GEQ3lUip+xVCXooZNbsydCWs3xA7ZsYOv+CFRdzGxRX78BmQHu9B1Eso59AOZQOJDEdQ== +"@babel/generator@^7.12.11", "@babel/generator@^7.12.5", "@babel/generator@^7.18.2": + version "7.18.2" + resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.18.2.tgz#33873d6f89b21efe2da63fe554460f3df1c5880d" + integrity sha512-W1lG5vUwFvfMd8HVXqdfbuG7RuaSrTCCD8cl8fP8wOivdbtbIg2Db3IWUcgvfxKbbn6ZBGYRW/Zk1MIwK49mgw== dependencies: - "@babel/types" "^7.17.0" + "@babel/types" "^7.18.2" + "@jridgewell/gen-mapping" "^0.3.0" jsesc "^2.5.1" - source-map "^0.5.0" "@babel/helper-annotate-as-pure@^7.16.7": version "7.16.7" @@ -88,20 +89,20 @@ "@babel/helper-explode-assignable-expression" "^7.16.7" "@babel/types" "^7.16.7" -"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.16.7", "@babel/helper-compilation-targets@^7.17.7": - version "7.17.7" - resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.17.7.tgz#a3c2924f5e5f0379b356d4cfb313d1414dc30e46" - integrity sha512-UFzlz2jjd8kroj0hmCFV5zr+tQPi1dpC2cRsDV/3IEW8bJfCPrPpmcSN6ZS8RqIq4LXcmpipCQFPddyFA5Yc7w== +"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.16.7", "@babel/helper-compilation-targets@^7.17.10", "@babel/helper-compilation-targets@^7.18.2": + version "7.18.2" + resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.2.tgz#67a85a10cbd5fc7f1457fec2e7f45441dc6c754b" + integrity sha512-s1jnPotJS9uQnzFtiZVBUxe67CuBa679oWFHpxYYnTpRL/1ffhyX44R9uYiXoa/pLXcY9H2moJta0iaanlk/rQ== dependencies: - "@babel/compat-data" "^7.17.7" + "@babel/compat-data" "^7.17.10" "@babel/helper-validator-option" "^7.16.7" - browserslist "^4.17.5" + browserslist "^4.20.2" semver "^6.3.0" -"@babel/helper-create-class-features-plugin@^7.16.10", "@babel/helper-create-class-features-plugin@^7.16.7", "@babel/helper-create-class-features-plugin@^7.17.6", "@babel/helper-create-class-features-plugin@^7.17.9": - version "7.17.9" - resolved "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.17.9.tgz#71835d7fb9f38bd9f1378e40a4c0902fdc2ea49d" - integrity sha512-kUjip3gruz6AJKOq5i3nC6CoCEEF/oHH3cp6tOZhB+IyyyPyW0g1Gfsxn3mkk6S08pIA2y8GQh609v9G/5sHVQ== +"@babel/helper-create-class-features-plugin@^7.17.12", "@babel/helper-create-class-features-plugin@^7.18.0": + version "7.18.0" + resolved "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.18.0.tgz#fac430912606331cb075ea8d82f9a4c145a4da19" + integrity sha512-Kh8zTGR9de3J63e5nS0rQUdRs/kbtwoeQQ0sriS0lItjC96u8XXZN6lKpuyWd2coKSU13py/y+LTmThLuVX0Pg== dependencies: "@babel/helper-annotate-as-pure" "^7.16.7" "@babel/helper-environment-visitor" "^7.16.7" @@ -111,10 +112,10 @@ "@babel/helper-replace-supers" "^7.16.7" "@babel/helper-split-export-declaration" "^7.16.7" -"@babel/helper-create-regexp-features-plugin@^7.16.7": - version "7.17.0" - resolved "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.17.0.tgz#1dcc7d40ba0c6b6b25618997c5dbfd310f186fe1" - integrity sha512-awO2So99wG6KnlE+TPs6rn83gCz5WlEePJDTnLEqbchMVrBeAujURVphRdigsk094VhvZehFoNOihSlcBjwsXA== +"@babel/helper-create-regexp-features-plugin@^7.16.7", "@babel/helper-create-regexp-features-plugin@^7.17.12": + version "7.17.12" + resolved "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.17.12.tgz#bb37ca467f9694bbe55b884ae7a5cc1e0084e4fd" + integrity sha512-b2aZrV4zvutr9AIa6/gA3wsZKRwTKYoDxYiFKcESS3Ug2GTXzwBEvMuuFLhCQpEnRXs1zng4ISAXSUxxKBIcxw== dependencies: "@babel/helper-annotate-as-pure" "^7.16.7" regexpu-core "^5.0.1" @@ -147,12 +148,10 @@ resolve "^1.14.2" semver "^6.1.2" -"@babel/helper-environment-visitor@^7.16.7": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz#ff484094a839bde9d89cd63cba017d7aae80ecd7" - integrity sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag== - dependencies: - "@babel/types" "^7.16.7" +"@babel/helper-environment-visitor@^7.16.7", "@babel/helper-environment-visitor@^7.18.2": + version "7.18.2" + resolved "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.2.tgz#8a6d2dedb53f6bf248e31b4baf38739ee4a637bd" + integrity sha512-14GQKWkX9oJzPiQQ7/J36FTXcD4kSp8egKjO9nINlSKiHITRA9q/R74qu8S9xlc/b/yjsJItQUeeh3xnGN0voQ== "@babel/helper-explode-assignable-expression@^7.16.7": version "7.16.7" @@ -176,24 +175,24 @@ dependencies: "@babel/types" "^7.16.7" -"@babel/helper-member-expression-to-functions@^7.16.7", "@babel/helper-member-expression-to-functions@^7.17.7": +"@babel/helper-member-expression-to-functions@^7.17.7": version "7.17.7" resolved "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.17.7.tgz#a34013b57d8542a8c4ff8ba3f747c02452a4d8c4" integrity sha512-thxXgnQ8qQ11W2wVUObIqDL4p148VMxkt5T/qpN5k2fboRyzFGFmKsTGViquyM5QHKUy48OZoca8kw4ajaDPyw== dependencies: "@babel/types" "^7.17.0" -"@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.16.7": +"@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.16.7": version "7.16.7" resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz#25612a8091a999704461c8a222d0efec5d091437" integrity sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg== dependencies: "@babel/types" "^7.16.7" -"@babel/helper-module-transforms@^7.12.1", "@babel/helper-module-transforms@^7.16.7", "@babel/helper-module-transforms@^7.17.7": - version "7.17.7" - resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.17.7.tgz#3943c7f777139e7954a5355c815263741a9c1cbd" - integrity sha512-VmZD99F3gNTYB7fJRDTi+u6l/zxY0BE6OIxPSU7a50s6ZUQkHwSDmV92FfM+oCG0pZRVojGYhkR8I0OGeCVREw== +"@babel/helper-module-transforms@^7.12.1", "@babel/helper-module-transforms@^7.18.0": + version "7.18.0" + resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.18.0.tgz#baf05dec7a5875fb9235bd34ca18bad4e21221cd" + integrity sha512-kclUYSUBIjlvnzN2++K9f2qzYKFgjmnmjwL4zlmU5f8ZtzgWe8s0rUPSTGy2HmK4P8T52MQsS+HTQAgZd3dMEA== dependencies: "@babel/helper-environment-visitor" "^7.16.7" "@babel/helper-module-imports" "^7.16.7" @@ -201,8 +200,8 @@ "@babel/helper-split-export-declaration" "^7.16.7" "@babel/helper-validator-identifier" "^7.16.7" "@babel/template" "^7.16.7" - "@babel/traverse" "^7.17.3" - "@babel/types" "^7.17.0" + "@babel/traverse" "^7.18.0" + "@babel/types" "^7.18.0" "@babel/helper-optimise-call-expression@^7.16.7": version "7.16.7" @@ -216,10 +215,10 @@ resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz#2f75a831269d4f677de49986dff59927533cf375" integrity sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg== -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.13.0", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz#aa3a8ab4c3cceff8e65eb9e73d87dc4ff320b2f5" - integrity sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA== +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.13.0", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.17.12", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": + version "7.17.12" + resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.17.12.tgz#86c2347da5acbf5583ba0a10aed4c9bf9da9cf96" + integrity sha512-JDkf04mqtN3y4iAbO1hv9U2ARpPyPL1zqyWs/2WG1pgSq9llHFjStX5jdxb84himgJm+8Ng+x0oiWF/nw/XQKA== "@babel/helper-remap-async-to-generator@^7.16.8": version "7.16.8" @@ -230,23 +229,23 @@ "@babel/helper-wrap-function" "^7.16.8" "@babel/types" "^7.16.8" -"@babel/helper-replace-supers@^7.16.7": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.16.7.tgz#e9f5f5f32ac90429c1a4bdec0f231ef0c2838ab1" - integrity sha512-y9vsWilTNaVnVh6xiJfABzsNpgDPKev9HnAgz6Gb1p6UUwf9NepdlsV7VXGCftJM+jqD5f7JIEubcpLjZj5dBw== +"@babel/helper-replace-supers@^7.16.7", "@babel/helper-replace-supers@^7.18.2": + version "7.18.2" + resolved "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.18.2.tgz#41fdfcc9abaf900e18ba6e5931816d9062a7b2e0" + integrity sha512-XzAIyxx+vFnrOxiQrToSUOzUOn0e1J2Li40ntddek1Y69AXUTXoDJ40/D5RdjFu7s7qHiaeoTiempZcbuVXh2Q== dependencies: - "@babel/helper-environment-visitor" "^7.16.7" - "@babel/helper-member-expression-to-functions" "^7.16.7" + "@babel/helper-environment-visitor" "^7.18.2" + "@babel/helper-member-expression-to-functions" "^7.17.7" "@babel/helper-optimise-call-expression" "^7.16.7" - "@babel/traverse" "^7.16.7" - "@babel/types" "^7.16.7" + "@babel/traverse" "^7.18.2" + "@babel/types" "^7.18.2" -"@babel/helper-simple-access@^7.17.7": - version "7.17.7" - resolved "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.17.7.tgz#aaa473de92b7987c6dfa7ce9a7d9674724823367" - integrity sha512-txyMCGroZ96i+Pxr3Je3lzEJjqwaRC9buMUgtomcrLe5Nd0+fk1h0LLA+ixUF5OW7AhHuQ7Es1WcQJZmZsz2XA== +"@babel/helper-simple-access@^7.17.7", "@babel/helper-simple-access@^7.18.2": + version "7.18.2" + resolved "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.18.2.tgz#4dc473c2169ac3a1c9f4a51cfcd091d1c36fcff9" + integrity sha512-7LIrjYzndorDY88MycupkpQLKS1AFfsVRm2k/9PtKScSy5tZq0McZTj+DiMRynboZfIqOKvo03pmhTaUgiD6fQ== dependencies: - "@babel/types" "^7.17.0" + "@babel/types" "^7.18.2" "@babel/helper-skip-transparent-expression-wrappers@^7.16.0": version "7.16.0" @@ -282,81 +281,81 @@ "@babel/traverse" "^7.16.8" "@babel/types" "^7.16.8" -"@babel/helpers@^7.12.5", "@babel/helpers@^7.17.9": - version "7.17.9" - resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.9.tgz#b2af120821bfbe44f9907b1826e168e819375a1a" - integrity sha512-cPCt915ShDWUEzEp3+UNRktO2n6v49l5RSnG9M5pS24hA+2FAc5si+Pn1i4VVbQQ+jh+bIZhPFQOJOzbrOYY1Q== +"@babel/helpers@^7.12.5", "@babel/helpers@^7.18.2": + version "7.18.2" + resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.18.2.tgz#970d74f0deadc3f5a938bfa250738eb4ac889384" + integrity sha512-j+d+u5xT5utcQSzrh9p+PaJX94h++KN+ng9b9WEJq7pkUPAd61FGqhjuUEdfknb3E/uDBb7ruwEeKkIxNJPIrg== dependencies: "@babel/template" "^7.16.7" - "@babel/traverse" "^7.17.9" - "@babel/types" "^7.17.0" + "@babel/traverse" "^7.18.2" + "@babel/types" "^7.18.2" "@babel/highlight@^7.16.7": - version "7.17.9" - resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.17.9.tgz#61b2ee7f32ea0454612def4fccdae0de232b73e3" - integrity sha512-J9PfEKCbFIv2X5bjTMiZu6Vf341N05QIY+d6FvVKynkG1S7G0j3I0QoRtWIrXhZ+/Nlb5Q0MzqL7TokEJ5BNHg== + version "7.17.12" + resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.17.12.tgz#257de56ee5afbd20451ac0a75686b6b404257351" + integrity sha512-7yykMVF3hfZY2jsHZEEgLc+3x4o1O+fYyULu11GynEUQNwB6lua+IIQn1FiJxNucd5UlyJryrwsOh8PL9Sn8Qg== dependencies: "@babel/helper-validator-identifier" "^7.16.7" chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.12.11", "@babel/parser@^7.12.7", "@babel/parser@^7.16.7", "@babel/parser@^7.17.9": - version "7.17.9" - resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.17.9.tgz#9c94189a6062f0291418ca021077983058e171ef" - integrity sha512-vqUSBLP8dQHFPdPi9bc5GK9vRkYHJ49fsZdtoJ8EQ8ibpwk5rPKfvNIwChB0KVXcIjcepEBBd2VHC5r9Gy8ueg== +"@babel/parser@^7.12.11", "@babel/parser@^7.12.7", "@babel/parser@^7.16.7", "@babel/parser@^7.18.5": + version "7.18.5" + resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.18.5.tgz#337062363436a893a2d22faa60be5bb37091c83c" + integrity sha512-YZWVaglMiplo7v8f1oMQ5ZPQr0vn7HPeZXxXWsxXJRjGVrzUFn9OxFQl1sb5wzfootjA/yChhW84BV+383FSOw== -"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.16.7": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.16.7.tgz#4eda6d6c2a0aa79c70fa7b6da67763dfe2141050" - integrity sha512-anv/DObl7waiGEnC24O9zqL0pSuI9hljihqiDuFHC8d7/bjr/4RLGPWuc8rYOff/QPzbEPSkzG8wGG9aDuhHRg== +"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.17.12": + version "7.17.12" + resolved "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.17.12.tgz#1dca338caaefca368639c9ffb095afbd4d420b1e" + integrity sha512-xCJQXl4EeQ3J9C4yOmpTrtVGmzpm2iSzyxbkZHw7UCnZBftHpF/hpII80uWVyVrc40ytIClHjgWGTG1g/yB+aw== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.17.12" -"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.16.7": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.16.7.tgz#cc001234dfc139ac45f6bcf801866198c8c72ff9" - integrity sha512-di8vUHRdf+4aJ7ltXhaDbPoszdkh59AQtJM5soLsuHpQJdFQZOA4uGj0V2u/CZ8bJ/u8ULDL5yq6FO/bCXnKHw== +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.17.12": + version "7.17.12" + resolved "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.17.12.tgz#0d498ec8f0374b1e2eb54b9cb2c4c78714c77753" + integrity sha512-/vt0hpIw0x4b6BLKUkwlvEoiGZYYLNZ96CzyHYPbtG2jZGz6LBe7/V+drYrc/d+ovrF9NBi0pmtvmNb/FsWtRQ== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.17.12" "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" - "@babel/plugin-proposal-optional-chaining" "^7.16.7" + "@babel/plugin-proposal-optional-chaining" "^7.17.12" -"@babel/plugin-proposal-async-generator-functions@^7.16.8": - version "7.16.8" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.16.8.tgz#3bdd1ebbe620804ea9416706cd67d60787504bc8" - integrity sha512-71YHIvMuiuqWJQkebWJtdhQTfd4Q4mF76q2IX37uZPkG9+olBxsX+rH1vkhFto4UeJZ9dPY2s+mDvhDm1u2BGQ== +"@babel/plugin-proposal-async-generator-functions@^7.17.12": + version "7.17.12" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.17.12.tgz#094a417e31ce7e692d84bab06c8e2a607cbeef03" + integrity sha512-RWVvqD1ooLKP6IqWTA5GyFVX2isGEgC5iFxKzfYOIy/QEFdxYyCybBDtIGjipHpb9bDWHzcqGqFakf+mVmBTdQ== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.17.12" "@babel/helper-remap-async-to-generator" "^7.16.8" "@babel/plugin-syntax-async-generators" "^7.8.4" -"@babel/plugin-proposal-class-properties@^7.12.1", "@babel/plugin-proposal-class-properties@^7.16.7": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.7.tgz#925cad7b3b1a2fcea7e59ecc8eb5954f961f91b0" - integrity sha512-IobU0Xme31ewjYOShSIqd/ZGM/r/cuOz2z0MDbNrhF5FW+ZVgi0f2lyeoj9KFPDOAqsYxmLWZte1WOwlvY9aww== +"@babel/plugin-proposal-class-properties@^7.12.1", "@babel/plugin-proposal-class-properties@^7.17.12": + version "7.17.12" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.17.12.tgz#84f65c0cc247d46f40a6da99aadd6438315d80a4" + integrity sha512-U0mI9q8pW5Q9EaTHFPwSVusPMV/DV9Mm8p7csqROFLtIE9rBF5piLqyrBGigftALrBcsBGu4m38JneAe7ZDLXw== dependencies: - "@babel/helper-create-class-features-plugin" "^7.16.7" - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-create-class-features-plugin" "^7.17.12" + "@babel/helper-plugin-utils" "^7.17.12" -"@babel/plugin-proposal-class-static-block@^7.16.7": - version "7.17.6" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.17.6.tgz#164e8fd25f0d80fa48c5a4d1438a6629325ad83c" - integrity sha512-X/tididvL2zbs7jZCeeRJ8167U/+Ac135AM6jCAx6gYXDUviZV5Ku9UDvWS2NCuWlFjIRXklYhwo6HhAC7ETnA== +"@babel/plugin-proposal-class-static-block@^7.18.0": + version "7.18.0" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.18.0.tgz#7d02253156e3c3793bdb9f2faac3a1c05f0ba710" + integrity sha512-t+8LsRMMDE74c6sV7KShIw13sqbqd58tlqNrsWoWBTIMw7SVQ0cZ905wLNS/FBCy/3PyooRHLFFlfrUNyyz5lA== dependencies: - "@babel/helper-create-class-features-plugin" "^7.17.6" - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-create-class-features-plugin" "^7.18.0" + "@babel/helper-plugin-utils" "^7.17.12" "@babel/plugin-syntax-class-static-block" "^7.14.5" "@babel/plugin-proposal-decorators@^7.12.12": - version "7.17.9" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.17.9.tgz#67a1653be9c77ce5b6c318aa90c8287b87831619" - integrity sha512-EfH2LZ/vPa2wuPwJ26j+kYRkaubf89UlwxKXtxqEm57HrgSEYDB8t4swFP+p8LcI9yiP9ZRJJjo/58hS6BnaDA== + version "7.18.2" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.18.2.tgz#dbe4086d2d42db489399783c3aa9272e9700afd4" + integrity sha512-kbDISufFOxeczi0v4NQP3p5kIeW6izn/6klfWBrIIdGZZe4UpHR+QU03FAoWjGGd9SUXAwbw2pup1kaL4OQsJQ== dependencies: - "@babel/helper-create-class-features-plugin" "^7.17.9" - "@babel/helper-plugin-utils" "^7.16.7" - "@babel/helper-replace-supers" "^7.16.7" + "@babel/helper-create-class-features-plugin" "^7.18.0" + "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-replace-supers" "^7.18.2" "@babel/helper-split-export-declaration" "^7.16.7" - "@babel/plugin-syntax-decorators" "^7.17.0" + "@babel/plugin-syntax-decorators" "^7.17.12" charcodes "^0.2.0" "@babel/plugin-proposal-dynamic-import@^7.16.7": @@ -368,43 +367,43 @@ "@babel/plugin-syntax-dynamic-import" "^7.8.3" "@babel/plugin-proposal-export-default-from@^7.12.1": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.16.7.tgz#a40ab158ca55627b71c5513f03d3469026a9e929" - integrity sha512-+cENpW1rgIjExn+o5c8Jw/4BuH4eGKKYvkMB8/0ZxFQ9mC0t4z09VsPIwNg6waF69QYC81zxGeAsREGuqQoKeg== + version "7.17.12" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.17.12.tgz#df785e638618d8ffa14e08c78c44d9695d083b73" + integrity sha512-LpsTRw725eBAXXKUOnJJct+SEaOzwR78zahcLuripD2+dKc2Sj+8Q2DzA+GC/jOpOu/KlDXuxrzG214o1zTauQ== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.17.12" "@babel/plugin-syntax-export-default-from" "^7.16.7" -"@babel/plugin-proposal-export-namespace-from@^7.16.7": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.16.7.tgz#09de09df18445a5786a305681423ae63507a6163" - integrity sha512-ZxdtqDXLRGBL64ocZcs7ovt71L3jhC1RGSyR996svrCi3PYqHNkb3SwPJCs8RIzD86s+WPpt2S73+EHCGO+NUA== +"@babel/plugin-proposal-export-namespace-from@^7.17.12": + version "7.17.12" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.17.12.tgz#b22864ccd662db9606edb2287ea5fd1709f05378" + integrity sha512-j7Ye5EWdwoXOpRmo5QmRyHPsDIe6+u70ZYZrd7uz+ebPYFKfRcLcNu3Ro0vOlJ5zuv8rU7xa+GttNiRzX56snQ== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.17.12" "@babel/plugin-syntax-export-namespace-from" "^7.8.3" -"@babel/plugin-proposal-json-strings@^7.16.7": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.16.7.tgz#9732cb1d17d9a2626a08c5be25186c195b6fa6e8" - integrity sha512-lNZ3EEggsGY78JavgbHsK9u5P3pQaW7k4axlgFLYkMd7UBsiNahCITShLjNQschPyjtO6dADrL24757IdhBrsQ== +"@babel/plugin-proposal-json-strings@^7.17.12": + version "7.17.12" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.17.12.tgz#f4642951792437233216d8c1af370bb0fbff4664" + integrity sha512-rKJ+rKBoXwLnIn7n6o6fulViHMrOThz99ybH+hKHcOZbnN14VuMnH9fo2eHE69C8pO4uX1Q7t2HYYIDmv8VYkg== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.17.12" "@babel/plugin-syntax-json-strings" "^7.8.3" -"@babel/plugin-proposal-logical-assignment-operators@^7.16.7": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.16.7.tgz#be23c0ba74deec1922e639832904be0bea73cdea" - integrity sha512-K3XzyZJGQCr00+EtYtrDjmwX7o7PLK6U9bi1nCwkQioRFVUv6dJoxbQjtWVtP+bCPy82bONBKG8NPyQ4+i6yjg== +"@babel/plugin-proposal-logical-assignment-operators@^7.17.12": + version "7.17.12" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.17.12.tgz#c64a1bcb2b0a6d0ed2ff674fd120f90ee4b88a23" + integrity sha512-EqFo2s1Z5yy+JeJu7SFfbIUtToJTVlC61/C7WLKDntSw4Sz6JNAIfL7zQ74VvirxpjB5kz/kIx0gCcb+5OEo2Q== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.17.12" "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" -"@babel/plugin-proposal-nullish-coalescing-operator@^7.12.1", "@babel/plugin-proposal-nullish-coalescing-operator@^7.16.7": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.16.7.tgz#141fc20b6857e59459d430c850a0011e36561d99" - integrity sha512-aUOrYU3EVtjf62jQrCj63pYZ7k6vns2h/DQvHPWGmsJRYzWXZ6/AsfgpiRy6XiuIDADhJzP2Q9MwSMKauBQ+UQ== +"@babel/plugin-proposal-nullish-coalescing-operator@^7.12.1", "@babel/plugin-proposal-nullish-coalescing-operator@^7.17.12": + version "7.17.12" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.17.12.tgz#1e93079bbc2cbc756f6db6a1925157c4a92b94be" + integrity sha512-ws/g3FSGVzv+VH86+QvgtuJL/kR67xaEIF2x0iPqdDfYW6ra6JF3lKVBkWynRLcNtIC1oCTfDRVxmm2mKzy+ag== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.17.12" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" "@babel/plugin-proposal-numeric-separator@^7.16.7": @@ -424,16 +423,16 @@ "@babel/plugin-syntax-object-rest-spread" "^7.8.0" "@babel/plugin-transform-parameters" "^7.12.1" -"@babel/plugin-proposal-object-rest-spread@^7.12.1", "@babel/plugin-proposal-object-rest-spread@^7.16.7": - version "7.17.3" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.17.3.tgz#d9eb649a54628a51701aef7e0ea3d17e2b9dd390" - integrity sha512-yuL5iQA/TbZn+RGAfxQXfi7CNLmKi1f8zInn4IgobuCWcAb7i+zj4TYzQ9l8cEzVyJ89PDGuqxK1xZpUDISesw== +"@babel/plugin-proposal-object-rest-spread@^7.12.1", "@babel/plugin-proposal-object-rest-spread@^7.18.0": + version "7.18.0" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.18.0.tgz#79f2390c892ba2a68ec112eb0d895cfbd11155e8" + integrity sha512-nbTv371eTrFabDfHLElkn9oyf9VG+VKK6WMzhY2o4eHKaG19BToD9947zzGMO6I/Irstx9d8CwX6njPNIAR/yw== dependencies: - "@babel/compat-data" "^7.17.0" - "@babel/helper-compilation-targets" "^7.16.7" - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/compat-data" "^7.17.10" + "@babel/helper-compilation-targets" "^7.17.10" + "@babel/helper-plugin-utils" "^7.17.12" "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-transform-parameters" "^7.16.7" + "@babel/plugin-transform-parameters" "^7.17.12" "@babel/plugin-proposal-optional-catch-binding@^7.16.7": version "7.16.7" @@ -443,40 +442,40 @@ "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" -"@babel/plugin-proposal-optional-chaining@^7.12.7", "@babel/plugin-proposal-optional-chaining@^7.16.7": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.16.7.tgz#7cd629564724816c0e8a969535551f943c64c39a" - integrity sha512-eC3xy+ZrUcBtP7x+sq62Q/HYd674pPTb/77XZMb5wbDPGWIdUbSr4Agr052+zaUPSb+gGRnjxXfKFvx5iMJ+DA== +"@babel/plugin-proposal-optional-chaining@^7.12.7", "@babel/plugin-proposal-optional-chaining@^7.17.12": + version "7.17.12" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.17.12.tgz#f96949e9bacace3a9066323a5cf90cfb9de67174" + integrity sha512-7wigcOs/Z4YWlK7xxjkvaIw84vGhDv/P1dFGQap0nHkc8gFKY/r+hXc8Qzf5k1gY7CvGIcHqAnOagVKJJ1wVOQ== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.17.12" "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" "@babel/plugin-syntax-optional-chaining" "^7.8.3" -"@babel/plugin-proposal-private-methods@^7.12.1", "@babel/plugin-proposal-private-methods@^7.16.11": - version "7.16.11" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.16.11.tgz#e8df108288555ff259f4527dbe84813aac3a1c50" - integrity sha512-F/2uAkPlXDr8+BHpZvo19w3hLFKge+k75XUprE6jaqKxjGkSYcK+4c+bup5PdW/7W/Rpjwql7FTVEDW+fRAQsw== +"@babel/plugin-proposal-private-methods@^7.12.1", "@babel/plugin-proposal-private-methods@^7.17.12": + version "7.17.12" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.17.12.tgz#c2ca3a80beb7539289938da005ad525a038a819c" + integrity sha512-SllXoxo19HmxhDWm3luPz+cPhtoTSKLJE9PXshsfrOzBqs60QP0r8OaJItrPhAj0d7mZMnNF0Y1UUggCDgMz1A== dependencies: - "@babel/helper-create-class-features-plugin" "^7.16.10" - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-create-class-features-plugin" "^7.17.12" + "@babel/helper-plugin-utils" "^7.17.12" -"@babel/plugin-proposal-private-property-in-object@^7.16.7": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.16.7.tgz#b0b8cef543c2c3d57e59e2c611994861d46a3fce" - integrity sha512-rMQkjcOFbm+ufe3bTZLyOfsOUOxyvLXZJCTARhJr+8UMSoZmqTe1K1BgkFcrW37rAchWg57yI69ORxiWvUINuQ== +"@babel/plugin-proposal-private-property-in-object@^7.12.1", "@babel/plugin-proposal-private-property-in-object@^7.17.12": + version "7.17.12" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.17.12.tgz#b02efb7f106d544667d91ae97405a9fd8c93952d" + integrity sha512-/6BtVi57CJfrtDNKfK5b66ydK2J5pXUKBKSPD2G1whamMuEnZWgoOIfO8Vf9F/DoD4izBLD/Au4NMQfruzzykg== dependencies: "@babel/helper-annotate-as-pure" "^7.16.7" - "@babel/helper-create-class-features-plugin" "^7.16.7" - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-create-class-features-plugin" "^7.17.12" + "@babel/helper-plugin-utils" "^7.17.12" "@babel/plugin-syntax-private-property-in-object" "^7.14.5" -"@babel/plugin-proposal-unicode-property-regex@^7.16.7", "@babel/plugin-proposal-unicode-property-regex@^7.4.4": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.16.7.tgz#635d18eb10c6214210ffc5ff4932552de08188a2" - integrity sha512-QRK0YI/40VLhNVGIjRNAAQkEHws0cswSdFFjpFyt943YmJIU1da9uW63Iu6NFV6CxTZW5eTDCrwZUstBWgp/Rg== +"@babel/plugin-proposal-unicode-property-regex@^7.17.12", "@babel/plugin-proposal-unicode-property-regex@^7.4.4": + version "7.17.12" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.17.12.tgz#3dbd7a67bd7f94c8238b394da112d86aaf32ad4d" + integrity sha512-Wb9qLjXf3ZazqXA7IvI7ozqRIXIGPtSo+L5coFmEkhTQK18ao4UDDD0zdTGAarmbLj2urpRwrc6893cu5Bfh0A== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.16.7" - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-create-regexp-features-plugin" "^7.17.12" + "@babel/helper-plugin-utils" "^7.17.12" "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" @@ -499,12 +498,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-syntax-decorators@^7.17.0": - version "7.17.0" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.17.0.tgz#a2be3b2c9fe7d78bd4994e790896bc411e2f166d" - integrity sha512-qWe85yCXsvDEluNP0OyeQjH63DlhAR3W7K9BxxU1MvbDb48tgBG+Ao6IJJ6smPDrrVzSQZrbF6donpkFBMcs3A== +"@babel/plugin-syntax-decorators@^7.17.12": + version "7.17.12" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.17.12.tgz#02e8f678602f0af8222235271efea945cfdb018a" + integrity sha512-D1Hz0qtGTza8K2xGyEdVNCYLdVHukAcbQr4K3/s6r/esadyEriZovpJimQOpu8ju4/jV8dW/1xdaE0UpDroidw== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.17.12" "@babel/plugin-syntax-dynamic-import@^7.8.3": version "7.8.3" @@ -527,12 +526,19 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" -"@babel/plugin-syntax-flow@^7.16.7": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.16.7.tgz#202b147e5892b8452bbb0bb269c7ed2539ab8832" - integrity sha512-UDo3YGQO0jH6ytzVwgSLv9i/CzMcUjbKenL67dTrAZPPv6GFAtDhe6jqnvmoKzC/7htNTohhos+onPtDMqJwaQ== +"@babel/plugin-syntax-flow@^7.17.12": + version "7.17.12" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.17.12.tgz#23d852902acd19f42923fca9d0f196984d124e73" + integrity sha512-B8QIgBvkIG6G2jgsOHQUist7Sm0EBLDCx8sen072IwqNuzMegZNXrYnSv77cYzA8mLDZAfQYqsLIhimiP1s2HQ== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.17.12" + +"@babel/plugin-syntax-import-assertions@^7.17.12": + version "7.17.12" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.17.12.tgz#58096a92b11b2e4e54b24c6a0cc0e5e607abcedd" + integrity sha512-n/loy2zkq9ZEM8tEOwON9wTQSTNDTDEz6NujPtJGLU7qObzT1N4c4YZZf8E6ATB2AjNQg/Ib2AIpO03EZaCehw== + dependencies: + "@babel/helper-plugin-utils" "^7.17.12" "@babel/plugin-syntax-json-strings@^7.8.3": version "7.8.3" @@ -548,12 +554,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-syntax-jsx@^7.16.7": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.16.7.tgz#50b6571d13f764266a113d77c82b4a6508bbe665" - integrity sha512-Esxmk7YjA8QysKeT3VhTXvF6y77f/a91SIs4pWb4H2eWGQkCKFgQaG6hdoEVZtGsrAcb2K5BW66XsOErD4WU3Q== +"@babel/plugin-syntax-jsx@^7.17.12": + version "7.17.12" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.17.12.tgz#834035b45061983a491f60096f61a2e7c5674a47" + integrity sha512-spyY3E3AURfxh/RHtjx5j6hs8am5NbUBGfcZ2vB3uShSpZdQyXSf5rR5Mk76vbtlAZOelyVQ71Fg0x9SG4fsog== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.17.12" "@babel/plugin-syntax-logical-assignment-operators@^7.10.4": version "7.10.4" @@ -611,27 +617,27 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-syntax-typescript@^7.16.7": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.16.7.tgz#39c9b55ee153151990fb038651d58d3fd03f98f8" - integrity sha512-YhUIJHHGkqPgEcMYkPCKTyGUdoGKWtopIycQyjJH8OjvRgOYsXsaKehLVPScKJWAULPxMa4N1vCe6szREFlZ7A== +"@babel/plugin-syntax-typescript@^7.17.12": + version "7.17.12" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.17.12.tgz#b54fc3be6de734a56b87508f99d6428b5b605a7b" + integrity sha512-TYY0SXFiO31YXtNg3HtFwNJHjLsAyIIhAhNWkQ5whPPS7HWUFlg9z0Ta4qAQNjQbP1wsSt/oKkmZ/4/WWdMUpw== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.17.12" -"@babel/plugin-transform-arrow-functions@^7.12.1", "@babel/plugin-transform-arrow-functions@^7.16.7": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.16.7.tgz#44125e653d94b98db76369de9c396dc14bef4154" - integrity sha512-9ffkFFMbvzTvv+7dTp/66xvZAWASuPD5Tl9LK3Z9vhOmANo6j94rik+5YMBt4CwHVMWLWpMsriIc2zsa3WW3xQ== +"@babel/plugin-transform-arrow-functions@^7.12.1", "@babel/plugin-transform-arrow-functions@^7.17.12": + version "7.17.12" + resolved "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.17.12.tgz#dddd783b473b1b1537ef46423e3944ff24898c45" + integrity sha512-PHln3CNi/49V+mza4xMwrg+WGYevSF1oaiXaC2EQfdp4HWlSjRsrDXWJiQBKpP7749u6vQ9mcry2uuFOv5CXvA== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.17.12" -"@babel/plugin-transform-async-to-generator@^7.16.8": - version "7.16.8" - resolved "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.16.8.tgz#b83dff4b970cf41f1b819f8b49cc0cfbaa53a808" - integrity sha512-MtmUmTJQHCnyJVrScNzNlofQJ3dLFuobYn3mwOTKHnSCMtbNsqvF71GQmJfFjdrXSsAA7iysFmYWw4bXZ20hOg== +"@babel/plugin-transform-async-to-generator@^7.17.12": + version "7.17.12" + resolved "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.17.12.tgz#dbe5511e6b01eee1496c944e35cdfe3f58050832" + integrity sha512-J8dbrWIOO3orDzir57NRsjg4uxucvhby0L/KZuGsWDj0g7twWK3g7JhJhOrXtuXiw8MeiSdJ3E0OW9H8LYEzLQ== dependencies: "@babel/helper-module-imports" "^7.16.7" - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.17.12" "@babel/helper-remap-async-to-generator" "^7.16.8" "@babel/plugin-transform-block-scoped-functions@^7.16.7": @@ -641,40 +647,40 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-block-scoping@^7.12.12", "@babel/plugin-transform-block-scoping@^7.16.7": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.16.7.tgz#f50664ab99ddeaee5bc681b8f3a6ea9d72ab4f87" - integrity sha512-ObZev2nxVAYA4bhyusELdo9hb3H+A56bxH3FZMbEImZFiEDYVHXQSJ1hQKFlDnlt8G9bBrCZ5ZpURZUrV4G5qQ== +"@babel/plugin-transform-block-scoping@^7.12.12", "@babel/plugin-transform-block-scoping@^7.17.12": + version "7.18.4" + resolved "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.18.4.tgz#7988627b3e9186a13e4d7735dc9c34a056613fb9" + integrity sha512-+Hq10ye+jlvLEogSOtq4mKvtk7qwcUQ1f0Mrueai866C82f844Yom2cttfJdMdqRLTxWpsbfbkIkOIfovyUQXw== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.17.12" -"@babel/plugin-transform-classes@^7.12.1", "@babel/plugin-transform-classes@^7.16.7": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.16.7.tgz#8f4b9562850cd973de3b498f1218796eb181ce00" - integrity sha512-WY7og38SFAGYRe64BrjKf8OrE6ulEHtr5jEYaZMwox9KebgqPi67Zqz8K53EKk1fFEJgm96r32rkKZ3qA2nCWQ== +"@babel/plugin-transform-classes@^7.12.1", "@babel/plugin-transform-classes@^7.17.12": + version "7.18.4" + resolved "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.18.4.tgz#51310b812a090b846c784e47087fa6457baef814" + integrity sha512-e42NSG2mlKWgxKUAD9EJJSkZxR67+wZqzNxLSpc51T8tRU5SLFHsPmgYR5yr7sdgX4u+iHA1C5VafJ6AyImV3A== dependencies: "@babel/helper-annotate-as-pure" "^7.16.7" - "@babel/helper-environment-visitor" "^7.16.7" - "@babel/helper-function-name" "^7.16.7" + "@babel/helper-environment-visitor" "^7.18.2" + "@babel/helper-function-name" "^7.17.9" "@babel/helper-optimise-call-expression" "^7.16.7" - "@babel/helper-plugin-utils" "^7.16.7" - "@babel/helper-replace-supers" "^7.16.7" + "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-replace-supers" "^7.18.2" "@babel/helper-split-export-declaration" "^7.16.7" globals "^11.1.0" -"@babel/plugin-transform-computed-properties@^7.16.7": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.16.7.tgz#66dee12e46f61d2aae7a73710f591eb3df616470" - integrity sha512-gN72G9bcmenVILj//sv1zLNaPyYcOzUho2lIJBMh/iakJ9ygCo/hEF9cpGb61SCMEDxbbyBoVQxrt+bWKu5KGw== +"@babel/plugin-transform-computed-properties@^7.17.12": + version "7.17.12" + resolved "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.17.12.tgz#bca616a83679698f3258e892ed422546e531387f" + integrity sha512-a7XINeplB5cQUWMg1E/GI1tFz3LfK021IjV1rj1ypE+R7jHm+pIHmHl25VNkZxtx9uuYp7ThGk8fur1HHG7PgQ== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.17.12" -"@babel/plugin-transform-destructuring@^7.12.1", "@babel/plugin-transform-destructuring@^7.16.7": - version "7.17.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.17.7.tgz#49dc2675a7afa9a5e4c6bdee636061136c3408d1" - integrity sha512-XVh0r5yq9sLR4vZ6eVZe8FKfIcSgaTBxVBRSYokRj2qksf6QerYnTxz9/GTuKTH/n/HwLP7t6gtlybHetJ/6hQ== +"@babel/plugin-transform-destructuring@^7.12.1", "@babel/plugin-transform-destructuring@^7.18.0": + version "7.18.0" + resolved "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.18.0.tgz#dc4f92587e291b4daa78aa20cc2d7a63aa11e858" + integrity sha512-Mo69klS79z6KEfrLg/1WkmVnB8javh75HX4pi2btjvlIoasuxilEyjtsQW6XPrubNd7AQy0MMaNIaQE4e7+PQw== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.17.12" "@babel/plugin-transform-dotall-regex@^7.16.7", "@babel/plugin-transform-dotall-regex@^7.4.4": version "7.16.7" @@ -684,12 +690,12 @@ "@babel/helper-create-regexp-features-plugin" "^7.16.7" "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-duplicate-keys@^7.16.7": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.16.7.tgz#2207e9ca8f82a0d36a5a67b6536e7ef8b08823c9" - integrity sha512-03DvpbRfvWIXyK0/6QiR1KMTWeT6OcQ7tbhjrXyFS02kjuX/mu5Bvnh5SDSWHxyawit2g5aWhKwI86EE7GUnTw== +"@babel/plugin-transform-duplicate-keys@^7.17.12": + version "7.17.12" + resolved "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.17.12.tgz#a09aa709a3310013f8e48e0e23bc7ace0f21477c" + integrity sha512-EA5eYFUG6xeerdabina/xIoB95jJ17mAkR8ivx6ZSu9frKShBjpOGZPn511MTDTkiCO+zXnzNczvUM69YSf3Zw== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.17.12" "@babel/plugin-transform-exponentiation-operator@^7.16.7": version "7.16.7" @@ -699,20 +705,20 @@ "@babel/helper-builder-binary-assignment-operator-visitor" "^7.16.7" "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-flow-strip-types@^7.16.7": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.16.7.tgz#291fb140c78dabbf87f2427e7c7c332b126964b8" - integrity sha512-mzmCq3cNsDpZZu9FADYYyfZJIOrSONmHcop2XEKPdBNMa4PDC4eEvcOvzZaCNcjKu72v0XQlA5y1g58aLRXdYg== +"@babel/plugin-transform-flow-strip-types@^7.17.12": + version "7.17.12" + resolved "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.17.12.tgz#5e070f99a4152194bd9275de140e83a92966cab3" + integrity sha512-g8cSNt+cHCpG/uunPQELdq/TeV3eg1OLJYwxypwHtAWo9+nErH3lQx9CSO2uI9lF74A0mR0t4KoMjs1snSgnTw== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" - "@babel/plugin-syntax-flow" "^7.16.7" + "@babel/helper-plugin-utils" "^7.17.12" + "@babel/plugin-syntax-flow" "^7.17.12" -"@babel/plugin-transform-for-of@^7.12.1", "@babel/plugin-transform-for-of@^7.16.7": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.16.7.tgz#649d639d4617dff502a9a158c479b3b556728d8c" - integrity sha512-/QZm9W92Ptpw7sjI9Nx1mbcsWz33+l8kuMIQnDwgQBG5s3fAfQvkRjQ7NqXhtNcKOnPkdICmUHyCaWW06HCsqg== +"@babel/plugin-transform-for-of@^7.12.1", "@babel/plugin-transform-for-of@^7.18.1": + version "7.18.1" + resolved "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.1.tgz#ed14b657e162b72afbbb2b4cdad277bf2bb32036" + integrity sha512-+TTB5XwvJ5hZbO8xvl2H4XaMDOAK57zF4miuC9qQJgysPNEAZZ9Z69rdF5LJkozGdZrjBIUAIyKUWRMmebI7vg== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.17.12" "@babel/plugin-transform-function-name@^7.16.7": version "7.16.7" @@ -723,12 +729,12 @@ "@babel/helper-function-name" "^7.16.7" "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-literals@^7.16.7": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.16.7.tgz#254c9618c5ff749e87cb0c0cef1a0a050c0bdab1" - integrity sha512-6tH8RTpTWI0s2sV6uq3e/C9wPo4PTqqZps4uF0kzQ9/xPLFQtipynvmT1g/dOfEJ+0EQsHhkQ/zyRId8J2b8zQ== +"@babel/plugin-transform-literals@^7.17.12": + version "7.17.12" + resolved "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.17.12.tgz#97131fbc6bbb261487105b4b3edbf9ebf9c830ae" + integrity sha512-8iRkvaTjJciWycPIZ9k9duu663FT7VrBdNqNgxnVXEFwOIp55JWcZd23VBRySYbnS3PwQ3rGiabJBBBGj5APmQ== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.17.12" "@babel/plugin-transform-member-expression-literals@^7.16.7": version "7.16.7" @@ -737,57 +743,58 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-modules-amd@^7.16.7": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.16.7.tgz#b28d323016a7daaae8609781d1f8c9da42b13186" - integrity sha512-KaaEtgBL7FKYwjJ/teH63oAmE3lP34N3kshz8mm4VMAw7U3PxjVwwUmxEFksbgsNUaO3wId9R2AVQYSEGRa2+g== +"@babel/plugin-transform-modules-amd@^7.18.0": + version "7.18.0" + resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.18.0.tgz#7ef1002e67e36da3155edc8bf1ac9398064c02ed" + integrity sha512-h8FjOlYmdZwl7Xm2Ug4iX2j7Qy63NANI+NQVWQzv6r25fqgg7k2dZl03p95kvqNclglHs4FZ+isv4p1uXMA+QA== dependencies: - "@babel/helper-module-transforms" "^7.16.7" - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-module-transforms" "^7.18.0" + "@babel/helper-plugin-utils" "^7.17.12" babel-plugin-dynamic-import-node "^2.3.3" -"@babel/plugin-transform-modules-commonjs@^7.16.8": - version "7.17.9" - resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.17.9.tgz#274be1a2087beec0254d4abd4d86e52442e1e5b6" - integrity sha512-2TBFd/r2I6VlYn0YRTz2JdazS+FoUuQ2rIFHoAxtyP/0G3D82SBLaRq9rnUkpqlLg03Byfl/+M32mpxjO6KaPw== +"@babel/plugin-transform-modules-commonjs@^7.18.2": + version "7.18.2" + resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.18.2.tgz#1aa8efa2e2a6e818b6a7f2235fceaf09bdb31e9e" + integrity sha512-f5A865gFPAJAEE0K7F/+nm5CmAE3y8AWlMBG9unu5j9+tk50UQVK0QS8RNxSp7MJf0wh97uYyLWt3Zvu71zyOQ== dependencies: - "@babel/helper-module-transforms" "^7.17.7" - "@babel/helper-plugin-utils" "^7.16.7" - "@babel/helper-simple-access" "^7.17.7" + "@babel/helper-module-transforms" "^7.18.0" + "@babel/helper-plugin-utils" "^7.17.12" + "@babel/helper-simple-access" "^7.18.2" babel-plugin-dynamic-import-node "^2.3.3" -"@babel/plugin-transform-modules-systemjs@^7.16.7": - version "7.17.8" - resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.17.8.tgz#81fd834024fae14ea78fbe34168b042f38703859" - integrity sha512-39reIkMTUVagzgA5x88zDYXPCMT6lcaRKs1+S9K6NKBPErbgO/w/kP8GlNQTC87b412ZTlmNgr3k2JrWgHH+Bw== +"@babel/plugin-transform-modules-systemjs@^7.18.0": + version "7.18.5" + resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.18.5.tgz#87f11c44fbfd3657be000d4897e192d9cb535996" + integrity sha512-SEewrhPpcqMF1V7DhnEbhVJLrC+nnYfe1E0piZMZXBpxi9WvZqWGwpsk7JYP7wPWeqaBh4gyKlBhHJu3uz5g4Q== dependencies: "@babel/helper-hoist-variables" "^7.16.7" - "@babel/helper-module-transforms" "^7.17.7" - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-module-transforms" "^7.18.0" + "@babel/helper-plugin-utils" "^7.17.12" "@babel/helper-validator-identifier" "^7.16.7" babel-plugin-dynamic-import-node "^2.3.3" -"@babel/plugin-transform-modules-umd@^7.16.7": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.16.7.tgz#23dad479fa585283dbd22215bff12719171e7618" - integrity sha512-EMh7uolsC8O4xhudF2F6wedbSHm1HHZ0C6aJ7K67zcDNidMzVcxWdGr+htW9n21klm+bOn+Rx4CBsAntZd3rEQ== +"@babel/plugin-transform-modules-umd@^7.18.0": + version "7.18.0" + resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.0.tgz#56aac64a2c2a1922341129a4597d1fd5c3ff020f" + integrity sha512-d/zZ8I3BWli1tmROLxXLc9A6YXvGK8egMxHp+E/rRwMh1Kip0AP77VwZae3snEJ33iiWwvNv2+UIIhfalqhzZA== dependencies: - "@babel/helper-module-transforms" "^7.16.7" - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-module-transforms" "^7.18.0" + "@babel/helper-plugin-utils" "^7.17.12" -"@babel/plugin-transform-named-capturing-groups-regex@^7.16.8": - version "7.16.8" - resolved "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.16.8.tgz#7f860e0e40d844a02c9dcf9d84965e7dfd666252" - integrity sha512-j3Jw+n5PvpmhRR+mrgIh04puSANCk/T/UA3m3P1MjJkhlK906+ApHhDIqBQDdOgL/r1UYpz4GNclTXxyZrYGSw== +"@babel/plugin-transform-named-capturing-groups-regex@^7.17.12": + version "7.17.12" + resolved "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.17.12.tgz#9c4a5a5966e0434d515f2675c227fd8cc8606931" + integrity sha512-vWoWFM5CKaTeHrdUJ/3SIOTRV+MBVGybOC9mhJkaprGNt5demMymDW24yC74avb915/mIRe3TgNb/d8idvnCRA== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.16.7" + "@babel/helper-create-regexp-features-plugin" "^7.17.12" + "@babel/helper-plugin-utils" "^7.17.12" -"@babel/plugin-transform-new-target@^7.16.7": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.16.7.tgz#9967d89a5c243818e0800fdad89db22c5f514244" - integrity sha512-xiLDzWNMfKoGOpc6t3U+etCE2yRnn3SM09BXqWPIZOBpL2gvVrBWUKnsJx0K/ADi5F5YC5f8APFfWrz25TdlGg== +"@babel/plugin-transform-new-target@^7.17.12": + version "7.18.5" + resolved "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.5.tgz#8c228c4a07501dd12c95c5f23d1622131cc23931" + integrity sha512-TuRL5uGW4KXU6OsRj+mLp9BM7pO8e7SGNTEokQRRxHFkXYMFiy2jlKSZPFtI/mKORDzciH+hneskcSOp0gU8hg== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.17.12" "@babel/plugin-transform-object-super@^7.16.7": version "7.16.7" @@ -797,12 +804,12 @@ "@babel/helper-plugin-utils" "^7.16.7" "@babel/helper-replace-supers" "^7.16.7" -"@babel/plugin-transform-parameters@^7.12.1", "@babel/plugin-transform-parameters@^7.16.7": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.16.7.tgz#a1721f55b99b736511cb7e0152f61f17688f331f" - integrity sha512-AT3MufQ7zZEhU2hwOA11axBnExW0Lszu4RL/tAlUJBuNoRak+wehQW8h6KcXOcgjY42fHtDxswuMhMjFEuv/aw== +"@babel/plugin-transform-parameters@^7.12.1", "@babel/plugin-transform-parameters@^7.17.12": + version "7.17.12" + resolved "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.17.12.tgz#eb467cd9586ff5ff115a9880d6fdbd4a846b7766" + integrity sha512-6qW4rWo1cyCdq1FkYri7AHpauchbGLXpdwnYsfxFb+KtddHENfsY5JZb35xUwkK5opOLcJ3BNd2l7PhRYGlwIA== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.17.12" "@babel/plugin-transform-property-literals@^7.16.7": version "7.16.7" @@ -825,38 +832,39 @@ dependencies: "@babel/plugin-transform-react-jsx" "^7.16.7" -"@babel/plugin-transform-react-jsx@^7.12.12", "@babel/plugin-transform-react-jsx@^7.16.7": - version "7.17.3" - resolved "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.17.3.tgz#eac1565da176ccb1a715dae0b4609858808008c1" - integrity sha512-9tjBm4O07f7mzKSIlEmPdiE6ub7kfIe6Cd+w+oQebpATfTQMAgW+YOuWxogbKVTulA+MEO7byMeIUtQ1z+z+ZQ== +"@babel/plugin-transform-react-jsx@^7.12.12", "@babel/plugin-transform-react-jsx@^7.16.7", "@babel/plugin-transform-react-jsx@^7.17.12": + version "7.17.12" + resolved "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.17.12.tgz#2aa20022709cd6a3f40b45d60603d5f269586dba" + integrity sha512-Lcaw8bxd1DKht3thfD4A12dqo1X16he1Lm8rIv8sTwjAYNInRS1qHa9aJoqvzpscItXvftKDCfaEQzwoVyXpEQ== dependencies: "@babel/helper-annotate-as-pure" "^7.16.7" "@babel/helper-module-imports" "^7.16.7" - "@babel/helper-plugin-utils" "^7.16.7" - "@babel/plugin-syntax-jsx" "^7.16.7" - "@babel/types" "^7.17.0" + "@babel/helper-plugin-utils" "^7.17.12" + "@babel/plugin-syntax-jsx" "^7.17.12" + "@babel/types" "^7.17.12" "@babel/plugin-transform-react-pure-annotations@^7.16.7": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.16.7.tgz#232bfd2f12eb551d6d7d01d13fe3f86b45eb9c67" - integrity sha512-hs71ToC97k3QWxswh2ElzMFABXHvGiJ01IB1TbYQDGeWRKWz/MPUTh5jGExdHvosYKpnJW5Pm3S4+TA3FyX+GA== + version "7.18.0" + resolved "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.18.0.tgz#ef82c8e310913f3522462c9ac967d395092f1954" + integrity sha512-6+0IK6ouvqDn9bmEG7mEyF/pwlJXVj5lwydybpyyH3D0A7Hftk+NCTdYjnLNZksn261xaOV5ksmp20pQEmc2RQ== dependencies: "@babel/helper-annotate-as-pure" "^7.16.7" - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.17.12" -"@babel/plugin-transform-regenerator@^7.16.7": - version "7.17.9" - resolved "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.17.9.tgz#0a33c3a61cf47f45ed3232903683a0afd2d3460c" - integrity sha512-Lc2TfbxR1HOyn/c6b4Y/b6NHoTb67n/IoWLxTu4kC7h4KQnWlhCq2S8Tx0t2SVvv5Uu87Hs+6JEJ5kt2tYGylQ== +"@babel/plugin-transform-regenerator@^7.18.0": + version "7.18.0" + resolved "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.18.0.tgz#44274d655eb3f1af3f3a574ba819d3f48caf99d5" + integrity sha512-C8YdRw9uzx25HSIzwA7EM7YP0FhCe5wNvJbZzjVNHHPGVcDJ3Aie+qGYYdS1oVQgn+B3eAIJbWFLrJ4Jipv7nw== dependencies: + "@babel/helper-plugin-utils" "^7.17.12" regenerator-transform "^0.15.0" -"@babel/plugin-transform-reserved-words@^7.16.7": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.16.7.tgz#1d798e078f7c5958eec952059c460b220a63f586" - integrity sha512-KQzzDnZ9hWQBjwi5lpY5v9shmm6IVG0U9pB18zvMu2i4H90xpT4gmqwPYsn8rObiadYe2M0gmgsiOIF5A/2rtg== +"@babel/plugin-transform-reserved-words@^7.17.12": + version "7.17.12" + resolved "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.17.12.tgz#7dbd349f3cdffba751e817cf40ca1386732f652f" + integrity sha512-1KYqwbJV3Co03NIi14uEHW8P50Md6KqFgt0FfpHdK6oyAHQVTosgPuPSiWud1HX0oYJ1hGRRlk0fP87jFpqXZA== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.17.12" "@babel/plugin-transform-shorthand-properties@^7.12.1", "@babel/plugin-transform-shorthand-properties@^7.16.7": version "7.16.7" @@ -865,12 +873,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-spread@^7.12.1", "@babel/plugin-transform-spread@^7.16.7": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.16.7.tgz#a303e2122f9f12e0105daeedd0f30fb197d8ff44" - integrity sha512-+pjJpgAngb53L0iaA5gU/1MLXJIfXcYepLgXB3esVRf4fqmj8f2cxM3/FKaHsZms08hFQJkFccEWuIpm429TXg== +"@babel/plugin-transform-spread@^7.12.1", "@babel/plugin-transform-spread@^7.17.12": + version "7.17.12" + resolved "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.17.12.tgz#c112cad3064299f03ea32afed1d659223935d1f5" + integrity sha512-9pgmuQAtFi3lpNUstvG9nGfk9DkrdmWNp9KeKPFmuZCpEnxRzYlS8JgwPjYj+1AWDOSvoGN0H30p1cBOmT/Svg== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.17.12" "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" "@babel/plugin-transform-sticky-regex@^7.16.7": @@ -880,28 +888,28 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-template-literals@^7.12.1", "@babel/plugin-transform-template-literals@^7.16.7": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.16.7.tgz#f3d1c45d28967c8e80f53666fc9c3e50618217ab" - integrity sha512-VwbkDDUeenlIjmfNeDX/V0aWrQH2QiVyJtwymVQSzItFDTpxfyJh3EVaQiS0rIN/CqbLGr0VcGmuwyTdZtdIsA== +"@babel/plugin-transform-template-literals@^7.12.1", "@babel/plugin-transform-template-literals@^7.18.2": + version "7.18.2" + resolved "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.2.tgz#31ed6915721864847c48b656281d0098ea1add28" + integrity sha512-/cmuBVw9sZBGZVOMkpAEaVLwm4JmK2GZ1dFKOGGpMzEHWFmyZZ59lUU0PdRr8YNYeQdNzTDwuxP2X2gzydTc9g== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.17.12" -"@babel/plugin-transform-typeof-symbol@^7.16.7": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.16.7.tgz#9cdbe622582c21368bd482b660ba87d5545d4f7e" - integrity sha512-p2rOixCKRJzpg9JB4gjnG4gjWkWa89ZoYUnl9snJ1cWIcTH/hvxZqfO+WjG6T8DRBpctEol5jw1O5rA8gkCokQ== +"@babel/plugin-transform-typeof-symbol@^7.17.12": + version "7.17.12" + resolved "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.17.12.tgz#0f12f57ac35e98b35b4ed34829948d42bd0e6889" + integrity sha512-Q8y+Jp7ZdtSPXCThB6zjQ74N3lj0f6TDh1Hnf5B+sYlzQ8i5Pjp8gW0My79iekSpT4WnI06blqP6DT0OmaXXmw== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.17.12" -"@babel/plugin-transform-typescript@^7.16.7": - version "7.16.8" - resolved "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.16.8.tgz#591ce9b6b83504903fa9dd3652c357c2ba7a1ee0" - integrity sha512-bHdQ9k7YpBDO2d0NVfkj51DpQcvwIzIusJ7mEUaMlbZq3Kt/U47j24inXZHQ5MDiYpCs+oZiwnXyKedE8+q7AQ== +"@babel/plugin-transform-typescript@^7.17.12": + version "7.18.4" + resolved "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.18.4.tgz#587eaf6a39edb8c06215e550dc939faeadd750bf" + integrity sha512-l4vHuSLUajptpHNEOUDEGsnpl9pfRLsN1XUoDQDD/YBuXTM+v37SHGS+c6n4jdcZy96QtuUuSvZYMLSSsjH8Mw== dependencies: - "@babel/helper-create-class-features-plugin" "^7.16.7" - "@babel/helper-plugin-utils" "^7.16.7" - "@babel/plugin-syntax-typescript" "^7.16.7" + "@babel/helper-create-class-features-plugin" "^7.18.0" + "@babel/helper-plugin-utils" "^7.17.12" + "@babel/plugin-syntax-typescript" "^7.17.12" "@babel/plugin-transform-unicode-escapes@^7.16.7": version "7.16.7" @@ -919,36 +927,37 @@ "@babel/helper-plugin-utils" "^7.16.7" "@babel/preset-env@^7.12.11": - version "7.16.11" - resolved "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.16.11.tgz#5dd88fd885fae36f88fd7c8342475c9f0abe2982" - integrity sha512-qcmWG8R7ZW6WBRPZK//y+E3Cli151B20W1Rv7ln27vuPaXU/8TKms6jFdiJtF7UDTxcrb7mZd88tAeK9LjdT8g== + version "7.18.2" + resolved "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.18.2.tgz#f47d3000a098617926e674c945d95a28cb90977a" + integrity sha512-PfpdxotV6afmXMU47S08F9ZKIm2bJIQ0YbAAtDfIENX7G1NUAXigLREh69CWDjtgUy7dYn7bsMzkgdtAlmS68Q== dependencies: - "@babel/compat-data" "^7.16.8" - "@babel/helper-compilation-targets" "^7.16.7" - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/compat-data" "^7.17.10" + "@babel/helper-compilation-targets" "^7.18.2" + "@babel/helper-plugin-utils" "^7.17.12" "@babel/helper-validator-option" "^7.16.7" - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.16.7" - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.16.7" - "@babel/plugin-proposal-async-generator-functions" "^7.16.8" - "@babel/plugin-proposal-class-properties" "^7.16.7" - "@babel/plugin-proposal-class-static-block" "^7.16.7" + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.17.12" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.17.12" + "@babel/plugin-proposal-async-generator-functions" "^7.17.12" + "@babel/plugin-proposal-class-properties" "^7.17.12" + "@babel/plugin-proposal-class-static-block" "^7.18.0" "@babel/plugin-proposal-dynamic-import" "^7.16.7" - "@babel/plugin-proposal-export-namespace-from" "^7.16.7" - "@babel/plugin-proposal-json-strings" "^7.16.7" - "@babel/plugin-proposal-logical-assignment-operators" "^7.16.7" - "@babel/plugin-proposal-nullish-coalescing-operator" "^7.16.7" + "@babel/plugin-proposal-export-namespace-from" "^7.17.12" + "@babel/plugin-proposal-json-strings" "^7.17.12" + "@babel/plugin-proposal-logical-assignment-operators" "^7.17.12" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.17.12" "@babel/plugin-proposal-numeric-separator" "^7.16.7" - "@babel/plugin-proposal-object-rest-spread" "^7.16.7" + "@babel/plugin-proposal-object-rest-spread" "^7.18.0" "@babel/plugin-proposal-optional-catch-binding" "^7.16.7" - "@babel/plugin-proposal-optional-chaining" "^7.16.7" - "@babel/plugin-proposal-private-methods" "^7.16.11" - "@babel/plugin-proposal-private-property-in-object" "^7.16.7" - "@babel/plugin-proposal-unicode-property-regex" "^7.16.7" + "@babel/plugin-proposal-optional-chaining" "^7.17.12" + "@babel/plugin-proposal-private-methods" "^7.17.12" + "@babel/plugin-proposal-private-property-in-object" "^7.17.12" + "@babel/plugin-proposal-unicode-property-regex" "^7.17.12" "@babel/plugin-syntax-async-generators" "^7.8.4" "@babel/plugin-syntax-class-properties" "^7.12.13" "@babel/plugin-syntax-class-static-block" "^7.14.5" "@babel/plugin-syntax-dynamic-import" "^7.8.3" "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + "@babel/plugin-syntax-import-assertions" "^7.17.12" "@babel/plugin-syntax-json-strings" "^7.8.3" "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" @@ -958,54 +967,54 @@ "@babel/plugin-syntax-optional-chaining" "^7.8.3" "@babel/plugin-syntax-private-property-in-object" "^7.14.5" "@babel/plugin-syntax-top-level-await" "^7.14.5" - "@babel/plugin-transform-arrow-functions" "^7.16.7" - "@babel/plugin-transform-async-to-generator" "^7.16.8" + "@babel/plugin-transform-arrow-functions" "^7.17.12" + "@babel/plugin-transform-async-to-generator" "^7.17.12" "@babel/plugin-transform-block-scoped-functions" "^7.16.7" - "@babel/plugin-transform-block-scoping" "^7.16.7" - "@babel/plugin-transform-classes" "^7.16.7" - "@babel/plugin-transform-computed-properties" "^7.16.7" - "@babel/plugin-transform-destructuring" "^7.16.7" + "@babel/plugin-transform-block-scoping" "^7.17.12" + "@babel/plugin-transform-classes" "^7.17.12" + "@babel/plugin-transform-computed-properties" "^7.17.12" + "@babel/plugin-transform-destructuring" "^7.18.0" "@babel/plugin-transform-dotall-regex" "^7.16.7" - "@babel/plugin-transform-duplicate-keys" "^7.16.7" + "@babel/plugin-transform-duplicate-keys" "^7.17.12" "@babel/plugin-transform-exponentiation-operator" "^7.16.7" - "@babel/plugin-transform-for-of" "^7.16.7" + "@babel/plugin-transform-for-of" "^7.18.1" "@babel/plugin-transform-function-name" "^7.16.7" - "@babel/plugin-transform-literals" "^7.16.7" + "@babel/plugin-transform-literals" "^7.17.12" "@babel/plugin-transform-member-expression-literals" "^7.16.7" - "@babel/plugin-transform-modules-amd" "^7.16.7" - "@babel/plugin-transform-modules-commonjs" "^7.16.8" - "@babel/plugin-transform-modules-systemjs" "^7.16.7" - "@babel/plugin-transform-modules-umd" "^7.16.7" - "@babel/plugin-transform-named-capturing-groups-regex" "^7.16.8" - "@babel/plugin-transform-new-target" "^7.16.7" + "@babel/plugin-transform-modules-amd" "^7.18.0" + "@babel/plugin-transform-modules-commonjs" "^7.18.2" + "@babel/plugin-transform-modules-systemjs" "^7.18.0" + "@babel/plugin-transform-modules-umd" "^7.18.0" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.17.12" + "@babel/plugin-transform-new-target" "^7.17.12" "@babel/plugin-transform-object-super" "^7.16.7" - "@babel/plugin-transform-parameters" "^7.16.7" + "@babel/plugin-transform-parameters" "^7.17.12" "@babel/plugin-transform-property-literals" "^7.16.7" - "@babel/plugin-transform-regenerator" "^7.16.7" - "@babel/plugin-transform-reserved-words" "^7.16.7" + "@babel/plugin-transform-regenerator" "^7.18.0" + "@babel/plugin-transform-reserved-words" "^7.17.12" "@babel/plugin-transform-shorthand-properties" "^7.16.7" - "@babel/plugin-transform-spread" "^7.16.7" + "@babel/plugin-transform-spread" "^7.17.12" "@babel/plugin-transform-sticky-regex" "^7.16.7" - "@babel/plugin-transform-template-literals" "^7.16.7" - "@babel/plugin-transform-typeof-symbol" "^7.16.7" + "@babel/plugin-transform-template-literals" "^7.18.2" + "@babel/plugin-transform-typeof-symbol" "^7.17.12" "@babel/plugin-transform-unicode-escapes" "^7.16.7" "@babel/plugin-transform-unicode-regex" "^7.16.7" "@babel/preset-modules" "^0.1.5" - "@babel/types" "^7.16.8" + "@babel/types" "^7.18.2" babel-plugin-polyfill-corejs2 "^0.3.0" babel-plugin-polyfill-corejs3 "^0.5.0" babel-plugin-polyfill-regenerator "^0.3.0" - core-js-compat "^3.20.2" + core-js-compat "^3.22.1" semver "^6.3.0" "@babel/preset-flow@^7.12.1": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/preset-flow/-/preset-flow-7.16.7.tgz#7fd831323ab25eeba6e4b77a589f680e30581cbd" - integrity sha512-6ceP7IyZdUYQ3wUVqyRSQXztd1YmFHWI4Xv11MIqAlE4WqxBSd/FZ61V9k+TS5Gd4mkHOtQtPp9ymRpxH4y1Ug== + version "7.17.12" + resolved "https://registry.npmjs.org/@babel/preset-flow/-/preset-flow-7.17.12.tgz#664a5df59190260939eee862800a255bef3bd66f" + integrity sha512-7QDz7k4uiaBdu7N89VKjUn807pJRXmdirQu0KyR9LXnQrr5Jt41eIMKTS7ljej+H29erwmMrwq9Io9mJHLI3Lw== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.17.12" "@babel/helper-validator-option" "^7.16.7" - "@babel/plugin-transform-flow-strip-types" "^7.16.7" + "@babel/plugin-transform-flow-strip-types" "^7.17.12" "@babel/preset-modules@^0.1.5": version "0.1.5" @@ -1019,25 +1028,25 @@ esutils "^2.0.2" "@babel/preset-react@^7.12.10": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.16.7.tgz#4c18150491edc69c183ff818f9f2aecbe5d93852" - integrity sha512-fWpyI8UM/HE6DfPBzD8LnhQ/OcH8AgTaqcqP2nGOXEUV+VKBR5JRN9hCk9ai+zQQ57vtm9oWeXguBCPNUjytgA== + version "7.17.12" + resolved "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.17.12.tgz#62adbd2d1870c0de3893095757ed5b00b492ab3d" + integrity sha512-h5U+rwreXtZaRBEQhW1hOJLMq8XNJBQ/9oymXiCXTuT/0uOwpbT0gUt+sXeOqoXBgNuUKI7TaObVwoEyWkpFgA== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.17.12" "@babel/helper-validator-option" "^7.16.7" "@babel/plugin-transform-react-display-name" "^7.16.7" - "@babel/plugin-transform-react-jsx" "^7.16.7" + "@babel/plugin-transform-react-jsx" "^7.17.12" "@babel/plugin-transform-react-jsx-development" "^7.16.7" "@babel/plugin-transform-react-pure-annotations" "^7.16.7" "@babel/preset-typescript@^7.12.7": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.16.7.tgz#ab114d68bb2020afc069cd51b37ff98a046a70b9" - integrity sha512-WbVEmgXdIyvzB77AQjGBEyYPZx+8tTsO50XtfozQrkW8QB2rLJpH2lgx0TRw5EJrBxOZQ+wCcyPVQvS8tjEHpQ== + version "7.17.12" + resolved "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.17.12.tgz#40269e0a0084d56fc5731b6c40febe1c9a4a3e8c" + integrity sha512-S1ViF8W2QwAKUGJXxP9NAfNaqGDdEBJKpYkxHf5Yy2C4NPPzXGeR3Lhk7G8xJaaLcFTRfNjVbtbVtm8Gb0mqvg== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.17.12" "@babel/helper-validator-option" "^7.16.7" - "@babel/plugin-transform-typescript" "^7.16.7" + "@babel/plugin-transform-typescript" "^7.17.12" "@babel/register@^7.12.1": version "7.17.7" @@ -1050,10 +1059,10 @@ pirates "^4.0.5" source-map-support "^0.5.16" -"@babel/runtime@^7.0.0", "@babel/runtime@^7.10.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.14.8", "@babel/runtime@^7.17.8", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4": - version "7.17.9" - resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.9.tgz#d19fbf802d01a8cb6cf053a64e472d42c434ba72" - integrity sha512-lSiBBvodq29uShpWGNbgFdKYNiFDo5/HIYsaCEY9ff4sb10x9jizo2+pRrSyF4jKZCXqgzuqBOQKbUm90gQwJg== +"@babel/runtime@^7.0.0", "@babel/runtime@^7.12.5", "@babel/runtime@^7.17.8", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.0", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4": + version "7.18.3" + resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.3.tgz#c7b654b57f6f63cf7f8b418ac9ca04408c4579f4" + integrity sha512-38Y8f7YUhce/K7RMwTp7m0uCumpv9hZkitCbBClqQIow1qSbCvGkcegKOXpEWCQLfWmevgRiWokZ1GkpfhbZug== dependencies: regenerator-runtime "^0.13.4" @@ -1066,30 +1075,35 @@ "@babel/parser" "^7.16.7" "@babel/types" "^7.16.7" -"@babel/traverse@^7.1.6", "@babel/traverse@^7.12.11", "@babel/traverse@^7.12.9", "@babel/traverse@^7.13.0", "@babel/traverse@^7.16.7", "@babel/traverse@^7.16.8", "@babel/traverse@^7.17.3", "@babel/traverse@^7.17.9": - version "7.17.9" - resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.9.tgz#1f9b207435d9ae4a8ed6998b2b82300d83c37a0d" - integrity sha512-PQO8sDIJ8SIwipTPiR71kJQCKQYB5NGImbOviK8K+kg5xkNSYXLBupuX9QhatFowrsvo9Hj8WgArg3W7ijNAQw== +"@babel/traverse@^7.1.6", "@babel/traverse@^7.12.11", "@babel/traverse@^7.12.9", "@babel/traverse@^7.13.0", "@babel/traverse@^7.16.8", "@babel/traverse@^7.18.0", "@babel/traverse@^7.18.2", "@babel/traverse@^7.18.5": + version "7.18.5" + resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.5.tgz#94a8195ad9642801837988ab77f36e992d9a20cd" + integrity sha512-aKXj1KT66sBj0vVzk6rEeAO6Z9aiiQ68wfDgge3nHhA/my6xMM/7HGQUNumKZaoa2qUPQ5whJG9aAifsxUKfLA== dependencies: "@babel/code-frame" "^7.16.7" - "@babel/generator" "^7.17.9" - "@babel/helper-environment-visitor" "^7.16.7" + "@babel/generator" "^7.18.2" + "@babel/helper-environment-visitor" "^7.18.2" "@babel/helper-function-name" "^7.17.9" "@babel/helper-hoist-variables" "^7.16.7" "@babel/helper-split-export-declaration" "^7.16.7" - "@babel/parser" "^7.17.9" - "@babel/types" "^7.17.0" + "@babel/parser" "^7.18.5" + "@babel/types" "^7.18.4" debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.12.11", "@babel/types@^7.12.7", "@babel/types@^7.16.0", "@babel/types@^7.16.7", "@babel/types@^7.16.8", "@babel/types@^7.17.0", "@babel/types@^7.2.0", "@babel/types@^7.4.4": - version "7.17.0" - resolved "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz#a826e368bccb6b3d84acd76acad5c0d87342390b" - integrity sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw== +"@babel/types@^7.12.11", "@babel/types@^7.12.7", "@babel/types@^7.16.0", "@babel/types@^7.16.7", "@babel/types@^7.16.8", "@babel/types@^7.17.0", "@babel/types@^7.17.12", "@babel/types@^7.18.0", "@babel/types@^7.18.2", "@babel/types@^7.18.4", "@babel/types@^7.2.0", "@babel/types@^7.4.4": + version "7.18.4" + resolved "https://registry.npmjs.org/@babel/types/-/types-7.18.4.tgz#27eae9b9fd18e9dccc3f9d6ad051336f307be354" + integrity sha512-ThN1mBcMq5pG/Vm2IcBmPPfyPXbd8S02rS+OBIDENdufvqC7Z/jHPCv9IcP01277aKtDI8g/2XysBN4hA8niiw== dependencies: "@babel/helper-validator-identifier" "^7.16.7" to-fast-properties "^2.0.0" +"@base2/pretty-print-object@1.0.1": + version "1.0.1" + resolved "https://registry.npmjs.org/@base2/pretty-print-object/-/pretty-print-object-1.0.1.tgz#371ba8be66d556812dc7fb169ebc3c08378f69d4" + integrity sha512-4iri8i1AqYHJE2DstZYkyEprg6Pq6sKx3xn5FpySk9sNhH7qN2LLlHJCfDTZRILNwQNPD7mATWM0TBui7uC1pA== + "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" @@ -1105,132 +1119,60 @@ resolved "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== -"@emotion/cache@^10.0.27": - version "10.0.29" - resolved "https://registry.npmjs.org/@emotion/cache/-/cache-10.0.29.tgz#87e7e64f412c060102d589fe7c6dc042e6f9d1e0" - integrity sha512-fU2VtSVlHiF27empSbxi1O2JFdNWZO+2NFHfwO0pxgTep6Xa3uGb+3pVKfLww2l/IBGLNEZl5Xf/++A4wAYDYQ== - dependencies: - "@emotion/sheet" "0.9.4" - "@emotion/stylis" "0.8.5" - "@emotion/utils" "0.11.3" - "@emotion/weak-memoize" "0.2.5" - -"@emotion/core@^10.1.1": - version "10.3.1" - resolved "https://registry.npmjs.org/@emotion/core/-/core-10.3.1.tgz#4021b6d8b33b3304d48b0bb478485e7d7421c69d" - integrity sha512-447aUEjPIm0MnE6QYIaFz9VQOHSXf4Iu6EWOIqq11EAPqinkSZmfymPTmlOE3QjLv846lH4JVZBUOtwGbuQoww== - dependencies: - "@babel/runtime" "^7.5.5" - "@emotion/cache" "^10.0.27" - "@emotion/css" "^10.0.27" - "@emotion/serialize" "^0.11.15" - "@emotion/sheet" "0.9.4" - "@emotion/utils" "0.11.3" - -"@emotion/css@^10.0.27": - version "10.0.27" - resolved "https://registry.npmjs.org/@emotion/css/-/css-10.0.27.tgz#3a7458198fbbebb53b01b2b87f64e5e21241e14c" - integrity sha512-6wZjsvYeBhyZQYNrGoR5yPMYbMBNEnanDrqmsqS1mzDm1cOTu12shvl2j4QHNS36UaTE0USIJawCH9C8oW34Zw== - dependencies: - "@emotion/serialize" "^0.11.15" - "@emotion/utils" "0.11.3" - babel-plugin-emotion "^10.0.27" - -"@emotion/hash@0.8.0": - version "0.8.0" - resolved "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz#bbbff68978fefdbe68ccb533bc8cbe1d1afb5413" - integrity sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow== - -"@emotion/is-prop-valid@0.8.8", "@emotion/is-prop-valid@^0.8.6": - version "0.8.8" - resolved "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz#db28b1c4368a259b60a97311d6a952d4fd01ac1a" - integrity sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA== - dependencies: - "@emotion/memoize" "0.7.4" - -"@emotion/memoize@0.7.4": - version "0.7.4" - resolved "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz#19bf0f5af19149111c40d98bb0cf82119f5d9eeb" - integrity sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw== - -"@emotion/serialize@^0.11.15", "@emotion/serialize@^0.11.16": - version "0.11.16" - resolved "https://registry.npmjs.org/@emotion/serialize/-/serialize-0.11.16.tgz#dee05f9e96ad2fb25a5206b6d759b2d1ed3379ad" - integrity sha512-G3J4o8by0VRrO+PFeSc3js2myYNOXVJ3Ya+RGVxnshRYgsvErfAOglKAiy1Eo1vhzxqtUvjCyS5gtewzkmvSSg== - dependencies: - "@emotion/hash" "0.8.0" - "@emotion/memoize" "0.7.4" - "@emotion/unitless" "0.7.5" - "@emotion/utils" "0.11.3" - csstype "^2.5.7" - -"@emotion/sheet@0.9.4": - version "0.9.4" - resolved "https://registry.npmjs.org/@emotion/sheet/-/sheet-0.9.4.tgz#894374bea39ec30f489bbfc3438192b9774d32e5" - integrity sha512-zM9PFmgVSqBw4zL101Q0HrBVTGmpAxFZH/pYx/cjJT5advXguvcgjHFTCaIO3enL/xr89vK2bh0Mfyj9aa0ANA== - -"@emotion/styled-base@^10.3.0": - version "10.3.0" - resolved "https://registry.npmjs.org/@emotion/styled-base/-/styled-base-10.3.0.tgz#9aa2c946100f78b47316e4bc6048321afa6d4e36" - integrity sha512-PBRqsVKR7QRNkmfH78hTSSwHWcwDpecH9W6heujWAcyp2wdz/64PP73s7fWS1dIPm8/Exc8JAzYS8dEWXjv60w== - dependencies: - "@babel/runtime" "^7.5.5" - "@emotion/is-prop-valid" "0.8.8" - "@emotion/serialize" "^0.11.15" - "@emotion/utils" "0.11.3" - -"@emotion/styled@^10.0.27": - version "10.3.0" - resolved "https://registry.npmjs.org/@emotion/styled/-/styled-10.3.0.tgz#8ee959bf75730789abb5f67f7c3ded0c30aec876" - integrity sha512-GgcUpXBBEU5ido+/p/mCT2/Xx+Oqmp9JzQRuC+a4lYM4i4LBBn/dWvc0rQ19N9ObA8/T4NWMrPNe79kMBDJqoQ== - dependencies: - "@emotion/styled-base" "^10.3.0" - babel-plugin-emotion "^10.0.27" - -"@emotion/stylis@0.8.5": - version "0.8.5" - resolved "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz#deacb389bd6ee77d1e7fcaccce9e16c5c7e78e04" - integrity sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ== - -"@emotion/unitless@0.7.5": - version "0.7.5" - resolved "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz#77211291c1900a700b8a78cfafda3160d76949ed" - integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg== - -"@emotion/utils@0.11.3": - version "0.11.3" - resolved "https://registry.npmjs.org/@emotion/utils/-/utils-0.11.3.tgz#a759863867befa7e583400d322652a3f44820924" - integrity sha512-0o4l6pZC+hI88+bzuaX/6BgOvQVhbt2PfmxauVaYOGgbsAw14wdKyvMCZXnsnsHys94iadcF+RG/wZyx6+ZZBw== - -"@emotion/weak-memoize@0.2.5": - version "0.2.5" - resolved "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz#8eed982e2ee6f7f4e44c253e12962980791efd46" - integrity sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA== - "@gar/promisify@^1.0.1": version "1.1.3" resolved "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6" integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw== -"@istanbuljs/schema@^0.1.2": +"@istanbuljs/schema@^0.1.2", "@istanbuljs/schema@^0.1.3": version "0.1.3" resolved "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== +"@jridgewell/gen-mapping@^0.1.0": + version "0.1.1" + resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz#e5d2e450306a9491e3bd77e323e38d7aff315996" + integrity sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w== + dependencies: + "@jridgewell/set-array" "^1.0.0" + "@jridgewell/sourcemap-codec" "^1.4.10" + +"@jridgewell/gen-mapping@^0.3.0": + version "0.3.1" + resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.1.tgz#cf92a983c83466b8c0ce9124fadeaf09f7c66ea9" + integrity sha512-GcHwniMlA2z+WFPWuY8lp3fsza0I8xPFMWL5+n8LYyP6PSvPrXf4+n8stDHZY2DM0zy9sVkRDy1jDI4XGzYVqg== + dependencies: + "@jridgewell/set-array" "^1.0.0" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.9" + "@jridgewell/resolve-uri@^3.0.3": - version "3.0.5" - resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz#68eb521368db76d040a6315cdb24bf2483037b9c" - integrity sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew== + version "3.0.7" + resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.7.tgz#30cd49820a962aff48c8fffc5cd760151fca61fe" + integrity sha512-8cXDaBBHOr2pQ7j77Y6Vp5VDT2sIqWyWQ56TjEq4ih/a4iST3dItRe8Q9fp0rrIl9DoKhWQtUQz/YpOxLkXbNA== + +"@jridgewell/set-array@^1.0.0": + version "1.1.1" + resolved "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.1.tgz#36a6acc93987adcf0ba50c66908bd0b70de8afea" + integrity sha512-Ct5MqZkLGEXTVmQYbGtx9SVqD2fqwvdubdps5D3djjAkgkKwT918VNOz65pEHFaYTeWcukmJmH5SwsA9Tn2ObQ== + +"@jridgewell/source-map@^0.3.2": + version "0.3.2" + resolved "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz#f45351aaed4527a298512ec72f81040c998580fb" + integrity sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw== + dependencies: + "@jridgewell/gen-mapping" "^0.3.0" + "@jridgewell/trace-mapping" "^0.3.9" "@jridgewell/sourcemap-codec@^1.4.10": - version "1.4.11" - resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz#771a1d8d744eeb71b6adb35808e1a6c7b9b8c8ec" - integrity sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg== + version "1.4.13" + resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.13.tgz#b6461fb0c2964356c469e115f504c95ad97ab88c" + integrity sha512-GryiOJmNcWbovBxTfZSF71V/mXbgcV3MewDe3kIMCLyIh5e7SKAeUZs+rMnJ8jkMolZ/4/VsdBmMrw3l+VdZ3w== -"@jridgewell/trace-mapping@^0.3.0": - version "0.3.4" - resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz#f6a0832dffd5b8a6aaa633b7d9f8e8e94c83a0c3" - integrity sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ== +"@jridgewell/trace-mapping@^0.3.7", "@jridgewell/trace-mapping@^0.3.9": + version "0.3.13" + resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.13.tgz#dcfe3e95f224c8fe97a87a5235defec999aa92ea" + integrity sha512-o1xbKhp9qnIAoHJSWd6KlCZfqslL4valSF81H8ImioOAxluWYWOpWkpyktY2vnt4tbrX9XYaxovq6cgowaJp2w== dependencies: "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" @@ -1315,10 +1257,10 @@ mkdirp "^1.0.4" rimraf "^3.0.2" -"@pmmmwh/react-refresh-webpack-plugin@^0.5.1": - version "0.5.5" - resolved "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.5.tgz#e77aac783bd079f548daa0a7f080ab5b5a9741ca" - integrity sha512-RbG7h6TuP6nFFYKJwbcToA1rjC1FyPg25NR2noAZ0vKI+la01KTSRPkuVPE+U88jXv7javx2JHglUcL1MHcshQ== +"@pmmmwh/react-refresh-webpack-plugin@^0.5.3": + version "0.5.7" + resolved "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.7.tgz#58f8217ba70069cc6a73f5d7e05e85b458c150e2" + integrity sha512-bcKCAzF0DV2IIROp9ZHkRJa6O4jy7NlnHdWL3GmcUxYWNjLXkK5kfELELwEfSP5hXPfVL/qOGMAROuMQb9GG8Q== dependencies: ansi-html-community "^0.0.8" common-path-prefix "^3.0.0" @@ -1330,24 +1272,19 @@ schema-utils "^3.0.0" source-map "^0.7.3" -"@popperjs/core@^2.5.4", "@popperjs/core@^2.6.0": - version "2.11.5" - resolved "https://registry.npmjs.org/@popperjs/core/-/core-2.11.5.tgz#db5a11bf66bdab39569719555b0f76e138d7bd64" - integrity sha512-9X2obfABZuDVLCgPK9aX0a/x4jaOEweTTWE2+9sr0Qqqevj2Uv5XorvusThmc9XGYpS9yI+fhh8RTafBtGposw== - "@storybook/addon-a11y@^6.4.21": - version "6.4.22" - resolved "https://registry.npmjs.org/@storybook/addon-a11y/-/addon-a11y-6.4.22.tgz#df75f1a82c83973c165984e8b0944ceed64c30e9" - integrity sha512-y125LDx5VR6JmiHB6/0RHWudwhe9QcFXqoAqGqWIj4zRv0kb9AyDPDtWvtDOSImCDXIPRmd8P05xTOnYH0ET3w== + version "6.5.9" + resolved "https://registry.npmjs.org/@storybook/addon-a11y/-/addon-a11y-6.5.9.tgz#191fb8ea9be4feee67fd11553b98ab56ecfc1295" + integrity sha512-jRiuJ2xlN8quVq2lOqpxqyuwAj8xLcgVBPy+Mf220u7AZmmbS/0sONyHKROfEBjJoHQAQYqn2vSAeuQZuTWyVA== dependencies: - "@storybook/addons" "6.4.22" - "@storybook/api" "6.4.22" - "@storybook/channels" "6.4.22" - "@storybook/client-logger" "6.4.22" - "@storybook/components" "6.4.22" - "@storybook/core-events" "6.4.22" - "@storybook/csf" "0.0.2--canary.87bc651.0" - "@storybook/theming" "6.4.22" + "@storybook/addons" "6.5.9" + "@storybook/api" "6.5.9" + "@storybook/channels" "6.5.9" + "@storybook/client-logger" "6.5.9" + "@storybook/components" "6.5.9" + "@storybook/core-events" "6.5.9" + "@storybook/csf" "0.0.2--canary.4566f4d.1" + "@storybook/theming" "6.5.9" axe-core "^4.2.0" core-js "^3.8.2" global "^4.4.0" @@ -1358,57 +1295,58 @@ util-deprecate "^1.0.2" "@storybook/addon-actions@^6.4.21": - version "6.4.22" - resolved "https://registry.npmjs.org/@storybook/addon-actions/-/addon-actions-6.4.22.tgz#ec1b4332e76a8021dc0a1375dfd71a0760457588" - integrity sha512-t2w3iLXFul+R/1ekYxIEzUOZZmvEa7EzUAVAuCHP4i6x0jBnTTZ7sAIUVRaxVREPguH5IqI/2OklYhKanty2Yw== + version "6.5.9" + resolved "https://registry.npmjs.org/@storybook/addon-actions/-/addon-actions-6.5.9.tgz#d50d65631403e1a5b680961429d9c0d7bd383e68" + integrity sha512-wDYm3M1bN+zcYZV3Q24M03b/P8DDpvj1oSoY6VLlxDAi56h8qZB/voeIS2I6vWXOB79C5tbwljYNQO0GsufS0g== dependencies: - "@storybook/addons" "6.4.22" - "@storybook/api" "6.4.22" - "@storybook/components" "6.4.22" - "@storybook/core-events" "6.4.22" - "@storybook/csf" "0.0.2--canary.87bc651.0" - "@storybook/theming" "6.4.22" + "@storybook/addons" "6.5.9" + "@storybook/api" "6.5.9" + "@storybook/client-logger" "6.5.9" + "@storybook/components" "6.5.9" + "@storybook/core-events" "6.5.9" + "@storybook/csf" "0.0.2--canary.4566f4d.1" + "@storybook/theming" "6.5.9" core-js "^3.8.2" fast-deep-equal "^3.1.3" global "^4.4.0" lodash "^4.17.21" - polished "^4.0.5" + polished "^4.2.2" prop-types "^15.7.2" react-inspector "^5.1.0" regenerator-runtime "^0.13.7" - telejson "^5.3.2" + telejson "^6.0.8" ts-dedent "^2.0.0" util-deprecate "^1.0.2" uuid-browser "^3.1.0" "@storybook/addon-controls@^6.4.22": - version "6.4.22" - resolved "https://registry.npmjs.org/@storybook/addon-controls/-/addon-controls-6.4.22.tgz#42c7f426eb7ba6d335e8e14369d6d13401878665" - integrity sha512-f/M/W+7UTEUnr/L6scBMvksq+ZA8GTfh3bomE5FtWyOyaFppq9k8daKAvdYNlzXAOrUUsoZVJDgpb20Z2VBiSQ== + version "6.5.9" + resolved "https://registry.npmjs.org/@storybook/addon-controls/-/addon-controls-6.5.9.tgz#8f6ef939c87b3dbad98f8bda7e124f0b34f668d2" + integrity sha512-VvjkgK32bGURKyWU2No6Q2B0RQZjLZk8D3neVNCnrWxwrl1G82StegxjRPn/UZm9+MZVPvTvI46nj1VdgOktnw== dependencies: - "@storybook/addons" "6.4.22" - "@storybook/api" "6.4.22" - "@storybook/client-logger" "6.4.22" - "@storybook/components" "6.4.22" - "@storybook/core-common" "6.4.22" - "@storybook/csf" "0.0.2--canary.87bc651.0" - "@storybook/node-logger" "6.4.22" - "@storybook/store" "6.4.22" - "@storybook/theming" "6.4.22" + "@storybook/addons" "6.5.9" + "@storybook/api" "6.5.9" + "@storybook/client-logger" "6.5.9" + "@storybook/components" "6.5.9" + "@storybook/core-common" "6.5.9" + "@storybook/csf" "0.0.2--canary.4566f4d.1" + "@storybook/node-logger" "6.5.9" + "@storybook/store" "6.5.9" + "@storybook/theming" "6.5.9" core-js "^3.8.2" lodash "^4.17.21" ts-dedent "^2.0.0" "@storybook/addon-links@^6.4.21": - version "6.4.22" - resolved "https://registry.npmjs.org/@storybook/addon-links/-/addon-links-6.4.22.tgz#c0ed9e9ef6505cf1562e1476bbc5064c82dadbe2" - integrity sha512-OSOyDnTXnmcplJHlXTYUTMkrfpLqxtHp2R69IXfAyI1e8WNDb79mXflrEXDA/RSNEliLkqYwCyYby7gDMGds5Q== + version "6.5.9" + resolved "https://registry.npmjs.org/@storybook/addon-links/-/addon-links-6.5.9.tgz#91cbca0c044796badf2498723fdd10dacea5748b" + integrity sha512-4BYC7pkxL3NLRnEgTA9jpIkObQKril+XFj1WtmY/lngF90vvK0Kc/TtvTA2/5tSgrHfxEuPevIdxMIyLJ4ejWQ== dependencies: - "@storybook/addons" "6.4.22" - "@storybook/client-logger" "6.4.22" - "@storybook/core-events" "6.4.22" - "@storybook/csf" "0.0.2--canary.87bc651.0" - "@storybook/router" "6.4.22" + "@storybook/addons" "6.5.9" + "@storybook/client-logger" "6.5.9" + "@storybook/core-events" "6.5.9" + "@storybook/csf" "0.0.2--canary.4566f4d.1" + "@storybook/router" "6.5.9" "@types/qs" "^6.9.5" core-js "^3.8.2" global "^4.4.0" @@ -1418,54 +1356,53 @@ ts-dedent "^2.0.0" "@storybook/addon-storysource@^6.4.21": - version "6.4.22" - resolved "https://registry.npmjs.org/@storybook/addon-storysource/-/addon-storysource-6.4.22.tgz#c2fc80e90476495ecacf74986e3cf81f25379f53" - integrity sha512-BFZ+/+CytET1CQzRe5qbg6Ca7pUKHa7C+pMQkq8M+tFG1f0Ov2Np5OvqXmZQcJeOpK1HDF0EaJv2ESg0CH8ytw== + version "6.5.9" + resolved "https://registry.npmjs.org/@storybook/addon-storysource/-/addon-storysource-6.5.9.tgz#8e5fb76b9314a69831a3b5e08e8570407c39de75" + integrity sha512-7KLw03mE1JJSJQrqNDftoVVp2BBq8wltd0qo7WHkpRVfir9dMO3g7Wy6S6UPsrb9th47ZjonwBJEc28GwAq0yg== dependencies: - "@storybook/addons" "6.4.22" - "@storybook/api" "6.4.22" - "@storybook/client-logger" "6.4.22" - "@storybook/components" "6.4.22" - "@storybook/router" "6.4.22" - "@storybook/source-loader" "6.4.22" - "@storybook/theming" "6.4.22" + "@storybook/addons" "6.5.9" + "@storybook/api" "6.5.9" + "@storybook/client-logger" "6.5.9" + "@storybook/components" "6.5.9" + "@storybook/router" "6.5.9" + "@storybook/source-loader" "6.5.9" + "@storybook/theming" "6.5.9" core-js "^3.8.2" estraverse "^5.2.0" loader-utils "^2.0.0" - prettier ">=2.2.1 <=2.3.0" prop-types "^15.7.2" - react-syntax-highlighter "^13.5.3" + react-syntax-highlighter "^15.4.5" regenerator-runtime "^0.13.7" -"@storybook/addons@6.4.22", "@storybook/addons@^6.4.21": - version "6.4.22" - resolved "https://registry.npmjs.org/@storybook/addons/-/addons-6.4.22.tgz#e165407ca132c2182de2d466b7ff7c5644b6ad7b" - integrity sha512-P/R+Jsxh7pawKLYo8MtE3QU/ilRFKbtCewV/T1o5U/gm8v7hKQdFz3YdRMAra4QuCY8bQIp7MKd2HrB5aH5a1A== +"@storybook/addons@6.5.9", "@storybook/addons@^6.4.21": + version "6.5.9" + resolved "https://registry.npmjs.org/@storybook/addons/-/addons-6.5.9.tgz#5a9d7395c579a9cbc44dfc122362fb3c95dfb9d5" + integrity sha512-adwdiXg+mntfPocLc1KXjZXyLgGk7Aac699Fwe+OUYPEC5tW347Rm/kFatcE556d42o5czcRiq3ZSIGWnm9ieQ== dependencies: - "@storybook/api" "6.4.22" - "@storybook/channels" "6.4.22" - "@storybook/client-logger" "6.4.22" - "@storybook/core-events" "6.4.22" - "@storybook/csf" "0.0.2--canary.87bc651.0" - "@storybook/router" "6.4.22" - "@storybook/theming" "6.4.22" + "@storybook/api" "6.5.9" + "@storybook/channels" "6.5.9" + "@storybook/client-logger" "6.5.9" + "@storybook/core-events" "6.5.9" + "@storybook/csf" "0.0.2--canary.4566f4d.1" + "@storybook/router" "6.5.9" + "@storybook/theming" "6.5.9" "@types/webpack-env" "^1.16.0" core-js "^3.8.2" global "^4.4.0" regenerator-runtime "^0.13.7" -"@storybook/api@6.4.22": - version "6.4.22" - resolved "https://registry.npmjs.org/@storybook/api/-/api-6.4.22.tgz#d63f7ad3ffdd74af01ae35099bff4c39702cf793" - integrity sha512-lAVI3o2hKupYHXFTt+1nqFct942up5dHH6YD7SZZJGyW21dwKC3HK1IzCsTawq3fZAKkgWFgmOO649hKk60yKg== +"@storybook/api@6.5.9": + version "6.5.9" + resolved "https://registry.npmjs.org/@storybook/api/-/api-6.5.9.tgz#303733214c9de0422d162f7c54ae05d088b89bf9" + integrity sha512-9ylztnty4Y+ALU/ehW3BML9czjCAFsWvrwuCi6UgcwNjswwjSX3VRLhfD1KT3pl16ho//95LgZ0LnSwROCcPOA== dependencies: - "@storybook/channels" "6.4.22" - "@storybook/client-logger" "6.4.22" - "@storybook/core-events" "6.4.22" - "@storybook/csf" "0.0.2--canary.87bc651.0" - "@storybook/router" "6.4.22" + "@storybook/channels" "6.5.9" + "@storybook/client-logger" "6.5.9" + "@storybook/core-events" "6.5.9" + "@storybook/csf" "0.0.2--canary.4566f4d.1" + "@storybook/router" "6.5.9" "@storybook/semver" "^7.3.2" - "@storybook/theming" "6.4.22" + "@storybook/theming" "6.5.9" core-js "^3.8.2" fast-deep-equal "^3.1.3" global "^4.4.0" @@ -1473,58 +1410,36 @@ memoizerific "^1.11.3" regenerator-runtime "^0.13.7" store2 "^2.12.0" - telejson "^5.3.2" + telejson "^6.0.8" ts-dedent "^2.0.0" util-deprecate "^1.0.2" -"@storybook/builder-webpack4@6.4.22": - version "6.4.22" - resolved "https://registry.npmjs.org/@storybook/builder-webpack4/-/builder-webpack4-6.4.22.tgz#d3384b146e97a2b3a6357c6eb8279ff0f1c7f8f5" - integrity sha512-A+GgGtKGnBneRFSFkDarUIgUTI8pYFdLmUVKEAGdh2hL+vLXAz9A46sEY7C8LQ85XWa8TKy3OTDxqR4+4iWj3A== +"@storybook/builder-webpack4@6.5.9": + version "6.5.9" + resolved "https://registry.npmjs.org/@storybook/builder-webpack4/-/builder-webpack4-6.5.9.tgz#4b37e1fa23a25aa4bfeaba640e5d318fcd511f95" + integrity sha512-YOeA4++9uRZ8Hog1wC60yjaxBOiI1FRQNtax7b9E7g+kP8UlSCPCGcv4gls9hFmzbzTOPfQTWnToA9Oa6jzRVw== dependencies: "@babel/core" "^7.12.10" - "@babel/plugin-proposal-class-properties" "^7.12.1" - "@babel/plugin-proposal-decorators" "^7.12.12" - "@babel/plugin-proposal-export-default-from" "^7.12.1" - "@babel/plugin-proposal-nullish-coalescing-operator" "^7.12.1" - "@babel/plugin-proposal-object-rest-spread" "^7.12.1" - "@babel/plugin-proposal-optional-chaining" "^7.12.7" - "@babel/plugin-proposal-private-methods" "^7.12.1" - "@babel/plugin-syntax-dynamic-import" "^7.8.3" - "@babel/plugin-transform-arrow-functions" "^7.12.1" - "@babel/plugin-transform-block-scoping" "^7.12.12" - "@babel/plugin-transform-classes" "^7.12.1" - "@babel/plugin-transform-destructuring" "^7.12.1" - "@babel/plugin-transform-for-of" "^7.12.1" - "@babel/plugin-transform-parameters" "^7.12.1" - "@babel/plugin-transform-shorthand-properties" "^7.12.1" - "@babel/plugin-transform-spread" "^7.12.1" - "@babel/plugin-transform-template-literals" "^7.12.1" - "@babel/preset-env" "^7.12.11" - "@babel/preset-react" "^7.12.10" - "@babel/preset-typescript" "^7.12.7" - "@storybook/addons" "6.4.22" - "@storybook/api" "6.4.22" - "@storybook/channel-postmessage" "6.4.22" - "@storybook/channels" "6.4.22" - "@storybook/client-api" "6.4.22" - "@storybook/client-logger" "6.4.22" - "@storybook/components" "6.4.22" - "@storybook/core-common" "6.4.22" - "@storybook/core-events" "6.4.22" - "@storybook/node-logger" "6.4.22" - "@storybook/preview-web" "6.4.22" - "@storybook/router" "6.4.22" + "@storybook/addons" "6.5.9" + "@storybook/api" "6.5.9" + "@storybook/channel-postmessage" "6.5.9" + "@storybook/channels" "6.5.9" + "@storybook/client-api" "6.5.9" + "@storybook/client-logger" "6.5.9" + "@storybook/components" "6.5.9" + "@storybook/core-common" "6.5.9" + "@storybook/core-events" "6.5.9" + "@storybook/node-logger" "6.5.9" + "@storybook/preview-web" "6.5.9" + "@storybook/router" "6.5.9" "@storybook/semver" "^7.3.2" - "@storybook/store" "6.4.22" - "@storybook/theming" "6.4.22" - "@storybook/ui" "6.4.22" - "@types/node" "^14.0.10" + "@storybook/store" "6.5.9" + "@storybook/theming" "6.5.9" + "@storybook/ui" "6.5.9" + "@types/node" "^14.0.10 || ^16.0.0" "@types/webpack" "^4.41.26" autoprefixer "^9.8.6" babel-loader "^8.0.0" - babel-plugin-macros "^2.8.0" - babel-plugin-polyfill-corejs3 "^0.1.0" case-sensitive-paths-webpack-plugin "^2.3.0" core-js "^3.8.2" css-loader "^3.6.0" @@ -1552,51 +1467,51 @@ webpack-hot-middleware "^2.25.1" webpack-virtual-modules "^0.2.2" -"@storybook/channel-postmessage@6.4.22": - version "6.4.22" - resolved "https://registry.npmjs.org/@storybook/channel-postmessage/-/channel-postmessage-6.4.22.tgz#8be0be1ea1e667a49fb0f09cdfdeeb4a45829637" - integrity sha512-gt+0VZLszt2XZyQMh8E94TqjHZ8ZFXZ+Lv/Mmzl0Yogsc2H+6VzTTQO4sv0IIx6xLbpgG72g5cr8VHsxW5kuDQ== +"@storybook/channel-postmessage@6.5.9": + version "6.5.9" + resolved "https://registry.npmjs.org/@storybook/channel-postmessage/-/channel-postmessage-6.5.9.tgz#9cf4530f0364cee0d5e58f92d6fb5ce98e10257b" + integrity sha512-pX/0R8UW7ezBhCrafRaL20OvMRcmESYvQQCDgjqSzJyHkcG51GOhsd6Ge93eJ6QvRMm9+w0Zs93N2VKjVtz0Qw== dependencies: - "@storybook/channels" "6.4.22" - "@storybook/client-logger" "6.4.22" - "@storybook/core-events" "6.4.22" + "@storybook/channels" "6.5.9" + "@storybook/client-logger" "6.5.9" + "@storybook/core-events" "6.5.9" core-js "^3.8.2" global "^4.4.0" qs "^6.10.0" - telejson "^5.3.2" + telejson "^6.0.8" -"@storybook/channel-websocket@6.4.22": - version "6.4.22" - resolved "https://registry.npmjs.org/@storybook/channel-websocket/-/channel-websocket-6.4.22.tgz#d541f69125873123c453757e2b879a75a9266c65" - integrity sha512-Bm/FcZ4Su4SAK5DmhyKKfHkr7HiHBui6PNutmFkASJInrL9wBduBfN8YQYaV7ztr8ezoHqnYRx8sj28jpwa6NA== +"@storybook/channel-websocket@6.5.9": + version "6.5.9" + resolved "https://registry.npmjs.org/@storybook/channel-websocket/-/channel-websocket-6.5.9.tgz#6b7a0127fec58ee5be4f6aebcf460adc564f2f34" + integrity sha512-xtHvSNwuOhkgALwVshKWsoFhDmuvcosdYfxcfFGEiYKXIu46tRS5ZXmpmgEC/0JAVkVoFj5nL8bV7IY5np6oaA== dependencies: - "@storybook/channels" "6.4.22" - "@storybook/client-logger" "6.4.22" + "@storybook/channels" "6.5.9" + "@storybook/client-logger" "6.5.9" core-js "^3.8.2" global "^4.4.0" - telejson "^5.3.2" + telejson "^6.0.8" -"@storybook/channels@6.4.22": - version "6.4.22" - resolved "https://registry.npmjs.org/@storybook/channels/-/channels-6.4.22.tgz#710f732763d63f063f615898ab1afbe74e309596" - integrity sha512-cfR74tu7MLah1A8Rru5sak71I+kH2e/sY6gkpVmlvBj4hEmdZp4Puj9PTeaKcMXh9DgIDPNA5mb8yvQH6VcyxQ== +"@storybook/channels@6.5.9": + version "6.5.9" + resolved "https://registry.npmjs.org/@storybook/channels/-/channels-6.5.9.tgz#abfab89a6587a2688e9926d4aafeb11c9d8b2e79" + integrity sha512-FvGA35nV38UPXWOl9ERapFTJaxwSTamQ339s2Ev7E9riyRG+GRkgTWzf5kECJgS1PAYKd/7m/RqKJT9BVv6A5g== dependencies: core-js "^3.8.2" ts-dedent "^2.0.0" util-deprecate "^1.0.2" -"@storybook/client-api@6.4.22": - version "6.4.22" - resolved "https://registry.npmjs.org/@storybook/client-api/-/client-api-6.4.22.tgz#df14f85e7900b94354c26c584bab53a67c47eae9" - integrity sha512-sO6HJNtrrdit7dNXQcZMdlmmZG1k6TswH3gAyP/DoYajycrTwSJ6ovkarzkO+0QcJ+etgra4TEdTIXiGHBMe/A== +"@storybook/client-api@6.5.9": + version "6.5.9" + resolved "https://registry.npmjs.org/@storybook/client-api/-/client-api-6.5.9.tgz#3e4a8ec1d277fd81325c5d959c553161a85fa182" + integrity sha512-pc7JKJoWLesixUKvG2nV36HukUuYoGRyAgD3PpIV7qSBS4JixqZ3VAHFUtqV1UzfOSQTovLSl4a0rIRnpie6gA== dependencies: - "@storybook/addons" "6.4.22" - "@storybook/channel-postmessage" "6.4.22" - "@storybook/channels" "6.4.22" - "@storybook/client-logger" "6.4.22" - "@storybook/core-events" "6.4.22" - "@storybook/csf" "0.0.2--canary.87bc651.0" - "@storybook/store" "6.4.22" + "@storybook/addons" "6.5.9" + "@storybook/channel-postmessage" "6.5.9" + "@storybook/channels" "6.5.9" + "@storybook/client-logger" "6.5.9" + "@storybook/core-events" "6.5.9" + "@storybook/csf" "0.0.2--canary.4566f4d.1" + "@storybook/store" "6.5.9" "@types/qs" "^6.9.5" "@types/webpack-env" "^1.16.0" core-js "^3.8.2" @@ -1611,59 +1526,45 @@ ts-dedent "^2.0.0" util-deprecate "^1.0.2" -"@storybook/client-logger@6.4.22": - version "6.4.22" - resolved "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-6.4.22.tgz#51abedb7d3c9bc21921aeb153ac8a19abc625cd6" - integrity sha512-LXhxh/lcDsdGnK8kimqfhu3C0+D2ylCSPPQNbU0IsLRmTfbpQYMdyl0XBjPdHiRVwlL7Gkw5OMjYemQgJ02zlw== +"@storybook/client-logger@6.5.9": + version "6.5.9" + resolved "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-6.5.9.tgz#dc1669abe8c45af1cc38f74c6f4b15ff33e63014" + integrity sha512-DOHL6p0uiDd3gV/Sb2FR+Vh6OiPrrf8BrA06uvXWsMRIIvEEvnparxv9EvPg7FlmUX0T3nq7d3juwjx4F8Wbcg== dependencies: core-js "^3.8.2" global "^4.4.0" -"@storybook/components@6.4.22": - version "6.4.22" - resolved "https://registry.npmjs.org/@storybook/components/-/components-6.4.22.tgz#4d425280240702883225b6a1f1abde7dc1a0e945" - integrity sha512-dCbXIJF9orMvH72VtAfCQsYbe57OP7fAADtR6YTwfCw9Sm1jFuZr8JbblQ1HcrXEoJG21nOyad3Hm5EYVb/sBw== +"@storybook/components@6.5.9": + version "6.5.9" + resolved "https://registry.npmjs.org/@storybook/components/-/components-6.5.9.tgz#97e07ffe11ab76c01ccee380888991bd161f75b2" + integrity sha512-BhfX980O9zn/1J4FNMeDo8ZvL1m5Ml3T4HRpfYmEBnf8oW5b5BeF6S2K2cwFStZRjWqm1feUcwNpZxCBVMkQnQ== dependencies: - "@popperjs/core" "^2.6.0" - "@storybook/client-logger" "6.4.22" - "@storybook/csf" "0.0.2--canary.87bc651.0" - "@storybook/theming" "6.4.22" - "@types/color-convert" "^2.0.0" - "@types/overlayscrollbars" "^1.12.0" + "@storybook/client-logger" "6.5.9" + "@storybook/csf" "0.0.2--canary.4566f4d.1" + "@storybook/theming" "6.5.9" "@types/react-syntax-highlighter" "11.0.5" - color-convert "^2.0.1" core-js "^3.8.2" - fast-deep-equal "^3.1.3" - global "^4.4.0" - lodash "^4.17.21" - markdown-to-jsx "^7.1.3" memoizerific "^1.11.3" - overlayscrollbars "^1.13.1" - polished "^4.0.5" - prop-types "^15.7.2" - react-colorful "^5.1.2" - react-popper-tooltip "^3.1.1" - react-syntax-highlighter "^13.5.3" - react-textarea-autosize "^8.3.0" + qs "^6.10.0" + react-syntax-highlighter "^15.4.5" regenerator-runtime "^0.13.7" - ts-dedent "^2.0.0" util-deprecate "^1.0.2" -"@storybook/core-client@6.4.22": - version "6.4.22" - resolved "https://registry.npmjs.org/@storybook/core-client/-/core-client-6.4.22.tgz#9079eda8a9c8e6ba24b84962a749b1c99668cb2a" - integrity sha512-uHg4yfCBeM6eASSVxStWRVTZrAnb4FT6X6v/xDqr4uXCpCttZLlBzrSDwPBLNNLtCa7ntRicHM8eGKIOD5lMYQ== +"@storybook/core-client@6.5.9": + version "6.5.9" + resolved "https://registry.npmjs.org/@storybook/core-client/-/core-client-6.5.9.tgz#ea6035d1c90d2c68e860e3cf629979491856cd88" + integrity sha512-LY0QbhShowO+PQx3gao3wdVjpKMH1AaSLmuI95FrcjoMmSXGf96jVLKQp9mJRGeHIsAa93EQBYuCihZycM3Kbg== dependencies: - "@storybook/addons" "6.4.22" - "@storybook/channel-postmessage" "6.4.22" - "@storybook/channel-websocket" "6.4.22" - "@storybook/client-api" "6.4.22" - "@storybook/client-logger" "6.4.22" - "@storybook/core-events" "6.4.22" - "@storybook/csf" "0.0.2--canary.87bc651.0" - "@storybook/preview-web" "6.4.22" - "@storybook/store" "6.4.22" - "@storybook/ui" "6.4.22" + "@storybook/addons" "6.5.9" + "@storybook/channel-postmessage" "6.5.9" + "@storybook/channel-websocket" "6.5.9" + "@storybook/client-api" "6.5.9" + "@storybook/client-logger" "6.5.9" + "@storybook/core-events" "6.5.9" + "@storybook/csf" "0.0.2--canary.4566f4d.1" + "@storybook/preview-web" "6.5.9" + "@storybook/store" "6.5.9" + "@storybook/ui" "6.5.9" airbnb-js-shims "^2.2.1" ansi-to-html "^0.6.11" core-js "^3.8.2" @@ -1675,10 +1576,10 @@ unfetch "^4.2.0" util-deprecate "^1.0.2" -"@storybook/core-common@6.4.22": - version "6.4.22" - resolved "https://registry.npmjs.org/@storybook/core-common/-/core-common-6.4.22.tgz#b00fa3c0625e074222a50be3196cb8052dd7f3bf" - integrity sha512-PD3N/FJXPNRHeQS2zdgzYFtqPLdi3MLwAicbnw+U3SokcsspfsAuyYHZOYZgwO8IAEKy6iCc7TpBdiSJZ/vAKQ== +"@storybook/core-common@6.5.9": + version "6.5.9" + resolved "https://registry.npmjs.org/@storybook/core-common/-/core-common-6.5.9.tgz#7ca8258ea2634b1d64695c1e4262f71cc7457989" + integrity sha512-NxOK0mrOCo0TWZ7Npc5HU66EKoRHlrtg18/ZixblLDWQMIqY9XCck8K1kJ8QYpYCHla+aHIsYUArFe2vhlEfZA== dependencies: "@babel/core" "^7.12.10" "@babel/plugin-proposal-class-properties" "^7.12.1" @@ -1688,6 +1589,7 @@ "@babel/plugin-proposal-object-rest-spread" "^7.12.1" "@babel/plugin-proposal-optional-chaining" "^7.12.7" "@babel/plugin-proposal-private-methods" "^7.12.1" + "@babel/plugin-proposal-private-property-in-object" "^7.12.1" "@babel/plugin-syntax-dynamic-import" "^7.8.3" "@babel/plugin-transform-arrow-functions" "^7.12.1" "@babel/plugin-transform-block-scoping" "^7.12.12" @@ -1701,9 +1603,9 @@ "@babel/preset-react" "^7.12.10" "@babel/preset-typescript" "^7.12.7" "@babel/register" "^7.12.1" - "@storybook/node-logger" "6.4.22" + "@storybook/node-logger" "6.5.9" "@storybook/semver" "^7.3.2" - "@types/node" "^14.0.10" + "@types/node" "^14.0.10 || ^16.0.0" "@types/pretty-hrtime" "^1.0.0" babel-loader "^8.0.0" babel-plugin-macros "^3.0.1" @@ -1725,35 +1627,36 @@ pretty-hrtime "^1.0.3" resolve-from "^5.0.0" slash "^3.0.0" - telejson "^5.3.2" + telejson "^6.0.8" ts-dedent "^2.0.0" util-deprecate "^1.0.2" webpack "4" -"@storybook/core-events@6.4.22": - version "6.4.22" - resolved "https://registry.npmjs.org/@storybook/core-events/-/core-events-6.4.22.tgz#c09b0571951affd4254028b8958a4d8652700989" - integrity sha512-5GYY5+1gd58Gxjqex27RVaX6qbfIQmJxcbzbNpXGNSqwqAuIIepcV1rdCVm6I4C3Yb7/AQ3cN5dVbf33QxRIwA== +"@storybook/core-events@6.5.9": + version "6.5.9" + resolved "https://registry.npmjs.org/@storybook/core-events/-/core-events-6.5.9.tgz#5b0783c7d22a586c0f5e927a61fe1b1223e19637" + integrity sha512-tXt7a3ZvJOCeEKpNa/B5rQM5VI7UJLlOh3IHOImWn4HqoBRrZvbourmac+PRZAtXpos0h3c6554Hjapj/Sny5Q== dependencies: core-js "^3.8.2" -"@storybook/core-server@6.4.22": - version "6.4.22" - resolved "https://registry.npmjs.org/@storybook/core-server/-/core-server-6.4.22.tgz#254409ec2ba49a78b23f5e4a4c0faea5a570a32b" - integrity sha512-wFh3e2fa0un1d4+BJP+nd3FVWUO7uHTqv3OGBfOmzQMKp4NU1zaBNdSQG7Hz6mw0fYPBPZgBjPfsJRwIYLLZyw== +"@storybook/core-server@6.5.9": + version "6.5.9" + resolved "https://registry.npmjs.org/@storybook/core-server/-/core-server-6.5.9.tgz#749a881c1a81d7cf1a69f3782c06a7f0c39a505c" + integrity sha512-YeePGUrd5fQPvGzMhowh124KrcZURFpFXg1VB0Op3ESqCIsInoMZeObci4Gc+binMXC7vcv7aw3EwSLU37qJzQ== dependencies: "@discoveryjs/json-ext" "^0.5.3" - "@storybook/builder-webpack4" "6.4.22" - "@storybook/core-client" "6.4.22" - "@storybook/core-common" "6.4.22" - "@storybook/core-events" "6.4.22" - "@storybook/csf" "0.0.2--canary.87bc651.0" - "@storybook/csf-tools" "6.4.22" - "@storybook/manager-webpack4" "6.4.22" - "@storybook/node-logger" "6.4.22" + "@storybook/builder-webpack4" "6.5.9" + "@storybook/core-client" "6.5.9" + "@storybook/core-common" "6.5.9" + "@storybook/core-events" "6.5.9" + "@storybook/csf" "0.0.2--canary.4566f4d.1" + "@storybook/csf-tools" "6.5.9" + "@storybook/manager-webpack4" "6.5.9" + "@storybook/node-logger" "6.5.9" "@storybook/semver" "^7.3.2" - "@storybook/store" "6.4.22" - "@types/node" "^14.0.10" + "@storybook/store" "6.5.9" + "@storybook/telemetry" "6.5.9" + "@types/node" "^14.0.10 || ^16.0.0" "@types/node-fetch" "^2.5.7" "@types/pretty-hrtime" "^1.0.0" "@types/webpack" "^4.41.26" @@ -1767,36 +1670,38 @@ cpy "^8.1.2" detect-port "^1.3.0" express "^4.17.1" - file-system-cache "^1.0.5" fs-extra "^9.0.1" + global "^4.4.0" globby "^11.0.2" - ip "^1.1.5" + ip "^2.0.0" lodash "^4.17.21" - node-fetch "^2.6.1" + node-fetch "^2.6.7" + open "^8.4.0" pretty-hrtime "^1.0.3" prompts "^2.4.0" regenerator-runtime "^0.13.7" serve-favicon "^2.5.0" slash "^3.0.0" - telejson "^5.3.3" + telejson "^6.0.8" ts-dedent "^2.0.0" util-deprecate "^1.0.2" watchpack "^2.2.0" webpack "4" ws "^8.2.3" + x-default-browser "^0.4.0" -"@storybook/core@6.4.22": - version "6.4.22" - resolved "https://registry.npmjs.org/@storybook/core/-/core-6.4.22.tgz#cf14280d7831b41d5dea78f76b414bdfde5918f0" - integrity sha512-KZYJt7GM5NgKFXbPRZZZPEONZ5u/tE/cRbMdkn/zWN3He8+VP+65/tz8hbriI/6m91AWVWkBKrODSkeq59NgRA== +"@storybook/core@6.5.9": + version "6.5.9" + resolved "https://registry.npmjs.org/@storybook/core/-/core-6.5.9.tgz#da4f237391d99aed1228323f24b335cafbdf3499" + integrity sha512-Mt3TTQnjQt2/pa60A+bqDsAOrYpohapdtt4DDZEbS8h0V6u11KyYYh3w7FCySlL+sPEyogj63l5Ec76Jah3l2w== dependencies: - "@storybook/core-client" "6.4.22" - "@storybook/core-server" "6.4.22" + "@storybook/core-client" "6.5.9" + "@storybook/core-server" "6.5.9" -"@storybook/csf-tools@6.4.22": - version "6.4.22" - resolved "https://registry.npmjs.org/@storybook/csf-tools/-/csf-tools-6.4.22.tgz#f6d64bcea1b36114555972acae66a1dbe9e34b5c" - integrity sha512-LMu8MZAiQspJAtMBLU2zitsIkqQv7jOwX7ih5JrXlyaDticH7l2j6Q+1mCZNWUOiMTizj0ivulmUsSaYbpToSw== +"@storybook/csf-tools@6.5.9": + version "6.5.9" + resolved "https://registry.npmjs.org/@storybook/csf-tools/-/csf-tools-6.5.9.tgz#8e01df2305b53e228229f0b45ada3720e6e42a1c" + integrity sha512-RAdhsO2XmEDyWy0qNQvdKMLeIZAuyfD+tYlUwBHRU6DbByDucvwgMOGy5dF97YNJFmyo93EUYJzXjUrJs3U1LQ== dependencies: "@babel/core" "^7.12.10" "@babel/generator" "^7.12.11" @@ -1805,39 +1710,49 @@ "@babel/preset-env" "^7.12.11" "@babel/traverse" "^7.12.11" "@babel/types" "^7.12.11" - "@mdx-js/mdx" "^1.6.22" - "@storybook/csf" "0.0.2--canary.87bc651.0" + "@storybook/csf" "0.0.2--canary.4566f4d.1" + "@storybook/mdx1-csf" "^0.0.1" core-js "^3.8.2" fs-extra "^9.0.1" global "^4.4.0" - js-string-escape "^1.0.1" - lodash "^4.17.21" - prettier ">=2.2.1 <=2.3.0" regenerator-runtime "^0.13.7" ts-dedent "^2.0.0" -"@storybook/csf@0.0.2--canary.87bc651.0": - version "0.0.2--canary.87bc651.0" - resolved "https://registry.npmjs.org/@storybook/csf/-/csf-0.0.2--canary.87bc651.0.tgz#c7b99b3a344117ef67b10137b6477a3d2750cf44" - integrity sha512-ajk1Uxa+rBpFQHKrCcTmJyQBXZ5slfwHVEaKlkuFaW77it8RgbPJp/ccna3sgoi8oZ7FkkOyvv1Ve4SmwFqRqw== +"@storybook/csf@0.0.2--canary.4566f4d.1": + version "0.0.2--canary.4566f4d.1" + resolved "https://registry.npmjs.org/@storybook/csf/-/csf-0.0.2--canary.4566f4d.1.tgz#dac52a21c40ef198554e71fe4d20d61e17f65327" + integrity sha512-9OVvMVh3t9znYZwb0Svf/YQoxX2gVOeQTGe2bses2yj+a3+OJnCrUF3/hGv6Em7KujtOdL2LL+JnG49oMVGFgQ== dependencies: lodash "^4.17.15" -"@storybook/manager-webpack4@6.4.22": - version "6.4.22" - resolved "https://registry.npmjs.org/@storybook/manager-webpack4/-/manager-webpack4-6.4.22.tgz#eabd674beee901c7f755d9b679e9f969cbab636d" - integrity sha512-nzhDMJYg0vXdcG0ctwE6YFZBX71+5NYaTGkxg3xT7gbgnP1YFXn9gVODvgq3tPb3gcRapjyOIxUa20rV+r8edA== +"@storybook/docs-tools@6.5.9": + version "6.5.9" + resolved "https://registry.npmjs.org/@storybook/docs-tools/-/docs-tools-6.5.9.tgz#5ff304f881e972ce14923a5ffcfed3f052094889" + integrity sha512-UoTaXLvec8x+q+4oYIk/t8DBju9C3ZTGklqOxDIt+0kS3TFAqEgI3JhKXqQOXgN5zDcvLVSxi8dbVAeSxk2ktA== + dependencies: + "@babel/core" "^7.12.10" + "@storybook/csf" "0.0.2--canary.4566f4d.1" + "@storybook/store" "6.5.9" + core-js "^3.8.2" + doctrine "^3.0.0" + lodash "^4.17.21" + regenerator-runtime "^0.13.7" + +"@storybook/manager-webpack4@6.5.9": + version "6.5.9" + resolved "https://registry.npmjs.org/@storybook/manager-webpack4/-/manager-webpack4-6.5.9.tgz#c75d2cced4550c8a786f00b0e57b203d613e706c" + integrity sha512-49LZlHqWc7zj9tQfOOANixPYmLxqWTTZceA6DSXnKd9xDiO2Gl23Y+l/CSPXNZGDB8QFAwpimwqyKJj/NLH45A== dependencies: "@babel/core" "^7.12.10" "@babel/plugin-transform-template-literals" "^7.12.1" "@babel/preset-react" "^7.12.10" - "@storybook/addons" "6.4.22" - "@storybook/core-client" "6.4.22" - "@storybook/core-common" "6.4.22" - "@storybook/node-logger" "6.4.22" - "@storybook/theming" "6.4.22" - "@storybook/ui" "6.4.22" - "@types/node" "^14.0.10" + "@storybook/addons" "6.5.9" + "@storybook/core-client" "6.5.9" + "@storybook/core-common" "6.5.9" + "@storybook/node-logger" "6.5.9" + "@storybook/theming" "6.5.9" + "@storybook/ui" "6.5.9" + "@types/node" "^14.0.10 || ^16.0.0" "@types/webpack" "^4.41.26" babel-loader "^8.0.0" case-sensitive-paths-webpack-plugin "^2.3.0" @@ -1846,17 +1761,16 @@ css-loader "^3.6.0" express "^4.17.1" file-loader "^6.2.0" - file-system-cache "^1.0.5" find-up "^5.0.0" fs-extra "^9.0.1" html-webpack-plugin "^4.0.0" - node-fetch "^2.6.1" + node-fetch "^2.6.7" pnp-webpack-plugin "1.6.4" read-pkg-up "^7.0.1" regenerator-runtime "^0.13.7" resolve-from "^5.0.0" style-loader "^1.3.0" - telejson "^5.3.2" + telejson "^6.0.8" terser-webpack-plugin "^4.2.3" ts-dedent "^2.0.0" url-loader "^4.1.1" @@ -1865,10 +1779,27 @@ webpack-dev-middleware "^3.7.3" webpack-virtual-modules "^0.2.2" -"@storybook/node-logger@6.4.22": - version "6.4.22" - resolved "https://registry.npmjs.org/@storybook/node-logger/-/node-logger-6.4.22.tgz#c4ec00f8714505f44eda7671bc88bb44abf7ae59" - integrity sha512-sUXYFqPxiqM7gGH7gBXvO89YEO42nA4gBicJKZjj9e+W4QQLrftjF9l+mAw2K0mVE10Bn7r4pfs5oEZ0aruyyA== +"@storybook/mdx1-csf@^0.0.1": + version "0.0.1" + resolved "https://registry.npmjs.org/@storybook/mdx1-csf/-/mdx1-csf-0.0.1.tgz#d4184e3f6486fade9f7a6bfaf934d9bc07718d5b" + integrity sha512-4biZIWWzoWlCarMZmTpqcJNgo/RBesYZwGFbQeXiGYsswuvfWARZnW9RE9aUEMZ4XPn7B1N3EKkWcdcWe/K2tg== + dependencies: + "@babel/generator" "^7.12.11" + "@babel/parser" "^7.12.11" + "@babel/preset-env" "^7.12.11" + "@babel/types" "^7.12.11" + "@mdx-js/mdx" "^1.6.22" + "@types/lodash" "^4.14.167" + js-string-escape "^1.0.1" + loader-utils "^2.0.0" + lodash "^4.17.21" + prettier ">=2.2.1 <=2.3.0" + ts-dedent "^2.0.0" + +"@storybook/node-logger@6.5.9": + version "6.5.9" + resolved "https://registry.npmjs.org/@storybook/node-logger/-/node-logger-6.5.9.tgz#129cfe0d0f79cab4f6a2ba194d39516680b1626f" + integrity sha512-nZZNZG2Wtwv6Trxi3FrnIqUmB55xO+X/WQGPT5iKlqNjdRIu/T72mE7addcp4rbuWCQfZUhcDDGpBOwKtBxaGg== dependencies: "@types/npmlog" "^4.1.2" chalk "^4.1.0" @@ -1876,17 +1807,17 @@ npmlog "^5.0.1" pretty-hrtime "^1.0.3" -"@storybook/preview-web@6.4.22": - version "6.4.22" - resolved "https://registry.npmjs.org/@storybook/preview-web/-/preview-web-6.4.22.tgz#58bfc6492503ff4265b50f42a27ea8b0bfcf738a" - integrity sha512-sWS+sgvwSvcNY83hDtWUUL75O2l2LY/GTAS0Zp2dh3WkObhtuJ/UehftzPZlZmmv7PCwhb4Q3+tZDKzMlFxnKQ== +"@storybook/preview-web@6.5.9": + version "6.5.9" + resolved "https://registry.npmjs.org/@storybook/preview-web/-/preview-web-6.5.9.tgz#557d919e6df50d66259521aa36ebf4055bbd236e" + integrity sha512-4eMrO2HJyZUYyL/j+gUaDvry6iGedshwT5MQqe7J9FaA+Q2pNARQRB1X53f410w7S4sObRmYIAIluWPYdWym9w== dependencies: - "@storybook/addons" "6.4.22" - "@storybook/channel-postmessage" "6.4.22" - "@storybook/client-logger" "6.4.22" - "@storybook/core-events" "6.4.22" - "@storybook/csf" "0.0.2--canary.87bc651.0" - "@storybook/store" "6.4.22" + "@storybook/addons" "6.5.9" + "@storybook/channel-postmessage" "6.5.9" + "@storybook/client-logger" "6.5.9" + "@storybook/core-events" "6.5.9" + "@storybook/csf" "0.0.2--canary.4566f4d.1" + "@storybook/store" "6.5.9" ansi-to-html "^0.6.11" core-js "^3.8.2" global "^4.4.0" @@ -1898,65 +1829,70 @@ unfetch "^4.2.0" util-deprecate "^1.0.2" -"@storybook/react-docgen-typescript-plugin@1.0.2-canary.253f8c1.0": - version "1.0.2-canary.253f8c1.0" - resolved "https://registry.npmjs.org/@storybook/react-docgen-typescript-plugin/-/react-docgen-typescript-plugin-1.0.2-canary.253f8c1.0.tgz#f2da40e6aae4aa586c2fb284a4a1744602c3c7fa" - integrity sha512-mmoRG/rNzAiTbh+vGP8d57dfcR2aP+5/Ll03KKFyfy5FqWFm/Gh7u27ikx1I3LmVMI8n6jh5SdWMkMKon7/tDw== +"@storybook/react-docgen-typescript-plugin@1.0.2-canary.6.9d540b91e815f8fc2f8829189deb00553559ff63.0": + version "1.0.2-canary.6.9d540b91e815f8fc2f8829189deb00553559ff63.0" + resolved "https://registry.npmjs.org/@storybook/react-docgen-typescript-plugin/-/react-docgen-typescript-plugin-1.0.2-canary.6.9d540b91e815f8fc2f8829189deb00553559ff63.0.tgz#3103532ff494fb7dc3cf835f10740ecf6a26c0f9" + integrity sha512-eVg3BxlOm2P+chijHBTByr90IZVUtgRW56qEOLX7xlww2NBuKrcavBlcmn+HH7GIUktquWkMPtvy6e0W0NgA5w== dependencies: debug "^4.1.1" endent "^2.0.1" find-cache-dir "^3.3.1" flat-cache "^3.0.4" micromatch "^4.0.2" - react-docgen-typescript "^2.0.0" + react-docgen-typescript "^2.1.1" tslib "^2.0.0" "@storybook/react@^6.4.21": - version "6.4.22" - resolved "https://registry.npmjs.org/@storybook/react/-/react-6.4.22.tgz#5940e5492bc87268555b47f12aff4be4b67eae54" - integrity sha512-5BFxtiguOcePS5Ty/UoH7C6odmvBYIZutfiy4R3Ua6FYmtxac5vP9r5KjCz1IzZKT8mCf4X+PuK1YvDrPPROgQ== + version "6.5.9" + resolved "https://registry.npmjs.org/@storybook/react/-/react-6.5.9.tgz#687ec1f6b785822a392b7ac115b61800f69fb7cd" + integrity sha512-Rp+QaTQAzxJhwuzJXVd49mnIBLQRlF8llTxPT2YoGHdrGkku/zl/HblQ6H2yzEf15367VyzaAv/BpLsO9Jlfxg== dependencies: "@babel/preset-flow" "^7.12.1" "@babel/preset-react" "^7.12.10" - "@pmmmwh/react-refresh-webpack-plugin" "^0.5.1" - "@storybook/addons" "6.4.22" - "@storybook/core" "6.4.22" - "@storybook/core-common" "6.4.22" - "@storybook/csf" "0.0.2--canary.87bc651.0" - "@storybook/node-logger" "6.4.22" - "@storybook/react-docgen-typescript-plugin" "1.0.2-canary.253f8c1.0" + "@pmmmwh/react-refresh-webpack-plugin" "^0.5.3" + "@storybook/addons" "6.5.9" + "@storybook/client-logger" "6.5.9" + "@storybook/core" "6.5.9" + "@storybook/core-common" "6.5.9" + "@storybook/csf" "0.0.2--canary.4566f4d.1" + "@storybook/docs-tools" "6.5.9" + "@storybook/node-logger" "6.5.9" + "@storybook/react-docgen-typescript-plugin" "1.0.2-canary.6.9d540b91e815f8fc2f8829189deb00553559ff63.0" "@storybook/semver" "^7.3.2" - "@storybook/store" "6.4.22" + "@storybook/store" "6.5.9" + "@types/estree" "^0.0.51" + "@types/node" "^14.14.20 || ^16.0.0" "@types/webpack-env" "^1.16.0" + acorn "^7.4.1" + acorn-jsx "^5.3.1" + acorn-walk "^7.2.0" babel-plugin-add-react-displayname "^0.0.5" - babel-plugin-named-asset-import "^0.3.1" babel-plugin-react-docgen "^4.2.1" core-js "^3.8.2" + escodegen "^2.0.0" + fs-extra "^9.0.1" global "^4.4.0" + html-tags "^3.1.0" lodash "^4.17.21" prop-types "^15.7.2" + react-element-to-jsx-string "^14.3.4" react-refresh "^0.11.0" read-pkg-up "^7.0.1" regenerator-runtime "^0.13.7" ts-dedent "^2.0.0" - webpack "4" + util-deprecate "^1.0.2" + webpack ">=4.43.0 <6.0.0" -"@storybook/router@6.4.22": - version "6.4.22" - resolved "https://registry.npmjs.org/@storybook/router/-/router-6.4.22.tgz#e3cc5cd8595668a367e971efb9695bbc122ed95e" - integrity sha512-zeuE8ZgFhNerQX8sICQYNYL65QEi3okyzw7ynF58Ud6nRw4fMxSOHcj2T+nZCIU5ufozRL4QWD/Rg9P2s/HtLw== +"@storybook/router@6.5.9": + version "6.5.9" + resolved "https://registry.npmjs.org/@storybook/router/-/router-6.5.9.tgz#4740248f8517425b2056273fb366ace8a17c65e8" + integrity sha512-G2Xp/2r8vU2O34eelE+G5VbEEVFDeHcCURrVJEROh6dq2asFJAPbzslVXSeCqgOTNLSpRDJ2NcN5BckkNqmqJg== dependencies: - "@storybook/client-logger" "6.4.22" + "@storybook/client-logger" "6.5.9" core-js "^3.8.2" - fast-deep-equal "^3.1.3" - global "^4.4.0" - history "5.0.0" - lodash "^4.17.21" memoizerific "^1.11.3" qs "^6.10.0" - react-router "^6.0.0" - react-router-dom "^6.0.0" - ts-dedent "^2.0.0" + regenerator-runtime "^0.13.7" "@storybook/semver@^7.3.2": version "7.3.2" @@ -1966,14 +1902,14 @@ core-js "^3.6.5" find-up "^4.1.0" -"@storybook/source-loader@6.4.22": - version "6.4.22" - resolved "https://registry.npmjs.org/@storybook/source-loader/-/source-loader-6.4.22.tgz#c931b81cf1bd63f79b51bfa9311de7f5a04a7b77" - integrity sha512-O4RxqPgRyOgAhssS6q1Rtc8LiOvPBpC1EqhCYWRV3K+D2EjFarfQMpjgPj18hC+QzpUSfzoBZYqsMECewEuLNw== +"@storybook/source-loader@6.5.9": + version "6.5.9" + resolved "https://registry.npmjs.org/@storybook/source-loader/-/source-loader-6.5.9.tgz#7b6f065c6a6108c4b4ca7e45bfd78707373d84ac" + integrity sha512-H03nFKaP6borfWMTTa9igBA+Jm2ph+FoVJImWC/X+LAmLSJYYSXuqSgmiZ/DZvbjxS4k8vccE2HXogne1IvaRA== dependencies: - "@storybook/addons" "6.4.22" - "@storybook/client-logger" "6.4.22" - "@storybook/csf" "0.0.2--canary.87bc651.0" + "@storybook/addons" "6.5.9" + "@storybook/client-logger" "6.5.9" + "@storybook/csf" "0.0.2--canary.4566f4d.1" core-js "^3.8.2" estraverse "^5.2.0" global "^4.4.0" @@ -1982,15 +1918,15 @@ prettier ">=2.2.1 <=2.3.0" regenerator-runtime "^0.13.7" -"@storybook/store@6.4.22": - version "6.4.22" - resolved "https://registry.npmjs.org/@storybook/store/-/store-6.4.22.tgz#f291fbe3639f14d25f875cac86abb209a97d4e2a" - integrity sha512-lrmcZtYJLc2emO+1l6AG4Txm9445K6Pyv9cGAuhOJ9Kks0aYe0YtvMkZVVry0RNNAIv6Ypz72zyKc/QK+tZLAQ== +"@storybook/store@6.5.9": + version "6.5.9" + resolved "https://registry.npmjs.org/@storybook/store/-/store-6.5.9.tgz#dc9963fc013636569082bd8f7200804866373735" + integrity sha512-80pcDTcCwK6wUA63aWOp13urI77jfipIVee9mpVvbNyfrNN8kGv1BS0z/JHDxuV6rC4g7LG1fb+BurR0yki7BA== dependencies: - "@storybook/addons" "6.4.22" - "@storybook/client-logger" "6.4.22" - "@storybook/core-events" "6.4.22" - "@storybook/csf" "0.0.2--canary.87bc651.0" + "@storybook/addons" "6.5.9" + "@storybook/client-logger" "6.5.9" + "@storybook/core-events" "6.5.9" + "@storybook/csf" "0.0.2--canary.4566f4d.1" core-js "^3.8.2" fast-deep-equal "^3.1.3" global "^4.4.0" @@ -2003,57 +1939,53 @@ ts-dedent "^2.0.0" util-deprecate "^1.0.2" -"@storybook/theming@6.4.22": - version "6.4.22" - resolved "https://registry.npmjs.org/@storybook/theming/-/theming-6.4.22.tgz#19097eec0366447ddd0d6917b0e0f81d0ec5e51e" - integrity sha512-NVMKH/jxSPtnMTO4VCN1k47uztq+u9fWv4GSnzq/eezxdGg9ceGL4/lCrNGoNajht9xbrsZ4QvsJ/V2sVGM8wA== +"@storybook/telemetry@6.5.9": + version "6.5.9" + resolved "https://registry.npmjs.org/@storybook/telemetry/-/telemetry-6.5.9.tgz#8e1e0d4a89fc2387620045e5ea96c109d16a7247" + integrity sha512-JluoHCRhHAr4X0eUNVBSBi1JIBA92404Tu1TPdbN7x6gCZxHXXPTSUTAnspXp/21cTdMhY2x+kfZQ8fmlGK4MQ== dependencies: - "@emotion/core" "^10.1.1" - "@emotion/is-prop-valid" "^0.8.6" - "@emotion/styled" "^10.0.27" - "@storybook/client-logger" "6.4.22" + "@storybook/client-logger" "6.5.9" + "@storybook/core-common" "6.5.9" + chalk "^4.1.0" core-js "^3.8.2" - deep-object-diff "^1.1.0" - emotion-theming "^10.0.27" + detect-package-manager "^2.0.1" + fetch-retry "^5.0.2" + fs-extra "^9.0.1" global "^4.4.0" - memoizerific "^1.11.3" - polished "^4.0.5" - resolve-from "^5.0.0" - ts-dedent "^2.0.0" + isomorphic-unfetch "^3.1.0" + nanoid "^3.3.1" + read-pkg-up "^7.0.1" + regenerator-runtime "^0.13.7" -"@storybook/ui@6.4.22": - version "6.4.22" - resolved "https://registry.npmjs.org/@storybook/ui/-/ui-6.4.22.tgz#49badd7994465d78d984ca4c42533c1c22201c46" - integrity sha512-UVjMoyVsqPr+mkS1L7m30O/xrdIEgZ5SCWsvqhmyMUok3F3tRB+6M+OA5Yy+cIVfvObpA7MhxirUT1elCGXsWQ== +"@storybook/theming@6.5.9": + version "6.5.9" + resolved "https://registry.npmjs.org/@storybook/theming/-/theming-6.5.9.tgz#13f60a3a3cd73ceb5caf9f188e1627e79f1891aa" + integrity sha512-KM0AMP5jMQPAdaO8tlbFCYqx9uYM/hZXGSVUhznhLYu7bhNAIK7ZVmXxyE/z/khM++8eUHzRoZGiO/cwCkg9Xw== dependencies: - "@emotion/core" "^10.1.1" - "@storybook/addons" "6.4.22" - "@storybook/api" "6.4.22" - "@storybook/channels" "6.4.22" - "@storybook/client-logger" "6.4.22" - "@storybook/components" "6.4.22" - "@storybook/core-events" "6.4.22" - "@storybook/router" "6.4.22" - "@storybook/semver" "^7.3.2" - "@storybook/theming" "6.4.22" - copy-to-clipboard "^3.3.1" + "@storybook/client-logger" "6.5.9" + core-js "^3.8.2" + memoizerific "^1.11.3" + regenerator-runtime "^0.13.7" + +"@storybook/ui@6.5.9": + version "6.5.9" + resolved "https://registry.npmjs.org/@storybook/ui/-/ui-6.5.9.tgz#41e59279323cccc0d613974ec9782d797220c8a7" + integrity sha512-ryuPxJgtbb0gPXKGgGAUC+Z185xGAd1IvQ0jM5fJ0SisHXI8jteG3RaWhntOehi9qCg+64Vv6eH/cj9QYNHt1Q== + dependencies: + "@storybook/addons" "6.5.9" + "@storybook/api" "6.5.9" + "@storybook/channels" "6.5.9" + "@storybook/client-logger" "6.5.9" + "@storybook/components" "6.5.9" + "@storybook/core-events" "6.5.9" + "@storybook/router" "6.5.9" + "@storybook/semver" "^7.3.2" + "@storybook/theming" "6.5.9" core-js "^3.8.2" - core-js-pure "^3.8.2" - downshift "^6.0.15" - emotion-theming "^10.0.27" - fuse.js "^3.6.1" - global "^4.4.0" - lodash "^4.17.21" - markdown-to-jsx "^7.1.3" memoizerific "^1.11.3" - polished "^4.0.5" qs "^6.10.0" - react-draggable "^4.4.3" - react-helmet-async "^1.0.7" - react-sizeme "^3.0.1" regenerator-runtime "^0.13.7" resolve-from "^5.0.0" - store2 "^2.12.0" "@sucrase/webpack-loader@^2.0.0": version "2.0.0" @@ -2062,17 +1994,26 @@ dependencies: loader-utils "^1.1.0" -"@types/color-convert@^2.0.0": - version "2.0.0" - resolved "https://registry.npmjs.org/@types/color-convert/-/color-convert-2.0.0.tgz#8f5ee6b9e863dcbee5703f5a517ffb13d3ea4e22" - integrity sha512-m7GG7IKKGuJUXvkZ1qqG3ChccdIM/qBBo913z+Xft0nKCX4hAU/IxKwZBU4cpRZ7GS5kV4vOblUkILtSShCPXQ== +"@types/eslint-scope@^3.7.3": + version "3.7.3" + resolved "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.3.tgz#125b88504b61e3c8bc6f870882003253005c3224" + integrity sha512-PB3ldyrcnAicT35TWPs5IcwKD8S333HMaa2VVv4+wdvebJkjWuW/xESoB8IwRcog8HYVYamb1g/R31Qv5Bx03g== dependencies: - "@types/color-name" "*" + "@types/eslint" "*" + "@types/estree" "*" -"@types/color-name@*": - version "1.1.1" - resolved "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" - integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ== +"@types/eslint@*": + version "8.4.3" + resolved "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.3.tgz#5c92815a3838b1985c90034cd85f26f59d9d0ece" + integrity sha512-YP1S7YJRMPs+7KZKDb9G63n8YejIwW9BALq7a5j2+H4yl6iOv9CB29edho+cuFRrvmJbbaH2yiVChKLJVysDGw== + dependencies: + "@types/estree" "*" + "@types/json-schema" "*" + +"@types/estree@*", "@types/estree@^0.0.51": + version "0.0.51" + resolved "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz#cfd70924a25a3fd32b218e5e420e6897e1ac4f40" + integrity sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ== "@types/glob@*", "@types/glob@^7.1.1": version "7.2.0" @@ -2104,11 +2045,16 @@ resolved "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz#8467d4b3c087805d63580480890791277ce35c44" integrity sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g== -"@types/json-schema@^7.0.4", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8": +"@types/json-schema@*", "@types/json-schema@^7.0.4", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8": version "7.0.11" resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== +"@types/lodash@^4.14.167": + version "4.14.182" + resolved "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.182.tgz#05301a4d5e62963227eaafe0ce04dd77c54ea5c2" + integrity sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q== + "@types/mdast@^3.0.0": version "3.0.10" resolved "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.10.tgz#4724244a82a4598884cbbe9bcfd73dff927ee8af" @@ -2130,14 +2076,14 @@ form-data "^3.0.0" "@types/node@*": - version "17.0.25" - resolved "https://registry.npmjs.org/@types/node/-/node-17.0.25.tgz#527051f3c2f77aa52e5dc74e45a3da5fb2301448" - integrity sha512-wANk6fBrUwdpY4isjWrKTufkrXdu1D2YHCot2fD/DfWxF5sMrVSA+KN7ydckvaTCh0HiqX9IVl0L5/ZoXg5M7w== + version "17.0.44" + resolved "https://registry.npmjs.org/@types/node/-/node-17.0.44.tgz#16dd0bb5338f016d8ca10631789f0d0612fe5d5b" + integrity sha512-gWYiOlu6Y4oyLYBvsJAPlwHbC8H4tX+tLsHy6Ee976wedwwZKrG2hFl3Y/HiH6bIyLTbDWQexQF/ohwKkOpUCg== -"@types/node@^14.0.10": - version "14.18.13" - resolved "https://registry.npmjs.org/@types/node/-/node-14.18.13.tgz#6ad4d9db59e6b3faf98dcfe4ca9d2aec84443277" - integrity sha512-Z6/KzgyWOga3pJNS42A+zayjhPbf2zM3hegRQaOPnLOzEi86VV++6FLDWgR1LGrVCRufP/ph2daa3tEa5br1zA== +"@types/node@^14.0.10 || ^16.0.0", "@types/node@^14.14.20 || ^16.0.0": + version "16.11.41" + resolved "https://registry.npmjs.org/@types/node/-/node-16.11.41.tgz#88eb485b1bfdb4c224d878b7832239536aa2f813" + integrity sha512-mqoYK2TnVjdkGk8qXAVGc/x9nSaTpSrFaGFm43BUH3IdoBV0nta6hYaGmdOvIMlbHJbUEVen3gvwpwovAZKNdQ== "@types/normalize-package-data@^2.4.0": version "2.4.1" @@ -2149,11 +2095,6 @@ resolved "https://registry.npmjs.org/@types/npmlog/-/npmlog-4.1.4.tgz#30eb872153c7ead3e8688c476054ddca004115f6" integrity sha512-WKG4gTr8przEZBiJ5r3s8ZIAoMXNbOgQ+j/d5O4X3x6kZJRLNvyUJuUK/KoG3+8BaOHPhp2m7WC6JKKeovDSzQ== -"@types/overlayscrollbars@^1.12.0": - version "1.12.1" - resolved "https://registry.npmjs.org/@types/overlayscrollbars/-/overlayscrollbars-1.12.1.tgz#fb637071b545834fb12aea94ee309a2ff4cdc0a8" - integrity sha512-V25YHbSoKQN35UasHf0EKD9U2vcmexRSp78qa8UglxFH8H3D+adEa9zGZwrqpH4TdvqeMrgMqVqsLB4woAryrQ== - "@types/parse-json@^4.0.0": version "4.0.0" resolved "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" @@ -2187,9 +2128,9 @@ "@types/react" "*" "@types/react@*": - version "18.0.5" - resolved "https://registry.npmjs.org/@types/react/-/react-18.0.5.tgz#1a4d4b705ae6af5aed369dec22800b20f89f5301" - integrity sha512-UPxNGInDCIKlfqBrm8LDXYWNfLHwIdisWcsH5GpMyGjhEDLFgTtlRBaoWuCua9HcyuE0rMkmAeZ3FXV1pYLIYQ== + version "18.0.12" + resolved "https://registry.npmjs.org/@types/react/-/react-18.0.12.tgz#cdaa209d0a542b3fcf69cf31a03976ec4cdd8840" + integrity sha512-duF1OTASSBQtcigUvhuiTB1Ya3OvSy+xORCiEf20H0P0lzx+/KeVsA99U5UjLXSbyo1DRJDlLKqTeM1ngosqtg== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" @@ -2211,9 +2152,9 @@ integrity sha512-ipixuVrh2OdNmauvtT51o3d8z12p6LtFW9in7U79der/kwejjdNchQC5UMn5u/KxNoM7VHHOs/l8KS8uHxhODQ== "@types/uglify-js@*": - version "3.13.2" - resolved "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.13.2.tgz#1044c1713fb81cb1ceef29ad8a9ee1ce08d690ef" - integrity sha512-/xFrPIo+4zOeNGtVMbf9rUm0N+i4pDf1ynExomqtokIJmVzR3962lJ1UE+MmexMkA0cmN9oTzg5Xcbwge0Ij2Q== + version "3.16.0" + resolved "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.16.0.tgz#2cf74a0e6ebb6cd54c0d48e509d5bd91160a9602" + integrity sha512-0yeUr92L3r0GLRnBOvtYK1v2SjqMIqQDHMl7GLb+l2L8+6LSFWEEWEIgVsPdMn5ImLM8qzWT8xFPtQYpp8co0g== dependencies: source-map "^0.6.1" @@ -2223,9 +2164,9 @@ integrity sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ== "@types/webpack-env@^1.16.0": - version "1.16.4" - resolved "https://registry.npmjs.org/@types/webpack-env/-/webpack-env-1.16.4.tgz#1f4969042bf76d7ef7b5914f59b3b60073f4e1f4" - integrity sha512-llS8qveOUX3wxHnSykP5hlYFFuMfJ9p5JvIyCiBgp7WTfl6K5ZcyHj8r8JsN/J6QODkAsRRCLIcTuOCu8etkUw== + version "1.17.0" + resolved "https://registry.npmjs.org/@types/webpack-env/-/webpack-env-1.17.0.tgz#f99ce359f1bfd87da90cc4a57cab0a18f34a48d0" + integrity sha512-eHSaNYEyxRA5IAG0Ym/yCyf86niZUIF/TpWKofQI/CVfh5HsMEUyfE2kwFxha4ow0s5g0LfISQxpDKjbRDrizw== "@types/webpack-sources@*": version "3.2.0" @@ -2248,6 +2189,14 @@ anymatch "^3.0.0" source-map "^0.6.0" +"@webassemblyjs/ast@1.11.1": + version "1.11.1" + resolved "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz#2bfd767eae1a6996f432ff7e8d7fc75679c0b6a7" + integrity sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw== + dependencies: + "@webassemblyjs/helper-numbers" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + "@webassemblyjs/ast@1.9.0": version "1.9.0" resolved "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz#bd850604b4042459a5a41cd7d338cbed695ed964" @@ -2257,16 +2206,31 @@ "@webassemblyjs/helper-wasm-bytecode" "1.9.0" "@webassemblyjs/wast-parser" "1.9.0" +"@webassemblyjs/floating-point-hex-parser@1.11.1": + version "1.11.1" + resolved "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz#f6c61a705f0fd7a6aecaa4e8198f23d9dc179e4f" + integrity sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ== + "@webassemblyjs/floating-point-hex-parser@1.9.0": version "1.9.0" resolved "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz#3c3d3b271bddfc84deb00f71344438311d52ffb4" integrity sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA== +"@webassemblyjs/helper-api-error@1.11.1": + version "1.11.1" + resolved "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz#1a63192d8788e5c012800ba6a7a46c705288fd16" + integrity sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg== + "@webassemblyjs/helper-api-error@1.9.0": version "1.9.0" resolved "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz#203f676e333b96c9da2eeab3ccef33c45928b6a2" integrity sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw== +"@webassemblyjs/helper-buffer@1.11.1": + version "1.11.1" + resolved "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz#832a900eb444884cde9a7cad467f81500f5e5ab5" + integrity sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA== + "@webassemblyjs/helper-buffer@1.9.0": version "1.9.0" resolved "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz#a1442d269c5feb23fcbc9ef759dac3547f29de00" @@ -2291,11 +2255,35 @@ dependencies: "@webassemblyjs/ast" "1.9.0" +"@webassemblyjs/helper-numbers@1.11.1": + version "1.11.1" + resolved "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz#64d81da219fbbba1e3bd1bfc74f6e8c4e10a62ae" + integrity sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ== + dependencies: + "@webassemblyjs/floating-point-hex-parser" "1.11.1" + "@webassemblyjs/helper-api-error" "1.11.1" + "@xtuc/long" "4.2.2" + +"@webassemblyjs/helper-wasm-bytecode@1.11.1": + version "1.11.1" + resolved "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz#f328241e41e7b199d0b20c18e88429c4433295e1" + integrity sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q== + "@webassemblyjs/helper-wasm-bytecode@1.9.0": version "1.9.0" resolved "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz#4fed8beac9b8c14f8c58b70d124d549dd1fe5790" integrity sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw== +"@webassemblyjs/helper-wasm-section@1.11.1": + version "1.11.1" + resolved "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz#21ee065a7b635f319e738f0dd73bfbda281c097a" + integrity sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-buffer" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + "@webassemblyjs/wasm-gen" "1.11.1" + "@webassemblyjs/helper-wasm-section@1.9.0": version "1.9.0" resolved "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz#5a4138d5a6292ba18b04c5ae49717e4167965346" @@ -2306,6 +2294,13 @@ "@webassemblyjs/helper-wasm-bytecode" "1.9.0" "@webassemblyjs/wasm-gen" "1.9.0" +"@webassemblyjs/ieee754@1.11.1": + version "1.11.1" + resolved "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz#963929e9bbd05709e7e12243a099180812992614" + integrity sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ== + dependencies: + "@xtuc/ieee754" "^1.2.0" + "@webassemblyjs/ieee754@1.9.0": version "1.9.0" resolved "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz#15c7a0fbaae83fb26143bbacf6d6df1702ad39e4" @@ -2313,6 +2308,13 @@ dependencies: "@xtuc/ieee754" "^1.2.0" +"@webassemblyjs/leb128@1.11.1": + version "1.11.1" + resolved "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz#ce814b45574e93d76bae1fb2644ab9cdd9527aa5" + integrity sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw== + dependencies: + "@xtuc/long" "4.2.2" + "@webassemblyjs/leb128@1.9.0": version "1.9.0" resolved "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.9.0.tgz#f19ca0b76a6dc55623a09cffa769e838fa1e1c95" @@ -2320,11 +2322,30 @@ dependencies: "@xtuc/long" "4.2.2" +"@webassemblyjs/utf8@1.11.1": + version "1.11.1" + resolved "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz#d1f8b764369e7c6e6bae350e854dec9a59f0a3ff" + integrity sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ== + "@webassemblyjs/utf8@1.9.0": version "1.9.0" resolved "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.9.0.tgz#04d33b636f78e6a6813227e82402f7637b6229ab" integrity sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w== +"@webassemblyjs/wasm-edit@1.11.1": + version "1.11.1" + resolved "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz#ad206ebf4bf95a058ce9880a8c092c5dec8193d6" + integrity sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-buffer" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + "@webassemblyjs/helper-wasm-section" "1.11.1" + "@webassemblyjs/wasm-gen" "1.11.1" + "@webassemblyjs/wasm-opt" "1.11.1" + "@webassemblyjs/wasm-parser" "1.11.1" + "@webassemblyjs/wast-printer" "1.11.1" + "@webassemblyjs/wasm-edit@1.9.0": version "1.9.0" resolved "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz#3fe6d79d3f0f922183aa86002c42dd256cfee9cf" @@ -2339,6 +2360,17 @@ "@webassemblyjs/wasm-parser" "1.9.0" "@webassemblyjs/wast-printer" "1.9.0" +"@webassemblyjs/wasm-gen@1.11.1": + version "1.11.1" + resolved "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz#86c5ea304849759b7d88c47a32f4f039ae3c8f76" + integrity sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + "@webassemblyjs/ieee754" "1.11.1" + "@webassemblyjs/leb128" "1.11.1" + "@webassemblyjs/utf8" "1.11.1" + "@webassemblyjs/wasm-gen@1.9.0": version "1.9.0" resolved "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz#50bc70ec68ded8e2763b01a1418bf43491a7a49c" @@ -2350,6 +2382,16 @@ "@webassemblyjs/leb128" "1.9.0" "@webassemblyjs/utf8" "1.9.0" +"@webassemblyjs/wasm-opt@1.11.1": + version "1.11.1" + resolved "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz#657b4c2202f4cf3b345f8a4c6461c8c2418985f2" + integrity sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-buffer" "1.11.1" + "@webassemblyjs/wasm-gen" "1.11.1" + "@webassemblyjs/wasm-parser" "1.11.1" + "@webassemblyjs/wasm-opt@1.9.0": version "1.9.0" resolved "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz#2211181e5b31326443cc8112eb9f0b9028721a61" @@ -2360,6 +2402,18 @@ "@webassemblyjs/wasm-gen" "1.9.0" "@webassemblyjs/wasm-parser" "1.9.0" +"@webassemblyjs/wasm-parser@1.11.1": + version "1.11.1" + resolved "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz#86ca734534f417e9bd3c67c7a1c75d8be41fb199" + integrity sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-api-error" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + "@webassemblyjs/ieee754" "1.11.1" + "@webassemblyjs/leb128" "1.11.1" + "@webassemblyjs/utf8" "1.11.1" + "@webassemblyjs/wasm-parser@1.9.0": version "1.9.0" resolved "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz#9d48e44826df4a6598294aa6c87469d642fff65e" @@ -2384,6 +2438,14 @@ "@webassemblyjs/helper-fsm" "1.9.0" "@xtuc/long" "4.2.2" +"@webassemblyjs/wast-printer@1.11.1": + version "1.11.1" + resolved "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz#d0c73beda8eec5426f10ae8ef55cee5e7084c2f0" + integrity sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@xtuc/long" "4.2.2" + "@webassemblyjs/wast-printer@1.9.0": version "1.9.0" resolved "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz#4935d54c85fef637b00ce9f52377451d00d47899" @@ -2411,20 +2473,40 @@ accepts@~1.3.5, accepts@~1.3.8: mime-types "~2.1.34" negotiator "0.6.3" +acorn-import-assertions@^1.7.6: + version "1.8.0" + resolved "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz#ba2b5939ce62c238db6d93d81c9b111b29b855e9" + integrity sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw== + +acorn-jsx@^5.3.1: + version "5.3.2" + resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + +acorn-walk@^7.2.0: + version "7.2.0" + resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" + integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== + acorn@^6.4.1: version "6.4.2" resolved "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ== -acorn@^8.5.0: - version "8.7.0" - resolved "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz#90951fde0f8f09df93549481e5fc141445b791cf" - integrity sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ== +acorn@^7.4.1: + version "7.4.1" + resolved "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" + integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== + +acorn@^8.4.1, acorn@^8.5.0: + version "8.7.1" + resolved "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz#0197122c843d1bf6d0a5e83220a788f278f63c30" + integrity sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A== address@^1.0.1: - version "1.1.2" - resolved "https://registry.npmjs.org/address/-/address-1.1.2.tgz#bf1116c9c758c51b7a933d296b72c221ed9428b6" - integrity sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA== + version "1.2.0" + resolved "https://registry.npmjs.org/address/-/address-1.2.0.tgz#d352a62c92fee90f89a693eccd2a8b2139ab02d9" + integrity sha512-tNEZYz5G/zYunxFm7sfhAxkXEuLj3K6BKwv6ZURlsF6yiUQ65z0Q2wZW9L5cPUl9ocofGvXOdFYbFHp0+6MOig== aggregate-error@^3.0.0: version "3.1.0" @@ -2497,7 +2579,7 @@ ansi-html-community@0.0.8, ansi-html-community@^0.0.8: ansi-regex@^2.0.0: version "2.1.1" resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" - integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= + integrity sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA== ansi-regex@^5.0.1: version "5.0.1" @@ -2549,7 +2631,7 @@ anymatch@^3.0.0, anymatch@~3.1.2: app-root-dir@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/app-root-dir/-/app-root-dir-1.0.2.tgz#38187ec2dea7577fff033ffcb12172692ff6e118" - integrity sha1-OBh+wt6nV3//Az/8sSFyaS/24Rg= + integrity sha512-jlpIfsOoNoafl92Sz//64uQHGSyMrD2vYG5d8o2a4qGvyNCvXur7bzIsWtAC/6flI2RYAp3kv8rsfBtaLm7w0g== "aproba@^1.0.3 || ^2.0.0": version "2.0.0" @@ -2572,7 +2654,7 @@ are-we-there-yet@^2.0.0: arr-diff@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" - integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA= + integrity sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA== arr-flatten@^1.1.0: version "1.1.0" @@ -2582,28 +2664,33 @@ arr-flatten@^1.1.0: arr-union@^3.1.0: version "3.1.0" resolved "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" - integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= + integrity sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q== + +array-find-index@^1.0.1: + version "1.0.2" + resolved "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" + integrity sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw== array-flatten@1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" - integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= + integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== array-includes@^3.0.3: - version "3.1.4" - resolved "https://registry.npmjs.org/array-includes/-/array-includes-3.1.4.tgz#f5b493162c760f3539631f005ba2bb46acb45ba9" - integrity sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw== + version "3.1.5" + resolved "https://registry.npmjs.org/array-includes/-/array-includes-3.1.5.tgz#2c320010db8d31031fd2a5f6b3bbd4b1aad31bdb" + integrity sha512-iSDYZMMyTPkiFasVqfuAQnWAYcvO/SeBSCGKePoEthjp4LEMTe4uLc7b025o4jAZpHhihh8xPo99TNWUWWkGDQ== dependencies: call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.1" + define-properties "^1.1.4" + es-abstract "^1.19.5" get-intrinsic "^1.1.1" is-string "^1.0.7" array-union@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" - integrity sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk= + integrity sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng== dependencies: array-uniq "^1.0.1" @@ -2615,12 +2702,12 @@ array-union@^2.1.0: array-uniq@^1.0.1: version "1.0.3" resolved "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" - integrity sha1-r2rId6Jcx/dOBYiUdThY39sk/bY= + integrity sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q== array-unique@^0.3.2: version "0.3.2" resolved "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" - integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= + integrity sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ== array.prototype.flat@^1.2.1: version "1.3.0" @@ -2653,6 +2740,17 @@ array.prototype.map@^1.0.4: es-array-method-boxes-properly "^1.0.0" is-string "^1.0.7" +array.prototype.reduce@^1.0.4: + version "1.0.4" + resolved "https://registry.npmjs.org/array.prototype.reduce/-/array.prototype.reduce-1.0.4.tgz#8167e80089f78bff70a99e20bd4201d4663b0a6f" + integrity sha512-WnM+AjG/DvLRLo4DDl+r+SvCzYtD2Jd9oeBYMcEaI7t3fFrHY9M53/wdLcTvmZNQ70IU6Htj0emFkZ5TS+lrdw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.2" + es-array-method-boxes-properly "^1.0.0" + is-string "^1.0.7" + arrify@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa" @@ -2679,7 +2777,7 @@ assert@^1.1.1: assign-symbols@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" - integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= + integrity sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw== ast-types@^0.14.2: version "0.14.2" @@ -2696,7 +2794,7 @@ async-each@^1.0.1: asynckit@^0.4.0: version "0.4.0" resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== at-least-node@^1.0.0: version "1.0.0" @@ -2722,14 +2820,14 @@ autoprefixer@^9.8.6: postcss-value-parser "^4.1.0" axe-core@^4.2.0: - version "4.4.1" - resolved "https://registry.npmjs.org/axe-core/-/axe-core-4.4.1.tgz#7dbdc25989298f9ad006645cd396782443757413" - integrity sha512-gd1kmb21kwNuWr6BQz8fv6GNECPBnUasepcoLbekws23NVBLODdsClRZ+bQ8+9Uomf3Sm3+Vwn0oYG9NvwnJCw== + version "4.4.2" + resolved "https://registry.npmjs.org/axe-core/-/axe-core-4.4.2.tgz#dcf7fb6dea866166c3eab33d68208afe4d5f670c" + integrity sha512-LVAaGp/wkkgYJcjmHsoKx4juT1aQvJyPcW09MLCjVTh3V2cc6PnyempiLMNH5iMdfIX/zdbjUx2KDjMLCTdPeA== babel-loader@^8.0.0: - version "8.2.4" - resolved "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.4.tgz#95f5023c791b2e9e2ca6f67b0984f39c82ff384b" - integrity sha512-8dytA3gcvPPPv4Grjhnt8b5IIiTcq/zeXOPk4iTYI0SVXcsmuGg7JtBRDp8S9X+gJfhQ8ektjXZlDu1Bb33U8A== + version "8.2.5" + resolved "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.5.tgz#d45f585e654d5a5d90f5350a779d7647c5ed512e" + integrity sha512-OSiFfH89LrEMiWd4pLNqGz4CwJDtbs2ZVc+iGu2HrkRfPxId9F2anQj38IxWpmRfsUY0aBZYi1EFcd3mhtRMLQ== dependencies: find-cache-dir "^3.3.1" loader-utils "^2.0.0" @@ -2739,7 +2837,7 @@ babel-loader@^8.0.0: babel-plugin-add-react-displayname@^0.0.5: version "0.0.5" resolved "https://registry.npmjs.org/babel-plugin-add-react-displayname/-/babel-plugin-add-react-displayname-0.0.5.tgz#339d4cddb7b65fd62d1df9db9fe04de134122bd5" - integrity sha1-M51M3be2X9YtHfnbn+BN4TQSK9U= + integrity sha512-LY3+Y0XVDYcShHHorshrDbt4KFWL4bSeniCtl4SYZbask+Syngk1uMPCeN9+nSiZo6zX5s0RTq/J9Pnaaf/KHw== babel-plugin-apply-mdx-type-prop@1.6.22: version "1.6.22" @@ -2756,22 +2854,6 @@ babel-plugin-dynamic-import-node@^2.3.3: dependencies: object.assign "^4.1.0" -babel-plugin-emotion@^10.0.27: - version "10.2.2" - resolved "https://registry.npmjs.org/babel-plugin-emotion/-/babel-plugin-emotion-10.2.2.tgz#a1fe3503cff80abfd0bdda14abd2e8e57a79d17d" - integrity sha512-SMSkGoqTbTyUTDeuVuPIWifPdUGkTk1Kf9BWRiXIOIcuyMfsdp2EjeiiFvOzX8NOBvEh/ypKYvUh2rkgAJMCLA== - dependencies: - "@babel/helper-module-imports" "^7.0.0" - "@emotion/hash" "0.8.0" - "@emotion/memoize" "0.7.4" - "@emotion/serialize" "^0.11.16" - babel-plugin-macros "^2.0.0" - babel-plugin-syntax-jsx "^6.18.0" - convert-source-map "^1.5.0" - escape-string-regexp "^1.0.5" - find-root "^1.1.0" - source-map "^0.5.7" - babel-plugin-extract-import-names@1.6.22: version "1.6.22" resolved "https://registry.npmjs.org/babel-plugin-extract-import-names/-/babel-plugin-extract-import-names-1.6.22.tgz#de5f9a28eb12f3eb2578bf74472204e66d1a13dc" @@ -2779,15 +2861,6 @@ babel-plugin-extract-import-names@1.6.22: dependencies: "@babel/helper-plugin-utils" "7.10.4" -babel-plugin-macros@^2.0.0, babel-plugin-macros@^2.8.0: - version "2.8.0" - resolved "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz#0f958a7cc6556b1e65344465d99111a1e5e10138" - integrity sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg== - dependencies: - "@babel/runtime" "^7.7.2" - cosmiconfig "^6.0.0" - resolve "^1.12.0" - babel-plugin-macros@^3.0.1: version "3.1.0" resolved "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz#9ef6dc74deb934b4db344dc973ee851d148c50c1" @@ -2797,11 +2870,6 @@ babel-plugin-macros@^3.0.1: cosmiconfig "^7.0.0" resolve "^1.19.0" -babel-plugin-named-asset-import@^0.3.1: - version "0.3.8" - resolved "https://registry.npmjs.org/babel-plugin-named-asset-import/-/babel-plugin-named-asset-import-0.3.8.tgz#6b7fa43c59229685368683c28bc9734f24524cc2" - integrity sha512-WXiAc++qo7XcJ1ZnTYGtLxmBCVbddAml3CEXgWaBzNzLNoxtQ8AiGEFDMOhot9XjTCQbvP5E77Fj9Gk924f00Q== - babel-plugin-polyfill-corejs2@^0.3.0: version "0.3.1" resolved "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.1.tgz#440f1b70ccfaabc6b676d196239b138f8a2cfba5" @@ -2843,11 +2911,6 @@ babel-plugin-react-docgen@^4.2.1: lodash "^4.17.15" react-docgen "^5.0.0" -babel-plugin-syntax-jsx@^6.18.0: - version "6.18.0" - resolved "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz#0af32a9a6e13ca7a3fd5069e62d7b0f58d0d8946" - integrity sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY= - bail@^1.0.0: version "1.0.5" resolved "https://registry.npmjs.org/bail/-/bail-1.0.5.tgz#b6fa133404a392cbc1f8c4bf63f5953351e7a776" @@ -2879,7 +2942,7 @@ base@^0.11.1: batch-processor@1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/batch-processor/-/batch-processor-1.0.0.tgz#75c95c32b748e0850d10c2b168f6bdbe9891ace8" - integrity sha1-dclcMrdI4IUNEMKxaPa9vpiRrOg= + integrity sha512-xoLQD8gmmR32MeuBHgH0Tzd5PuSZx71ZsbhVxOCRbgktZEPe4SQy7s9Z50uPp0F/f7iw2XmkHN2xkgbMfckMDA== better-opn@^2.1.1: version "2.1.1" @@ -2888,6 +2951,11 @@ better-opn@^2.1.1: dependencies: open "^7.0.3" +big-integer@^1.6.7: + version "1.6.51" + resolved "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz#0df92a5d9880560d3ff2d5fd20245c889d130686" + integrity sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg== + big.js@^5.2.2: version "5.2.2" resolved "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" @@ -2910,7 +2978,7 @@ bindings@^1.5.0: dependencies: file-uri-to-path "1.0.0" -bluebird@^3.3.5, bluebird@^3.5.5: +bluebird@^3.5.5: version "3.7.2" resolved "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== @@ -2921,30 +2989,32 @@ bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.9: integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== bn.js@^5.0.0, bn.js@^5.1.1: - version "5.2.0" - resolved "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002" - integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw== + version "5.2.1" + resolved "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" + integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== -body-parser@1.19.2: - version "1.19.2" - resolved "https://registry.npmjs.org/body-parser/-/body-parser-1.19.2.tgz#4714ccd9c157d44797b8b5607d72c0b89952f26e" - integrity sha512-SAAwOxgoCKMGs9uUAUFHygfLAyaniaoun6I8mFY9pRAJL9+Kec34aU+oIjDhTycub1jozEfEwx1W1IuOYxVSFw== +body-parser@1.20.0: + version "1.20.0" + resolved "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz#3de69bd89011c11573d7bfee6a64f11b6bd27cc5" + integrity sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg== dependencies: bytes "3.1.2" content-type "~1.0.4" debug "2.6.9" - depd "~1.1.2" - http-errors "1.8.1" + depd "2.0.0" + destroy "1.2.0" + http-errors "2.0.0" iconv-lite "0.4.24" - on-finished "~2.3.0" - qs "6.9.7" - raw-body "2.4.3" + on-finished "2.4.1" + qs "6.10.3" + raw-body "2.5.1" type-is "~1.6.18" + unpipe "1.0.0" boolbase@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" - integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= + integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww== boxen@^5.1.2: version "5.1.2" @@ -2960,6 +3030,13 @@ boxen@^5.1.2: widest-line "^3.1.0" wrap-ansi "^7.0.0" +bplist-parser@^0.1.0: + version "0.1.1" + resolved "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.1.1.tgz#d60d5dcc20cba6dc7e1f299b35d3e1f95dafbae6" + integrity sha512-2AEM0FXy8ZxVLBuqX0hqt1gDwcnz2zygEkQ6zaD5Wko/sB9paUNwlpawrFtKeHUAQUOzjVy9AO4oeonqIHKA9Q== + dependencies: + big-integer "^1.6.7" + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -2994,7 +3071,7 @@ braces@^3.0.2, braces@~3.0.2: brorand@^1.0.1, brorand@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" - integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= + integrity sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w== browserify-aes@^1.0.0, browserify-aes@^1.0.4: version "1.2.0" @@ -3057,15 +3134,15 @@ browserify-zlib@^0.2.0: dependencies: pako "~1.0.5" -browserslist@^4.12.0, browserslist@^4.17.5, browserslist@^4.20.2: - version "4.20.2" - resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.20.2.tgz#567b41508757ecd904dab4d1c646c612cd3d4f88" - integrity sha512-CQOBCqp/9pDvDbx3xfMi+86pr4KXIf2FDkTTdeuYw8OxS9t898LA1Khq57gtufFILXpfgsSx5woNgsBgvGjpsA== +browserslist@^4.12.0, browserslist@^4.14.5, browserslist@^4.20.2, browserslist@^4.20.4: + version "4.20.4" + resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.20.4.tgz#98096c9042af689ee1e0271333dbc564b8ce4477" + integrity sha512-ok1d+1WpnU24XYN7oC3QWgTyMhY/avPJ/r9T00xxvUOIparA/gc+UPUMaod3i+G6s+nI2nUb9xZ5k794uIwShw== dependencies: - caniuse-lite "^1.0.30001317" - electron-to-chromium "^1.4.84" + caniuse-lite "^1.0.30001349" + electron-to-chromium "^1.4.147" escalade "^3.1.1" - node-releases "^2.0.2" + node-releases "^2.0.5" picocolors "^1.0.0" buffer-from@^1.0.0: @@ -3076,7 +3153,7 @@ buffer-from@^1.0.0: buffer-xor@^1.0.3: version "1.0.3" resolved "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" - integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk= + integrity sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ== buffer@^4.3.0: version "4.9.2" @@ -3090,12 +3167,12 @@ buffer@^4.3.0: builtin-status-codes@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" - integrity sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug= + integrity sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ== bytes@3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" - integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg= + integrity sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw== bytes@3.1.2: version "3.1.2" @@ -3103,22 +3180,22 @@ bytes@3.1.2: integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== c8@^7.6.0: - version "7.11.0" - resolved "https://registry.npmjs.org/c8/-/c8-7.11.0.tgz#b3ab4e9e03295a102c47ce11d4ef6d735d9a9ac9" - integrity sha512-XqPyj1uvlHMr+Y1IeRndC2X5P7iJzJlEJwBpCdBbq2JocXOgJfr+JVfJkyNMGROke5LfKrhSFXGFXnwnRJAUJw== + version "7.11.3" + resolved "https://registry.npmjs.org/c8/-/c8-7.11.3.tgz#88c8459c1952ed4f701b619493c9ae732b057163" + integrity sha512-6YBmsaNmqRm9OS3ZbIiL2EZgi1+Xc4O24jL3vMYGE6idixYuGdy76rIfIdltSKDj9DpLNrcXSonUTR1miBD0wA== dependencies: "@bcoe/v8-coverage" "^0.2.3" - "@istanbuljs/schema" "^0.1.2" + "@istanbuljs/schema" "^0.1.3" find-up "^5.0.0" foreground-child "^2.0.0" - istanbul-lib-coverage "^3.0.1" + istanbul-lib-coverage "^3.2.0" istanbul-lib-report "^3.0.0" - istanbul-reports "^3.0.2" - rimraf "^3.0.0" + istanbul-reports "^3.1.4" + rimraf "^3.0.2" test-exclude "^6.0.0" - v8-to-istanbul "^8.0.0" + v8-to-istanbul "^9.0.0" yargs "^16.2.0" - yargs-parser "^20.2.7" + yargs-parser "^20.2.9" cacache@^12.0.2: version "12.0.4" @@ -3191,7 +3268,7 @@ call-bind@^1.0.0, call-bind@^1.0.2: call-me-maybe@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz#26d208ea89e37b5cbde60250a15f031c16a4d66b" - integrity sha1-JtII6onje1y95gJQoV8DHBak1ms= + integrity sha512-wCyFsDQkKPwwF8BDwOiWNx/9K45L/hvggQiDbve+viMNMQnWhrlYIuBk09offfwCRtCO9P6XwUttufzU11WCVw== callsites@^3.0.0: version "3.1.0" @@ -3211,6 +3288,19 @@ camelcase-css@2.0.1: resolved "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz#ee978f6947914cc30c6b44741b6ed1df7f043fd5" integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA== +camelcase-keys@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7" + integrity sha512-bA/Z/DERHKqoEOrp+qeGKw1QlvEQkGZSc0XaY6VnTxZr+Kv1G5zFwttpjv8qxZ/sBPT4nthwZaAcsAZTJlSKXQ== + dependencies: + camelcase "^2.0.0" + map-obj "^1.0.0" + +camelcase@^2.0.0: + version "2.1.1" + resolved "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" + integrity sha512-DLIsRzJVBQu72meAKPkWQOLcujdXT32hwdfnkI1frSiSRMK1MofjKHf+MEx0SB6fjEFXL8fBDv1dKymBlOp4Qw== + camelcase@^5.3.1: version "5.3.1" resolved "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" @@ -3221,10 +3311,10 @@ camelcase@^6.2.0: resolved "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== -caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001317: - version "1.0.30001332" - resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001332.tgz#39476d3aa8d83ea76359c70302eafdd4a1d727dd" - integrity sha512-10T30NYOEQtN6C11YGg411yebhvpnC6Z102+B95eAsN0oB6KUs01ivE8u+G6FMIRtIrVlYXhL+LUwQ3/hXwDWw== +caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001349: + version "1.0.30001354" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001354.tgz#95c5efdb64148bb4870771749b9a619304755ce5" + integrity sha512-mImKeCkyGDAHNywYFA4bqnLAzTUvVkqPvhY4DV47X+Gl2c5Z8c3KNETnXp14GQt11LvxE8AwjzGxJ+rsikiOzg== case-sensitive-paths-webpack-plugin@^2.3.0: version "2.4.0" @@ -3384,11 +3474,6 @@ clone-deep@^4.0.1: kind-of "^6.0.2" shallow-clone "^3.0.0" -clsx@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz#98b3134f9abbdf23b2663491ace13c5c03a73188" - integrity sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA== - collapse-white-space@^1.0.2: version "1.0.6" resolved "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-1.0.6.tgz#e63629c0016665792060dbbeb79c42239d2c5287" @@ -3397,7 +3482,7 @@ collapse-white-space@^1.0.2: collection-visit@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" - integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA= + integrity sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw== dependencies: map-visit "^1.0.0" object-visit "^1.0.0" @@ -3419,7 +3504,7 @@ color-convert@^2.0.1: color-name@1.1.3: version "1.1.3" resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" - integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== color-name@~1.1.4: version "1.1.4" @@ -3466,7 +3551,7 @@ common-path-prefix@^3.0.0: commondir@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" - integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= + integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg== component-emitter@^1.2.1: version "1.3.0" @@ -3493,15 +3578,10 @@ compression@^1.7.4: safe-buffer "5.1.2" vary "~1.1.2" -compute-scroll-into-view@^1.0.17: - version "1.0.17" - resolved "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-1.0.17.tgz#6a88f18acd9d42e9cf4baa6bec7e0522607ab7ab" - integrity sha512-j4dx+Fb0URmzbwwMUrhqWM2BEWHdFGx+qZ9qqASHRPqvTYdqvWnHg0H1hIbcyLnvgnoNAVMlwkepyqM3DaIFUg== - concat-map@0.0.1: version "0.0.1" resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== concat-stream@^1.5.0: version "1.6.2" @@ -3521,12 +3601,12 @@ console-browserify@^1.1.0: console-control-strings@^1.0.0, console-control-strings@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" - integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= + integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ== constants-browserify@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" - integrity sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U= + integrity sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ== content-disposition@0.5.4: version "0.5.4" @@ -3540,7 +3620,7 @@ content-type@~1.0.4: resolved "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== -convert-source-map@^1.5.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: +convert-source-map@^1.6.0, convert-source-map@^1.7.0: version "1.8.0" resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369" integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA== @@ -3550,12 +3630,12 @@ convert-source-map@^1.5.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: cookie-signature@1.0.6: version "1.0.6" resolved "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" - integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= + integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== -cookie@0.4.2: - version "0.4.2" - resolved "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" - integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== +cookie@0.5.0: + version "0.5.0" + resolved "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" + integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== copy-concurrently@^1.0.0: version "1.0.5" @@ -3572,32 +3652,25 @@ copy-concurrently@^1.0.0: copy-descriptor@^0.1.0: version "0.1.1" resolved "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" - integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= + integrity sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw== -copy-to-clipboard@^3.3.1: - version "3.3.1" - resolved "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.1.tgz#115aa1a9998ffab6196f93076ad6da3b913662ae" - integrity sha512-i13qo6kIHTTpCm8/Wup+0b1mVWETvu2kIMzKoK8FpkLkFxlt0znUAHcMzox+T8sPlqtZXq3CulEjQHsYiGFJUw== +core-js-compat@^3.21.0, core-js-compat@^3.22.1, core-js-compat@^3.8.1: + version "3.23.1" + resolved "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.23.1.tgz#23d44d9f209086e60dabf9130cea7719af6e199b" + integrity sha512-KeYrEc8t6FJsKYB2qnDwRHWaC0cJNaqlHfCpMe5q3j/W1nje3moib/txNklddLPCtGb+etcBIyJ8zuMa/LN5/A== dependencies: - toggle-selection "^1.0.6" - -core-js-compat@^3.20.2, core-js-compat@^3.21.0, core-js-compat@^3.8.1: - version "3.22.0" - resolved "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.22.0.tgz#7ce17ab57c378be2c717c7c8ed8f82a50a25b3e4" - integrity sha512-WwA7xbfRGrk8BGaaHlakauVXrlYmAIkk8PNGb1FDQS+Rbrewc3pgFfwJFRw6psmJVAll7Px9UHRYE16oRQnwAQ== - dependencies: - browserslist "^4.20.2" + browserslist "^4.20.4" semver "7.0.0" -core-js-pure@^3.8.1, core-js-pure@^3.8.2: - version "3.22.0" - resolved "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.22.0.tgz#0eaa54b6d1f4ebb4d19976bb4916dfad149a3747" - integrity sha512-ylOC9nVy0ak1N+fPIZj00umoZHgUVqmucklP5RT5N+vJof38klKn8Ze6KGyvchdClvEBr6LcQqJpI216LUMqYA== +core-js-pure@^3.8.1: + version "3.23.1" + resolved "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.23.1.tgz#0b27e4c3ad46178b84e790dbbb81987218ab82ad" + integrity sha512-3qNgf6TqI3U1uhuSYRzJZGfFd4T+YlbyVPl+jgRiKjdZopvG4keZQwWZDAWpu1UH9nCgTpUzIV3GFawC7cJsqg== core-js@^3.0.4, core-js@^3.6.5, core-js@^3.8.2: - version "3.22.0" - resolved "https://registry.npmjs.org/core-js/-/core-js-3.22.0.tgz#b52007870c5e091517352e833b77f0b2d2b259f3" - integrity sha512-8h9jBweRjMiY+ORO7bdWSeWfHhLPO7whobj7Z2Bl0IDo00C228EdGgH7FE4jGumbEjzcFfkfW8bXgdkEDhnwHQ== + version "3.23.1" + resolved "https://registry.npmjs.org/core-js/-/core-js-3.23.1.tgz#9f9a9255115f62c512db56d567f636da32ca0b78" + integrity sha512-wfMYHWi1WQjpgZNC9kAlN4ut04TM9fUTdi7CqIoTVM7yaiOUQTklOzfb+oWH3r9edQcT3F887swuVmxrV+CC8w== core-util-is@~1.0.0: version "1.0.3" @@ -3682,7 +3755,7 @@ create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: safe-buffer "^5.0.1" sha.js "^2.4.8" -cross-spawn@^7.0.0: +cross-spawn@^7.0.0, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -3748,20 +3821,22 @@ cssesc@^3.0.0: resolved "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== -csstype@^2.5.7: - version "2.6.20" - resolved "https://registry.npmjs.org/csstype/-/csstype-2.6.20.tgz#9229c65ea0b260cf4d3d997cb06288e36a8d6dda" - integrity sha512-/WwNkdXfckNgw6S5R125rrW8ez139lBHWouiBvX8dfMFtcn6V81REDqnH7+CRpRipfYlyU1CmOnOxrmGcFOjeA== - csstype@^3.0.2: - version "3.0.11" - resolved "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz#d66700c5eacfac1940deb4e3ee5642792d85cd33" - integrity sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw== + version "3.1.0" + resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz#4ddcac3718d787cf9df0d1b7d15033925c8f29f2" + integrity sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA== + +currently-unhandled@^0.4.1: + version "0.4.1" + resolved "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" + integrity sha512-/fITjgjGU50vjQ4FH6eUoYu+iUoUKIXws2hL15JJpIR+BbTxaXQsMuuyjtNh2WqsSBS5nsaZHFsFecyw5CCAng== + dependencies: + array-find-index "^1.0.1" cyclist@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9" - integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk= + integrity sha512-NJGVKPS81XejHcLhaLJS7plab0fK3slPh11mESeeDq2W4ZI5kUKK/LRRdVDvjJseojbPB7ZwjnyOybg3Igea/A== debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0: version "2.6.9" @@ -3784,27 +3859,46 @@ debug@^4.1.0, debug@^4.1.1: dependencies: ms "2.1.2" +decamelize@^1.1.2: + version "1.2.0" + resolved "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== + decode-uri-component@^0.2.0: version "0.2.0" resolved "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" - integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= + integrity sha512-hjf+xovcEn31w/EUYdTXQh/8smFL/dzYjohQGEIgjyNavaJfBY2p5F527Bo1VPATxv0VYTUC2bOcXvqFwk78Og== dedent@^0.7.0: version "0.7.0" resolved "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" - integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw= + integrity sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA== -deep-object-diff@^1.1.0: - version "1.1.7" - resolved "https://registry.npmjs.org/deep-object-diff/-/deep-object-diff-1.1.7.tgz#348b3246f426427dd633eaa50e1ed1fc2eafc7e4" - integrity sha512-QkgBca0mL08P6HiOjoqvmm6xOAl2W6CT2+34Ljhg0OeFan8cwlcdq8jrLKsBBuUFAZLsN5b6y491KdKEoSo9lg== +deep-is@~0.1.3: + version "0.1.4" + resolved "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== deepmerge@^4.2.2: version "4.2.2" resolved "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== -define-properties@^1.1.2, define-properties@^1.1.3: +default-browser-id@^1.0.4: + version "1.0.4" + resolved "https://registry.npmjs.org/default-browser-id/-/default-browser-id-1.0.4.tgz#e59d09a5d157b828b876c26816e61c3d2a2c203a" + integrity sha512-qPy925qewwul9Hifs+3sx1ZYn14obHxpkX+mPD369w4Rzg+YkJBgi3SOvwUq81nWSjqGUegIgEPwD8u+HUnxlw== + dependencies: + bplist-parser "^0.1.0" + meow "^3.1.0" + untildify "^2.0.0" + +define-lazy-prop@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" + integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== + +define-properties@^1.1.2, define-properties@^1.1.3, define-properties@^1.1.4: version "1.1.4" resolved "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz#0b14d7bd7fbeb2f3572c3a7eda80ea5d57fb05b1" integrity sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA== @@ -3815,14 +3909,14 @@ define-properties@^1.1.2, define-properties@^1.1.3: define-property@^0.2.5: version "0.2.5" resolved "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" - integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY= + integrity sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA== dependencies: is-descriptor "^0.1.0" define-property@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" - integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY= + integrity sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA== dependencies: is-descriptor "^1.0.0" @@ -3837,17 +3931,17 @@ define-property@^2.0.2: delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== delegates@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" - integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= + integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ== -depd@~1.1.2: - version "1.1.2" - resolved "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" - integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= +depd@2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== des.js@^1.0.0: version "1.0.1" @@ -3857,10 +3951,10 @@ des.js@^1.0.0: inherits "^2.0.1" minimalistic-assert "^1.0.0" -destroy@~1.0.4: - version "1.0.4" - resolved "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" - integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= +destroy@1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" + integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== detab@2.0.4: version "2.0.4" @@ -3869,6 +3963,13 @@ detab@2.0.4: dependencies: repeat-string "^1.5.4" +detect-package-manager@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/detect-package-manager/-/detect-package-manager-2.0.1.tgz#6b182e3ae5e1826752bfef1de9a7b828cffa50d8" + integrity sha512-j/lJHyoLlWi6G1LDdLgvUtz60Zo5GEj+sVYtTVXnYLDPuzgC3llMxonXym9zIwhhUII8vjdw0LXxavpLqTbl1A== + dependencies: + execa "^5.1.1" + detect-port@^1.3.0: version "1.3.0" resolved "https://registry.npmjs.org/detect-port/-/detect-port-1.3.0.tgz#d9c40e9accadd4df5cac6a782aefd014d573d1f1" @@ -3972,17 +4073,6 @@ dotenv@^8.0.0: resolved "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz#061af664d19f7f4d8fc6e4ff9b584ce237adcb8b" integrity sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g== -downshift@^6.0.15: - version "6.1.7" - resolved "https://registry.npmjs.org/downshift/-/downshift-6.1.7.tgz#fdb4c4e4f1d11587985cd76e21e8b4b3fa72e44c" - integrity sha512-cVprZg/9Lvj/uhYRxELzlu1aezRcgPWBjTvspiGTVEU64gF5pRdSRKFVLcxqsZC637cLAGMbL40JavEfWnqgNg== - dependencies: - "@babel/runtime" "^7.14.8" - compute-scroll-into-view "^1.0.17" - prop-types "^15.7.2" - react-is "^17.0.2" - tslib "^2.3.0" - duplexify@^3.4.2, duplexify@^3.6.0: version "3.7.1" resolved "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309" @@ -3996,12 +4086,12 @@ duplexify@^3.4.2, duplexify@^3.6.0: ee-first@1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" - integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= + integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== -electron-to-chromium@^1.4.84: - version "1.4.113" - resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.113.tgz#b3425c086e2f4fc31e9e53a724c6f239e3adb8b9" - integrity sha512-s30WKxp27F3bBH6fA07FYL2Xm/FYnYrKpMjHr3XVCTUb9anAyZn/BeZfPWgTZGAbJeT4NxNwISSbLcYZvggPMA== +electron-to-chromium@^1.4.147: + version "1.4.156" + resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.156.tgz#fc398e1bfbe586135351ebfaf198473a82923af5" + integrity sha512-/Wj5NC7E0wHaMCdqxWz9B0lv7CcycDTiHyXCtbbu3pXM9TV2AOp8BtMqkVuqvJNdEvltBG6LxT2Q+BxY4LUCIA== element-resize-detector@^1.2.2: version "1.2.4" @@ -4033,19 +4123,10 @@ emojis-list@^3.0.0: resolved "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== -emotion-theming@^10.0.27: - version "10.3.0" - resolved "https://registry.npmjs.org/emotion-theming/-/emotion-theming-10.3.0.tgz#7f84d7099581d7ffe808aab5cd870e30843db72a" - integrity sha512-mXiD2Oj7N9b6+h/dC6oLf9hwxbtKHQjoIqtodEyL8CpkN4F3V4IK/BT4D0C7zSs4BBFOu4UlPJbvvBLa88SGEA== - dependencies: - "@babel/runtime" "^7.5.5" - "@emotion/weak-memoize" "0.2.5" - hoist-non-react-statics "^3.3.0" - encodeurl@~1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" - integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= + integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== end-of-stream@^1.0.0, end-of-stream@^1.1.0: version "1.4.4" @@ -4072,6 +4153,14 @@ enhanced-resolve@^4.5.0: memory-fs "^0.5.0" tapable "^1.0.0" +enhanced-resolve@^5.9.3: + version "5.9.3" + resolved "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.3.tgz#44a342c012cbc473254af5cc6ae20ebd0aae5d88" + integrity sha512-Bq9VSor+kjvW3f9/MiiR4eE3XYgOl7/rS8lnSxbRbF3kS0B2r+Y9w5krBWxZgDxASVZbdYrn5wT4j/Wb0J9qow== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.2.0" + entities@^2.0.0: version "2.2.0" resolved "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" @@ -4084,7 +4173,7 @@ errno@^0.1.3, errno@~0.1.7: dependencies: prr "~1.0.1" -error-ex@^1.3.1: +error-ex@^1.2.0, error-ex@^1.3.1: version "1.3.2" resolved "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== @@ -4092,23 +4181,25 @@ error-ex@^1.3.1: is-arrayish "^0.2.1" error-stack-parser@^2.0.6: - version "2.0.7" - resolved "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.0.7.tgz#b0c6e2ce27d0495cf78ad98715e0cad1219abb57" - integrity sha512-chLOW0ZGRf4s8raLrDxa5sdkvPec5YdvwbFnqJme4rk0rFajP8mPtrDL1+I+CwrQDCjswDA5sREX7jYQDQs9vA== + version "2.1.4" + resolved "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz#229cb01cdbfa84440bfa91876285b94680188286" + integrity sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ== dependencies: - stackframe "^1.1.1" + stackframe "^1.3.4" -es-abstract@^1.19.0, es-abstract@^1.19.1, es-abstract@^1.19.2: - version "1.19.5" - resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.5.tgz#a2cb01eb87f724e815b278b0dd0d00f36ca9a7f1" - integrity sha512-Aa2G2+Rd3b6kxEUKTF4TaW67czBLyAv3z7VOhYRU50YBx+bbsYZ9xQP4lMNazePuFlybXI0V4MruPos7qUo5fA== +es-abstract@^1.19.0, es-abstract@^1.19.1, es-abstract@^1.19.2, es-abstract@^1.19.5, es-abstract@^1.20.1: + version "1.20.1" + resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.1.tgz#027292cd6ef44bd12b1913b828116f54787d1814" + integrity sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA== dependencies: call-bind "^1.0.2" es-to-primitive "^1.2.1" function-bind "^1.1.1" + function.prototype.name "^1.1.5" get-intrinsic "^1.1.1" get-symbol-description "^1.0.0" has "^1.0.3" + has-property-descriptors "^1.0.0" has-symbols "^1.0.3" internal-slot "^1.0.3" is-callable "^1.2.4" @@ -4120,9 +4211,10 @@ es-abstract@^1.19.0, es-abstract@^1.19.1, es-abstract@^1.19.2: object-inspect "^1.12.0" object-keys "^1.1.1" object.assign "^4.1.2" - string.prototype.trimend "^1.0.4" - string.prototype.trimstart "^1.0.4" - unbox-primitive "^1.0.1" + regexp.prototype.flags "^1.4.3" + string.prototype.trimend "^1.0.5" + string.prototype.trimstart "^1.0.5" + unbox-primitive "^1.0.2" es-array-method-boxes-properly@^1.0.0: version "1.0.0" @@ -4143,6 +4235,11 @@ es-get-iterator@^1.0.2: is-string "^1.0.5" isarray "^2.0.5" +es-module-lexer@^0.9.0: + version "0.9.3" + resolved "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz#6f13db00cc38417137daf74366f535c8eb438f19" + integrity sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ== + es-shim-unscopables@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz#702e632193201e3edf8713635d083d378e510241" @@ -4160,9 +4257,9 @@ es-to-primitive@^1.2.1: is-symbol "^1.0.2" es5-shim@^4.5.13: - version "4.6.5" - resolved "https://registry.npmjs.org/es5-shim/-/es5-shim-4.6.5.tgz#2124bb073b7cede2ed23b122a1fd87bb7b0bb724" - integrity sha512-vfQ4UAai8szn0sAubCy97xnZ4sJVDD1gt/Grn736hg8D7540wemIb1YPrYZSTqlM2H69EQX1or4HU/tSwRTI3w== + version "4.6.7" + resolved "https://registry.npmjs.org/es5-shim/-/es5-shim-4.6.7.tgz#bc67ae0fc3dd520636e0a1601cc73b450ad3e955" + integrity sha512-jg21/dmlrNQI7JyyA2w7n+yifSxBng0ZralnSfVZjoCawgNTCnS+yBCyVM9DL5itm7SUnDGgv7hcq2XCZX4iRQ== es6-shim@^0.35.5: version "0.35.6" @@ -4177,12 +4274,32 @@ escalade@^3.1.1: escape-html@~1.0.3: version "1.0.3" resolved "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" - integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= + integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" - integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== + +escodegen@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz#5e32b12833e8aa8fa35e1bf0befa89380484c7dd" + integrity sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw== + dependencies: + esprima "^4.0.1" + estraverse "^5.2.0" + esutils "^2.0.2" + optionator "^0.8.1" + optionalDependencies: + source-map "~0.6.1" + +eslint-scope@5.1.1: + version "5.1.1" + resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" eslint-scope@^4.0.3: version "4.0.3" @@ -4192,7 +4309,12 @@ eslint-scope@^4.0.3: esrecurse "^4.1.0" estraverse "^4.1.1" -esrecurse@^4.1.0: +esprima@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +esrecurse@^4.1.0, esrecurse@^4.3.0: version "4.3.0" resolved "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== @@ -4226,9 +4348,9 @@ esutils@^2.0.2: etag@~1.8.1: version "1.8.1" resolved "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" - integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= + integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== -events@^3.0.0: +events@^3.0.0, events@^3.2.0: version "3.3.0" resolved "https://registry.npmjs.org/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== @@ -4241,10 +4363,25 @@ evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: md5.js "^1.3.4" safe-buffer "^5.1.1" +execa@^5.1.1: + version "5.1.1" + resolved "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" + integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" + strip-final-newline "^2.0.0" + expand-brackets@^2.1.4: version "2.1.4" resolved "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" - integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI= + integrity sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA== dependencies: debug "^2.3.3" define-property "^0.2.5" @@ -4255,37 +4392,38 @@ expand-brackets@^2.1.4: to-regex "^3.0.1" express@^4.17.1: - version "4.17.3" - resolved "https://registry.npmjs.org/express/-/express-4.17.3.tgz#f6c7302194a4fb54271b73a1fe7a06478c8f85a1" - integrity sha512-yuSQpz5I+Ch7gFrPCk4/c+dIBKlQUxtgwqzph132bsT6qhuzss6I8cLJQz7B3rFblzd6wtcI0ZbGltH/C4LjUg== + version "4.18.1" + resolved "https://registry.npmjs.org/express/-/express-4.18.1.tgz#7797de8b9c72c857b9cd0e14a5eea80666267caf" + integrity sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q== dependencies: accepts "~1.3.8" array-flatten "1.1.1" - body-parser "1.19.2" + body-parser "1.20.0" content-disposition "0.5.4" content-type "~1.0.4" - cookie "0.4.2" + cookie "0.5.0" cookie-signature "1.0.6" debug "2.6.9" - depd "~1.1.2" + depd "2.0.0" encodeurl "~1.0.2" escape-html "~1.0.3" etag "~1.8.1" - finalhandler "~1.1.2" + finalhandler "1.2.0" fresh "0.5.2" + http-errors "2.0.0" merge-descriptors "1.0.1" methods "~1.1.2" - on-finished "~2.3.0" + on-finished "2.4.1" parseurl "~1.3.3" path-to-regexp "0.1.7" proxy-addr "~2.0.7" - qs "6.9.7" + qs "6.10.3" range-parser "~1.2.1" safe-buffer "5.2.1" - send "0.17.2" - serve-static "1.14.2" + send "0.18.0" + serve-static "1.15.0" setprototypeof "1.2.0" - statuses "~1.5.0" + statuses "2.0.1" type-is "~1.6.18" utils-merge "1.0.1" vary "~1.1.2" @@ -4293,14 +4431,14 @@ express@^4.17.1: extend-shallow@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" - integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= + integrity sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug== dependencies: is-extendable "^0.1.0" extend-shallow@^3.0.0, extend-shallow@^3.0.2: version "3.0.2" resolved "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" - integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg= + integrity sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q== dependencies: assign-symbols "^1.0.0" is-extendable "^1.0.1" @@ -4362,7 +4500,7 @@ fast-json-stable-stringify@^2.0.0: resolved "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== -fast-levenshtein@^2.0.6: +fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: version "2.0.6" resolved "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= @@ -4381,6 +4519,11 @@ fault@^1.0.0: dependencies: format "^0.2.0" +fetch-retry@^5.0.2: + version "5.0.2" + resolved "https://registry.npmjs.org/fetch-retry/-/fetch-retry-5.0.2.tgz#4c55663a7c056cb45f182394e479464f0ff8f3e3" + integrity sha512-57Hmu+1kc6pKFUGVIobT7qw3NeAzY/uNN26bSevERLVvf6VGFR/ooDCOFBHMNDgAxBiU2YJq1D0vFzc6U1DcPw== + figgy-pudding@^3.5.1: version "3.5.2" resolved "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz#b4eee8148abb01dcf1d1ac34367d59e12fa61d6e" @@ -4395,13 +4538,12 @@ file-loader@^6.2.0: schema-utils "^3.0.0" file-system-cache@^1.0.5: - version "1.0.5" - resolved "https://registry.npmjs.org/file-system-cache/-/file-system-cache-1.0.5.tgz#84259b36a2bbb8d3d6eb1021d3132ffe64cfff4f" - integrity sha1-hCWbNqK7uNPW6xAh0xMv/mTP/08= + version "1.1.0" + resolved "https://registry.npmjs.org/file-system-cache/-/file-system-cache-1.1.0.tgz#984de17b976b75a77a27e08d6828137c1aa80fa1" + integrity sha512-IzF5MBq+5CR0jXx5RxPe4BICl/oEhBSXKaL9fLhAXrIfIUS77Hr4vzrYyqYMHN6uTt+BOqi3fDCTjjEBCjERKw== dependencies: - bluebird "^3.3.5" - fs-extra "^0.30.0" - ramda "^0.21.0" + fs-extra "^10.1.0" + ramda "^0.28.0" file-uri-to-path@1.0.0: version "1.0.0" @@ -4411,7 +4553,7 @@ file-uri-to-path@1.0.0: fill-range@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" - integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc= + integrity sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ== dependencies: extend-shallow "^2.0.1" is-number "^3.0.0" @@ -4425,17 +4567,17 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" -finalhandler@~1.1.2: - version "1.1.2" - resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" - integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== +finalhandler@1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32" + integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg== dependencies: debug "2.6.9" encodeurl "~1.0.2" escape-html "~1.0.3" - on-finished "~2.3.0" + on-finished "2.4.1" parseurl "~1.3.3" - statuses "~1.5.0" + statuses "2.0.1" unpipe "~1.0.0" find-cache-dir@^2.0.0, find-cache-dir@^2.1.0: @@ -4456,10 +4598,13 @@ find-cache-dir@^3.3.1: make-dir "^3.0.2" pkg-dir "^4.1.0" -find-root@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4" - integrity sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng== +find-up@^1.0.0: + version "1.1.2" + resolved "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" + integrity sha512-jvElSjyuo4EMQGoTwo1uJU5pQMwTW5lS1x05zzfJuTIyLR3zwO27LYrxNg+dlvKpGOuGy/MzBdXh80g0ve5+HA== + dependencies: + path-exists "^2.0.0" + pinkie-promise "^2.0.0" find-up@^3.0.0: version "3.0.0" @@ -4508,7 +4653,7 @@ flush-write-stream@^1.0.0: for-in@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" - integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= + integrity sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ== foreground-child@^2.0.0: version "2.0.0" @@ -4532,9 +4677,9 @@ fork-ts-checker-webpack-plugin@^4.1.6: worker-rpc "^0.1.0" fork-ts-checker-webpack-plugin@^6.0.4: - version "6.5.1" - resolved "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.1.tgz#fd689e2d9de6ac76abb620909eea56438cd0f232" - integrity sha512-x1wumpHOEf4gDROmKTaB6i4/Q6H3LwmjVO7fIX47vBwlZbtPjU33hgoMuD/Q/y6SU8bnuYSoN6ZQOLshGp0T/g== + version "6.5.2" + resolved "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.2.tgz#4f67183f2f9eb8ba7df7177ce3cf3e75cdafb340" + integrity sha512-m5cUmF30xkZ7h4tWUgTAcEaKmUW7tfyUyTqNNOz7OxWJ0v1VWKTcOvH8FWHUwSjlW/356Ijc9vi3XfcPstpQKA== dependencies: "@babel/code-frame" "^7.8.3" "@types/json-schema" "^7.0.5" @@ -4562,7 +4707,7 @@ form-data@^3.0.0: format@^0.2.0: version "0.2.2" resolved "https://registry.npmjs.org/format/-/format-0.2.2.tgz#d6170107e9efdc4ed30c9dc39016df942b5cb58b" - integrity sha1-1hcBB+nv3E7TDJ3DkBbflCtctYs= + integrity sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww== forwarded@0.2.0: version "0.2.0" @@ -4572,33 +4717,31 @@ forwarded@0.2.0: fragment-cache@^0.2.1: version "0.2.1" resolved "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" - integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk= + integrity sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA== dependencies: map-cache "^0.2.2" fresh@0.5.2: version "0.5.2" resolved "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" - integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= + integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== from2@^2.1.0: version "2.3.0" resolved "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af" - integrity sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8= + integrity sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g== dependencies: inherits "^2.0.1" readable-stream "^2.0.0" -fs-extra@^0.30.0: - version "0.30.0" - resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-0.30.0.tgz#f233ffcc08d4da7d432daa449776989db1df93f0" - integrity sha1-8jP/zAjU2n1DLapEl3aYnbHfk/A= +fs-extra@^10.1.0: + version "10.1.0" + resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf" + integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ== dependencies: - graceful-fs "^4.1.2" - jsonfile "^2.1.0" - klaw "^1.0.0" - path-is-absolute "^1.0.0" - rimraf "^2.2.8" + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" fs-extra@^9.0.0, fs-extra@^9.0.1: version "9.1.0" @@ -4625,7 +4768,7 @@ fs-monkey@1.0.3: fs-write-stream-atomic@^1.0.8: version "1.0.10" resolved "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz#b47df53493ef911df75731e70a9ded0189db40c9" - integrity sha1-tH31NJPvkR33VzHnCp3tAYnbQMk= + integrity sha512-gehEzmPn2nAwr39eay+x3X34Ra+M2QlVUTLhkXPjWdeO8RF9kszk116avgBJM3ZyNHgHXBNx+VmPaFC36k0PzA== dependencies: graceful-fs "^4.1.2" iferr "^0.1.5" @@ -4635,7 +4778,7 @@ fs-write-stream-atomic@^1.0.8: fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== fsevents@^1.2.7: version "1.2.13" @@ -4655,7 +4798,7 @@ function-bind@^1.1.1: resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== -function.prototype.name@^1.1.0: +function.prototype.name@^1.1.0, function.prototype.name@^1.1.5: version "1.1.5" resolved "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz#cce0505fe1ffb80503e6f9e46cc64e46a12a9621" integrity sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA== @@ -4666,14 +4809,9 @@ function.prototype.name@^1.1.0: functions-have-names "^1.2.2" functions-have-names@^1.2.2: - version "1.2.2" - resolved "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.2.tgz#98d93991c39da9361f8e50b337c4f6e41f120e21" - integrity sha512-bLgc3asbWdwPbx2mNk2S49kmJCuQeu0nfmaOgbs8WIyzzkw3r4htszdIi9Q9EMezDPTYuJx2wvjZ/EwgAthpnA== - -fuse.js@^3.6.1: - version "3.6.1" - resolved "https://registry.npmjs.org/fuse.js/-/fuse.js-3.6.1.tgz#7de85fdd6e1b3377c23ce010892656385fd9b10c" - integrity sha512-hT9yh/tiinkmirKrlv4KWOjztdoZo1mx9Qh4KvWqC7isoXwdUY3PNWUxceF4/qO9R6riA2C29jdTOeQOIROjgw== + version "1.2.3" + resolved "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" + integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== gauge@^3.0.0: version "3.0.2" @@ -4701,13 +4839,23 @@ get-caller-file@^2.0.5: integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" - integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q== + version "1.1.2" + resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.2.tgz#336975123e05ad0b7ba41f152ee4aadbea6cf598" + integrity sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA== dependencies: function-bind "^1.1.1" has "^1.0.3" - has-symbols "^1.0.1" + has-symbols "^1.0.3" + +get-stdin@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" + integrity sha512-F5aQMywwJ2n85s4hJPTT9RPxGmubonuB10MNYo17/xph174n2MIR33HRguhzVag10O/npM7SPk73LMZNP+FaWw== + +get-stream@^6.0.0: + version "6.0.1" + resolved "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" + integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== get-symbol-description@^1.0.0: version "1.0.0" @@ -4720,12 +4868,12 @@ get-symbol-description@^1.0.0: get-value@^2.0.3, get-value@^2.0.6: version "2.0.6" resolved "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" - integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= + integrity sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA== glob-parent@^3.1.0: version "3.1.0" resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" - integrity sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4= + integrity sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA== dependencies: is-glob "^3.1.0" path-dirname "^1.0.0" @@ -4747,7 +4895,7 @@ glob-promise@^3.4.0: glob-to-regexp@^0.3.0: version "0.3.0" resolved "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab" - integrity sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs= + integrity sha512-Iozmtbqv0noj0uDDqoL0zNq0VBEfK2YFoMAZoxJe4cwphvLR+JskfF30QhXHOR4m3KrE6NLRYw+U9MRXvifyig== glob-to-regexp@^0.4.1: version "0.4.1" @@ -4767,14 +4915,14 @@ glob@7.1.6: path-is-absolute "^1.0.0" glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: - version "7.2.0" - resolved "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" - integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== + version "7.2.3" + resolved "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" inherits "2" - minimatch "^3.0.4" + minimatch "^3.1.1" once "^1.3.0" path-is-absolute "^1.0.0" @@ -4792,9 +4940,9 @@ globals@^11.1.0: integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== globalthis@^1.0.0: - version "1.0.2" - resolved "https://registry.npmjs.org/globalthis/-/globalthis-1.0.2.tgz#2a235d34f4d8036219f7e34929b5de9e18166b8b" - integrity sha512-ZQnSFO1la8P7auIOQECnm0sSuoMeaSq0EEdXMBFF2QJO4uNcwbyhSgG3MruWNbFTqCLmxVwGOl7LZ9kASvHdeQ== + version "1.0.3" + resolved "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz#5852882a52b80dc301b0660273e1ed082f0b6ccf" + integrity sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA== dependencies: define-properties "^1.1.3" @@ -4824,7 +4972,7 @@ globby@^9.2.0: pify "^4.0.1" slash "^2.0.0" -graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0: +graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.9: version "4.2.10" resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== @@ -4841,15 +4989,15 @@ handlebars@^4.7.7: optionalDependencies: uglify-js "^3.1.4" -has-bigints@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113" - integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA== +has-bigints@^1.0.1, has-bigints@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" + integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== has-flag@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" - integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== has-flag@^4.0.0: version "4.0.0" @@ -4859,7 +5007,7 @@ has-flag@^4.0.0: has-glob@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/has-glob/-/has-glob-1.0.0.tgz#9aaa9eedbffb1ba3990a7b0010fb678ee0081207" - integrity sha1-mqqe7b/7G6OZCnsAEPtnjuAIEgc= + integrity sha512-D+8A457fBShSEI3tFCj65PAbT++5sKiFtdCdOam0gnfBgw9D277OERk+HM9qYJXmdVLZ/znez10SqHN0BBQ50g== dependencies: is-glob "^3.0.0" @@ -4885,12 +5033,12 @@ has-tostringtag@^1.0.0: has-unicode@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" - integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= + integrity sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ== has-value@^0.3.1: version "0.3.1" resolved "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" - integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8= + integrity sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q== dependencies: get-value "^2.0.3" has-values "^0.1.4" @@ -4899,7 +5047,7 @@ has-value@^0.3.1: has-value@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" - integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc= + integrity sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw== dependencies: get-value "^2.0.6" has-values "^1.0.0" @@ -4908,12 +5056,12 @@ has-value@^1.0.0: has-values@^0.1.4: version "0.1.4" resolved "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" - integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E= + integrity sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ== has-values@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" - integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8= + integrity sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ== dependencies: is-number "^3.0.0" kind-of "^4.0.0" @@ -5015,29 +5163,15 @@ he@^1.2.0: resolved "https://registry.npmjs.org/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== -highlight.js@^10.1.1, highlight.js@~10.7.0: +highlight.js@^10.4.1, highlight.js@~10.7.0: version "10.7.3" resolved "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz#697272e3991356e40c3cac566a74eef681756531" integrity sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A== -history@5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/history/-/history-5.0.0.tgz#0cabbb6c4bbf835addb874f8259f6d25101efd08" - integrity sha512-3NyRMKIiFSJmIPdq7FxkNMJkQ7ZEtVblOQ38VtKaA0zZMW1Eo6Q6W8oDKEflr1kNNTItSnk4JMCO1deeSgbLLg== - dependencies: - "@babel/runtime" "^7.7.6" - -history@^5.2.0: - version "5.3.0" - resolved "https://registry.npmjs.org/history/-/history-5.3.0.tgz#1548abaa245ba47992f063a0783db91ef201c73b" - integrity sha512-ZqaKwjjrAYUYfLG+htGaIIZ4nioX2L70ZUMIFysS3xvBsSG4x/n1V6TXV3N8ZYNuFGlDirFg32T7B6WOUPDYcQ== - dependencies: - "@babel/runtime" "^7.7.6" - hmac-drbg@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" - integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE= + integrity sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg== dependencies: hash.js "^1.0.3" minimalistic-assert "^1.0.0" @@ -5078,6 +5212,11 @@ html-minifier-terser@^5.0.1: relateurl "^0.2.7" terser "^4.6.3" +html-tags@^3.1.0: + version "3.2.0" + resolved "https://registry.npmjs.org/html-tags/-/html-tags-3.2.0.tgz#dbb3518d20b726524e4dd43de397eb0a95726961" + integrity sha512-vy7ClnArOZwCnqZgvv+ddgHgJiAFXe3Ge9ML5/mBctVJoUoYPCdxVucOywjDARn6CVoh3dRSFdPHy2sX80L0Wg== + html-void-elements@^1.0.0: version "1.0.5" resolved "https://registry.npmjs.org/html-void-elements/-/html-void-elements-1.0.5.tgz#ce9159494e86d95e45795b166c2021c2cfca4483" @@ -5108,21 +5247,26 @@ htmlparser2@^6.1.0: domutils "^2.5.2" entities "^2.0.0" -http-errors@1.8.1: - version "1.8.1" - resolved "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz#7c3f28577cbc8a207388455dbd62295ed07bd68c" - integrity sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g== +http-errors@2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" + integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== dependencies: - depd "~1.1.2" + depd "2.0.0" inherits "2.0.4" setprototypeof "1.2.0" - statuses ">= 1.5.0 < 2" + statuses "2.0.1" toidentifier "1.0.1" https-browserify@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" - integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= + integrity sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg== + +human-signals@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== iconv-lite@0.4.24: version "0.4.24" @@ -5146,7 +5290,7 @@ ieee754@^1.1.4: iferr@^0.1.5: version "0.1.5" resolved "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501" - integrity sha1-xg7taebY/bazEEofy8ocGS3FtQE= + integrity sha512-DUNFN5j7Tln0D+TxzloUjKB+CtVu6myn0JEFak6dG18mNt9YkQ6lzGCdafwofISZ1lLF3xRHJ98VKy9ynkcFaA== ignore@^4.0.3: version "4.0.6" @@ -5169,7 +5313,14 @@ import-fresh@^3.1.0, import-fresh@^3.2.1: imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" - integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== + +indent-string@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80" + integrity sha512-aqwDFWSgSgfRaEwao5lg5KEcVd/2a+D1rvoG7NdilmYz0NwRk6StWpWdz/Hpk34MKPpx7s8XxUqimfcQK6gGlg== + dependencies: + repeating "^2.0.0" indent-string@^4.0.0: version "4.0.0" @@ -5184,7 +5335,7 @@ infer-owner@^1.0.3, infer-owner@^1.0.4: inflight@^1.0.4: version "1.0.6" resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== dependencies: once "^1.3.0" wrappy "1" @@ -5197,12 +5348,12 @@ inherits@2, inherits@2.0.4, inherits@^2.0.0, inherits@^2.0.1, inherits@^2.0.3, i inherits@2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" - integrity sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE= + integrity sha512-8nWq2nLTAwd02jTqJExUYFSD/fKq6VH9Y/oG2accc/kdI0V98Bag8d5a4gi3XHz73rDWa2PvTtvcWYquKqSENA== inherits@2.0.3: version "2.0.3" resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= + integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw== inline-style-parser@0.1.1: version "0.1.1" @@ -5230,10 +5381,10 @@ invariant@^2.2.4: dependencies: loose-envify "^1.0.0" -ip@^1.1.5: - version "1.1.5" - resolved "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" - integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= +ip@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz#4cf4ab182fee2314c75ede1276f8c80b479936da" + integrity sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ== ipaddr.js@1.9.1: version "1.9.1" @@ -5243,7 +5394,7 @@ ipaddr.js@1.9.1: is-accessor-descriptor@^0.1.6: version "0.1.6" resolved "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" - integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY= + integrity sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A== dependencies: kind-of "^3.0.2" @@ -5278,7 +5429,7 @@ is-arguments@^1.1.0: is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" - integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= + integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== is-bigint@^1.0.1: version "1.0.4" @@ -5290,7 +5441,7 @@ is-bigint@^1.0.1: is-binary-path@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" - integrity sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg= + integrity sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q== dependencies: binary-extensions "^1.0.0" @@ -5325,16 +5476,16 @@ is-callable@^1.1.4, is-callable@^1.2.4: integrity sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w== is-core-module@^2.8.1: - version "2.8.1" - resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211" - integrity sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA== + version "2.9.0" + resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz#e1c34429cd51c6dd9e09e0799e396e27b19a9c69" + integrity sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A== dependencies: has "^1.0.3" is-data-descriptor@^0.1.4: version "0.1.4" resolved "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" - integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y= + integrity sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg== dependencies: kind-of "^3.0.2" @@ -5375,7 +5526,7 @@ is-descriptor@^1.0.0, is-descriptor@^1.0.2: is-data-descriptor "^1.0.0" kind-of "^6.0.2" -is-docker@^2.0.0: +is-docker@^2.0.0, is-docker@^2.1.1: version "2.2.1" resolved "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== @@ -5391,7 +5542,7 @@ is-dom@^1.0.0: is-extendable@^0.1.0, is-extendable@^0.1.1: version "0.1.1" resolved "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" - integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= + integrity sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw== is-extendable@^1.0.1: version "1.0.1" @@ -5403,7 +5554,12 @@ is-extendable@^1.0.1: is-extglob@^2.1.0, is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" - integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-finite@^1.0.0: + version "1.1.0" + resolved "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz#904135c77fb42c0641d6aa1bcdbc4daa8da082f3" + integrity sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w== is-fullwidth-code-point@^3.0.0: version "3.0.0" @@ -5418,7 +5574,7 @@ is-function@^1.0.2: is-glob@^3.0.0, is-glob@^3.1.0: version "3.1.0" resolved "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" - integrity sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo= + integrity sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw== dependencies: is-extglob "^2.1.0" @@ -5454,7 +5610,7 @@ is-number-object@^1.0.4: is-number@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" - integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU= + integrity sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg== dependencies: kind-of "^3.0.2" @@ -5473,6 +5629,11 @@ is-plain-obj@^2.0.0: resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== +is-plain-object@5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" + integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== + is-plain-object@^2.0.3, is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" @@ -5500,6 +5661,11 @@ is-shared-array-buffer@^1.0.2: dependencies: call-bind "^1.0.2" +is-stream@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== + is-string@^1.0.5, is-string@^1.0.7: version "1.0.7" resolved "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" @@ -5514,6 +5680,11 @@ is-symbol@^1.0.2, is-symbol@^1.0.3: dependencies: has-symbols "^1.0.2" +is-utf8@^0.2.0: + version "0.2.1" + resolved "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" + integrity sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q== + is-weakref@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" @@ -5529,7 +5700,7 @@ is-whitespace-character@^1.0.0: is-window@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/is-window/-/is-window-1.0.2.tgz#2c896ca53db97de45d3c33133a65d8c9f563480d" - integrity sha1-LIlspT25feRdPDMTOmXYyfVjSA0= + integrity sha512-uj00kdXyZb9t9RcAUAwMZAnkBUwdYGhYlt7djMXhfyhUCzwNba50tIiBKR7q0l7tdoBtFVw/3JmLY6fI3rmZmg== is-windows@^1.0.2: version "1.0.2" @@ -5544,9 +5715,9 @@ is-word-character@^1.0.0: is-wsl@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" - integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0= + integrity sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw== -is-wsl@^2.1.1: +is-wsl@^2.1.1, is-wsl@^2.2.0: version "2.2.0" resolved "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== @@ -5556,7 +5727,7 @@ is-wsl@^2.1.1: isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= + integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== isarray@^2.0.5: version "2.0.5" @@ -5566,26 +5737,34 @@ isarray@^2.0.5: isexe@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== isobject@^2.0.0: version "2.1.0" resolved "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" - integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk= + integrity sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA== dependencies: isarray "1.0.0" isobject@^3.0.0, isobject@^3.0.1: version "3.0.1" resolved "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" - integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= + integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== isobject@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/isobject/-/isobject-4.0.0.tgz#3f1c9155e73b192022a80819bacd0343711697b0" integrity sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA== -istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.0.1: +isomorphic-unfetch@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/isomorphic-unfetch/-/isomorphic-unfetch-3.1.0.tgz#87341d5f4f7b63843d468438128cb087b7c3e98f" + integrity sha512-geDJjpoZ8N0kWexiwkX8F9NkTsXhetLPVbZFQ+JTW239QNOwvB0gniuR1Wc6f0AMTn7/mFGyXvHTifrCp/GH8Q== + dependencies: + node-fetch "^2.6.1" + unfetch "^4.2.0" + +istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: version "3.2.0" resolved "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz#189e7909d0a39fa5a3dfad5b03f71947770191d3" integrity sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw== @@ -5599,7 +5778,7 @@ istanbul-lib-report@^3.0.0: make-dir "^3.0.0" supports-color "^7.1.0" -istanbul-reports@^3.0.2: +istanbul-reports@^3.1.4: version "3.1.4" resolved "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.4.tgz#1b6f068ecbc6c331040aab5741991273e609e40c" integrity sha512-r1/DshN4KSE7xWEknZLLLLDn5CJybV3nw01VTkp6D5jzLuELlcbudfj/eSQFvrKsJuTVCGnePO7ho82Nw9zzfw== @@ -5629,10 +5808,19 @@ jest-worker@^26.5.0: merge-stream "^2.0.0" supports-color "^7.0.0" +jest-worker@^27.4.5: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" + integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^8.0.0" + js-string-escape@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/js-string-escape/-/js-string-escape-1.0.1.tgz#e2625badbc0d67c7533e9edc1068c587ae4137ef" - integrity sha1-4mJbrbwNZ8dTPp7cEGjFh65BN+8= + integrity sha512-Smw4xcfIQ5LVjAOuJCvN/zIodzA/BBSsluuoSykP+lUvScIi4U6RJLfwHet5cxFnCswUjISV8oAXaqaJDY3chg== "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" @@ -5647,14 +5835,14 @@ jsesc@^2.5.1: jsesc@~0.5.0: version "0.5.0" resolved "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" - integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0= + integrity sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA== json-parse-better-errors@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== -json-parse-even-better-errors@^2.3.0: +json-parse-even-better-errors@^2.3.0, json-parse-even-better-errors@^2.3.1: version "2.3.1" resolved "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== @@ -5676,13 +5864,6 @@ json5@^2.1.2, json5@^2.1.3, json5@^2.2.1: resolved "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c" integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA== -jsonfile@^2.1.0: - version "2.4.0" - resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8" - integrity sha1-NzaitCi4e72gzIO1P6PWM6NcKug= - optionalDependencies: - graceful-fs "^4.1.6" - jsonfile@^6.0.1: version "6.1.0" resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" @@ -5700,14 +5881,14 @@ junk@^3.1.0: kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: version "3.2.2" resolved "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" - integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= + integrity sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ== dependencies: is-buffer "^1.1.5" kind-of@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" - integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc= + integrity sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw== dependencies: is-buffer "^1.1.5" @@ -5721,13 +5902,6 @@ kind-of@^6.0.0, kind-of@^6.0.2: resolved "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== -klaw@^1.0.0: - version "1.3.1" - resolved "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz#4088433b46b3b1ba259d78785d8e96f73ba02439" - integrity sha1-QIhDO0azsbolnXh4XY6W9zugJDk= - optionalDependencies: - graceful-fs "^4.1.9" - kleur@^3.0.3: version "3.0.3" resolved "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" @@ -5749,16 +5923,40 @@ lazy-universal-dotenv@^3.0.1: dotenv "^8.0.0" dotenv-expand "^5.1.0" +levn@~0.3.0: + version "0.3.0" + resolved "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + integrity sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA== + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + lines-and-columns@^1.1.6: version "1.2.4" resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== +load-json-file@^1.0.0: + version "1.1.0" + resolved "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" + integrity sha512-cy7ZdNRXdablkXYNI049pthVeXFurRyb9+hA/dZzerZ0pGTx42z+y+ssxBaVV2l70t1muq5IdKhn4UtcoGUY9A== + dependencies: + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + pinkie-promise "^2.0.0" + strip-bom "^2.0.0" + loader-runner@^2.4.0: version "2.4.0" resolved "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357" integrity sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw== +loader-runner@^4.2.0: + version "4.3.0" + resolved "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1" + integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg== + loader-utils@^1.1.0, loader-utils@^1.2.3: version "1.4.0" resolved "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613" @@ -5802,12 +6000,12 @@ locate-path@^6.0.0: lodash.debounce@^4.0.8: version "4.0.8" resolved "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" - integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168= + integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== lodash.uniq@4.5.0: version "4.5.0" resolved "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" - integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= + integrity sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ== lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21: version "4.17.21" @@ -5821,6 +6019,14 @@ loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: dependencies: js-tokens "^3.0.0 || ^4.0.0" +loud-rejection@^1.0.0: + version "1.6.0" + resolved "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" + integrity sha512-RPNliZOFkqFumDhvYqOaNY4Uz9oJM2K9tC6JWsJJsNdhuONW4LQHRBpb0qf4pJApVffI5N39SwzWZJuEhfd7eQ== + dependencies: + currently-unhandled "^0.4.1" + signal-exit "^3.0.0" + lower-case@^2.0.2: version "2.0.2" resolved "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" @@ -5828,7 +6034,7 @@ lower-case@^2.0.2: dependencies: tslib "^2.0.3" -lowlight@^1.14.0: +lowlight@^1.17.0: version "1.20.0" resolved "https://registry.npmjs.org/lowlight/-/lowlight-1.20.0.tgz#ddb197d33462ad0d93bf19d17b6c301aa3941888" integrity sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw== @@ -5868,17 +6074,22 @@ make-dir@^3.0.0, make-dir@^3.0.2, make-dir@^3.1.0: map-cache@^0.2.2: version "0.2.2" resolved "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" - integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= + integrity sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg== + +map-obj@^1.0.0, map-obj@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" + integrity sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg== map-or-similar@^1.5.0: version "1.5.0" resolved "https://registry.npmjs.org/map-or-similar/-/map-or-similar-1.5.0.tgz#6de2653174adfb5d9edc33c69d3e92a1b76faf08" - integrity sha1-beJlMXSt+12e3DPGnT6Sobdvrwg= + integrity sha512-0aF7ZmVon1igznGI4VS30yugpduQW3y3GkcgGJOp7d8x8QrizhigUxjI/m2UojsXXto+jLAH3KSz+xOJTiORjg== map-visit@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" - integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48= + integrity sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w== dependencies: object-visit "^1.0.0" @@ -5887,11 +6098,6 @@ markdown-escapes@^1.0.0: resolved "https://registry.npmjs.org/markdown-escapes/-/markdown-escapes-1.0.4.tgz#c95415ef451499d7602b91095f3c8e8975f78535" integrity sha512-8z4efJYk43E0upd0NbVXwgSTQs6cT3T06etieCMEg7dRbzCbxUCK/GHlX8mhHRDcp+OLlHkPKsvqQTCvsRl2cg== -markdown-to-jsx@^7.1.3: - version "7.1.7" - resolved "https://registry.npmjs.org/markdown-to-jsx/-/markdown-to-jsx-7.1.7.tgz#a5f22102fb12241c8cea1ca6a4050bb76b23a25d" - integrity sha512-VI3TyyHlGkO8uFle0IOibzpO1c1iJDcXcS/zBrQrXQQvJ2tpdwVzVZ7XdKsyRz1NdRmre4dqQkMZzUHaKIG/1w== - md5.js@^1.3.4: version "1.3.5" resolved "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" @@ -5932,31 +6138,31 @@ mdast-util-to-hast@10.0.1: mdurl@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" - integrity sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4= + integrity sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g== media-typer@0.3.0: version "0.3.0" resolved "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" - integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= + integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== memfs@^3.1.2: - version "3.4.1" - resolved "https://registry.npmjs.org/memfs/-/memfs-3.4.1.tgz#b78092f466a0dce054d63d39275b24c71d3f1305" - integrity sha512-1c9VPVvW5P7I85c35zAdEr1TD5+F11IToIHIlrVIcflfnzPkJa0ZoYEoEdYDP8KgPFoSZ/opDrUsAoZWym3mtw== + version "3.4.4" + resolved "https://registry.npmjs.org/memfs/-/memfs-3.4.4.tgz#e8973cd8060548916adcca58a248e7805c715e89" + integrity sha512-W4gHNUE++1oSJVn8Y68jPXi+mkx3fXR5ITE/Ubz6EQ3xRpCN5k2CQ4AUR8094Z7211F876TyoBACGsIveqgiGA== dependencies: fs-monkey "1.0.3" memoizerific@^1.11.3: version "1.11.3" resolved "https://registry.npmjs.org/memoizerific/-/memoizerific-1.11.3.tgz#7c87a4646444c32d75438570905f2dbd1b1a805a" - integrity sha1-fIekZGREwy11Q4VwkF8tvRsagFo= + integrity sha512-/EuHYwAPdLtXwAwSZkh/Gutery6pD2KYd44oQLhAvQp/50mpyduZh8Q7PYHXTCJ+wuXxt7oij2LXyIJOOYFPog== dependencies: map-or-similar "^1.5.0" memory-fs@^0.4.1: version "0.4.1" resolved "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" - integrity sha1-OpoguEYlI+RHz7x+i7gO1me/xVI= + integrity sha512-cda4JKCxReDXFXRqOHPQscuIYg1PvxbE2S2GP45rnwfEK+vZaXC8C1OFvdHIbgw0DLzowXGVoxLaAmlgRy14GQ== dependencies: errno "^0.1.3" readable-stream "^2.0.1" @@ -5969,10 +6175,26 @@ memory-fs@^0.5.0: errno "^0.1.3" readable-stream "^2.0.1" +meow@^3.1.0: + version "3.7.0" + resolved "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" + integrity sha512-TNdwZs0skRlpPpCUK25StC4VH+tP5GgeY1HQOOGP+lQ2xtdkN2VtT/5tiX9k3IWpkBPV9b3LsAWXn4GGi/PrSA== + dependencies: + camelcase-keys "^2.0.0" + decamelize "^1.1.2" + loud-rejection "^1.0.0" + map-obj "^1.0.1" + minimist "^1.1.3" + normalize-package-data "^2.3.4" + object-assign "^4.0.1" + read-pkg-up "^1.0.1" + redent "^1.0.0" + trim-newlines "^1.0.0" + merge-descriptors@1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" - integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= + integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w== merge-stream@^2.0.0: version "2.0.0" @@ -5987,7 +6209,7 @@ merge2@^1.2.3, merge2@^1.3.0, merge2@^1.4.1: methods@~1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" - integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= + integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== microevent.ts@~0.1.1: version "0.1.1" @@ -6051,10 +6273,15 @@ mime@^2.4.4: resolved "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + min-document@^2.19.0: version "2.19.0" resolved "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685" - integrity sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU= + integrity sha512-9Wy1B3m3f66bPPmU5hdA4DR4PB2OfDU/+GS3yAB7IQozE3tqXaVv2zOjgla7MEGSRv95+ILmOuvhLkOK6wJtCQ== dependencies: dom-walk "^0.1.0" @@ -6071,16 +6298,16 @@ minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: minimalistic-crypto-utils@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" - integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= + integrity sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg== -minimatch@^3.0.2, minimatch@^3.0.4: +minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.1.1: version "3.1.2" resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== dependencies: brace-expansion "^1.1.7" -minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6: +minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6: version "1.2.6" resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== @@ -6160,7 +6387,7 @@ mkdirp@^1.0.3, mkdirp@^1.0.4: move-concurrently@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92" - integrity sha1-viwAX9oy4LKa8fBdfEszIUxwH5I= + integrity sha512-hdrFxZOycD/g6A6SoI2bB5NA/5NEqD0569+S47WZhPvm46sD50ZHdYaFmnua5lndde9rCHGjmfK7Z8BuCt/PcQ== dependencies: aproba "^1.1.1" copy-concurrently "^1.0.0" @@ -6172,7 +6399,7 @@ move-concurrently@^1.0.1: ms@2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== ms@2.1.1: version "2.1.1" @@ -6199,9 +6426,14 @@ mz@^2.7.0: thenify-all "^1.0.0" nan@^2.12.1: - version "2.15.0" - resolved "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz#3f34a473ff18e15c1b5626b62903b5ad6e665fee" - integrity sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ== + version "2.16.0" + resolved "https://registry.npmjs.org/nan/-/nan-2.16.0.tgz#664f43e45460fb98faf00edca0bb0d7b8dce7916" + integrity sha512-UdAqHyFngu7TfQKsCBgAA6pWDkT8MAO7d0jyOecVhN5354xbLqdn8mV9Tat9gepAupm0bt2DbeaSC8vS52MuFA== + +nanoid@^3.3.1: + version "3.3.4" + resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab" + integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw== nanomatch@^1.2.9: version "1.2.13" @@ -6225,7 +6457,7 @@ negotiator@0.6.3: resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== -neo-async@^2.5.0, neo-async@^2.6.0, neo-async@^2.6.1: +neo-async@^2.5.0, neo-async@^2.6.0, neo-async@^2.6.1, neo-async@^2.6.2: version "2.6.2" resolved "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== @@ -6246,11 +6478,11 @@ no-case@^3.0.4: node-dir@^0.1.10: version "0.1.17" resolved "https://registry.npmjs.org/node-dir/-/node-dir-0.1.17.tgz#5f5665d93351335caabef8f1c554516cf5f1e4e5" - integrity sha1-X1Zl2TNRM1yqvvjxxVRRbPXx5OU= + integrity sha512-tmPX422rYgofd4epzrNoOXiE8XFZYOcCq1vD7MAXCDO+O+zndlA2ztdKKMa+EeuBG5tHETpr4ml4RGgpqDCCAg== dependencies: minimatch "^3.0.2" -node-fetch@^2.6.1: +node-fetch@^2.6.1, node-fetch@^2.6.7: version "2.6.7" resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== @@ -6286,12 +6518,12 @@ node-libs-browser@^2.2.1: util "^0.11.0" vm-browserify "^1.0.1" -node-releases@^2.0.2: - version "2.0.3" - resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.3.tgz#225ee7488e4a5e636da8da52854844f9d716ca96" - integrity sha512-maHFz6OLqYxz+VQyCAtA3PTX4UP/53pa05fyDNc9CwjvJ0yEh6+xBwKsgCxMNhS8taUKBFYxfuiaD9U/55iFaw== +node-releases@^2.0.5: + version "2.0.5" + resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.5.tgz#280ed5bc3eba0d96ce44897d8aee478bfb3d9666" + integrity sha512-U9h1NLROZTq9uE1SNffn6WuPDg8icmi3ns4rEl/oTfIle4iLjTliCzgTsbaIFMq/Xn078/lfY/BL0GWZ+psK4Q== -normalize-package-data@^2.5.0: +normalize-package-data@^2.3.2, normalize-package-data@^2.3.4, normalize-package-data@^2.5.0: version "2.5.0" resolved "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== @@ -6304,7 +6536,7 @@ normalize-package-data@^2.5.0: normalize-path@^2.1.1: version "2.1.1" resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" - integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= + integrity sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w== dependencies: remove-trailing-separator "^1.0.1" @@ -6316,7 +6548,14 @@ normalize-path@^3.0.0, normalize-path@~3.0.0: normalize-range@^0.1.2: version "0.1.2" resolved "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" - integrity sha1-LRDAa9/TEuqXd2laTShDlFa3WUI= + integrity sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA== + +npm-run-path@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + dependencies: + path-key "^3.0.0" npmlog@^5.0.1: version "5.0.1" @@ -6329,35 +6568,35 @@ npmlog@^5.0.1: set-blocking "^2.0.0" nth-check@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz#2efe162f5c3da06a28959fbd3db75dbeea9f0fc2" - integrity sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w== + version "2.1.1" + resolved "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d" + integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w== dependencies: boolbase "^1.0.0" num2fraction@^1.2.2: version "1.2.2" resolved "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede" - integrity sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4= + integrity sha512-Y1wZESM7VUThYY+4W+X4ySH2maqcA+p7UR+w8VWNWVAd6lwuXXWz/w/Cz43J/dI2I+PS6wD5N+bJUF+gjWvIqg== object-assign@^4.0.1, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== object-copy@^0.1.0: version "0.1.0" resolved "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" - integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw= + integrity sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ== dependencies: copy-descriptor "^0.1.0" define-property "^0.2.5" kind-of "^3.0.3" object-inspect@^1.12.0, object-inspect@^1.9.0: - version "1.12.0" - resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz#6e2c120e868fd1fd18cb4f18c31741d0d6e776f0" - integrity sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g== + version "1.12.2" + resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea" + integrity sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ== object-keys@^1.1.1: version "1.1.1" @@ -6367,7 +6606,7 @@ object-keys@^1.1.1: object-visit@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" - integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs= + integrity sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA== dependencies: isobject "^3.0.0" @@ -6400,18 +6639,19 @@ object.entries@^1.1.0: es-abstract "^1.19.1" object.getownpropertydescriptors@^2.0.3, object.getownpropertydescriptors@^2.1.2: - version "2.1.3" - resolved "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.3.tgz#b223cf38e17fefb97a63c10c91df72ccb386df9e" - integrity sha512-VdDoCwvJI4QdC6ndjpqFmoL3/+HxffFBbcJzKi5hwLLqqx3mdbedRpfZDdK0SrOSauj8X4GzBvnDZl4vTN7dOw== + version "2.1.4" + resolved "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.4.tgz#7965e6437a57278b587383831a9b829455a4bc37" + integrity sha512-sccv3L/pMModT6dJAYF3fzGMVcb38ysQ0tEE6ixv2yXJDtEIPph268OlAdJj5/qZMZDq2g/jqvwppt36uS/uQQ== dependencies: + array.prototype.reduce "^1.0.4" call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.1" + define-properties "^1.1.4" + es-abstract "^1.20.1" object.pick@^1.3.0: version "1.3.0" resolved "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" - integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c= + integrity sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ== dependencies: isobject "^3.0.1" @@ -6429,10 +6669,10 @@ objectorarray@^1.0.5: resolved "https://registry.npmjs.org/objectorarray/-/objectorarray-1.0.5.tgz#2c05248bbefabd8f43ad13b41085951aac5e68a5" integrity sha512-eJJDYkhJFFbBBAxeh8xW+weHlkI28n2ZdQV/J/DNfWfSKlGEf2xcfAbZTv3riEXHAhL9SVOTs2pRmXiSTf78xg== -on-finished@~2.3.0: - version "2.3.0" - resolved "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" - integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= +on-finished@2.4.1: + version "2.4.1" + resolved "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" + integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== dependencies: ee-first "1.1.1" @@ -6444,10 +6684,17 @@ on-headers@~1.0.2: once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== dependencies: wrappy "1" +onetime@^5.1.2: + version "5.1.2" + resolved "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + open@^7.0.3: version "7.4.2" resolved "https://registry.npmjs.org/open/-/open-7.4.2.tgz#b8147e26dcf3e426316c730089fd71edd29c2321" @@ -6456,15 +6703,36 @@ open@^7.0.3: is-docker "^2.0.0" is-wsl "^2.1.1" +open@^8.4.0: + version "8.4.0" + resolved "https://registry.npmjs.org/open/-/open-8.4.0.tgz#345321ae18f8138f82565a910fdc6b39e8c244f8" + integrity sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q== + dependencies: + define-lazy-prop "^2.0.0" + is-docker "^2.1.1" + is-wsl "^2.2.0" + +optionator@^0.8.1: + version "0.8.3" + resolved "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" + integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.6" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + word-wrap "~1.2.3" + os-browserify@^0.3.0: version "0.3.0" resolved "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" - integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc= + integrity sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A== -overlayscrollbars@^1.13.1: - version "1.13.1" - resolved "https://registry.npmjs.org/overlayscrollbars/-/overlayscrollbars-1.13.1.tgz#0b840a88737f43a946b9d87875a2f9e421d0338a" - integrity sha512-gIQfzgGgu1wy80EB4/6DaJGHMEGmizq27xHIESrzXq0Y/J0Ay1P3DWk6tuVmEPIZH15zaBlxeEJOqdJKmowHCQ== +os-homedir@^1.0.0: + version "1.0.2" + resolved "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + integrity sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ== p-all@^2.1.0: version "2.1.0" @@ -6490,7 +6758,7 @@ p-filter@^2.1.0: p-finally@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" - integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= + integrity sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow== p-limit@^2.0.0, p-limit@^2.2.0: version "2.3.0" @@ -6610,6 +6878,13 @@ parse-entities@^2.0.0: is-decimal "^1.0.0" is-hexadecimal "^1.0.0" +parse-json@^2.2.0: + version "2.2.0" + resolved "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" + integrity sha512-QR/GGaKCkhwk1ePQNYDRKYZ3mwU9ypsKhB0XyFnLQdomyEqk3e8wpW3V5Jp88zbxK4n5ST1nqo+g9juTpownhQ== + dependencies: + error-ex "^1.2.0" + parse-json@^5.0.0: version "5.2.0" resolved "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" @@ -6641,7 +6916,7 @@ pascal-case@^3.1.2: pascalcase@^0.1.1: version "0.1.1" resolved "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" - integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= + integrity sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw== path-browserify@0.0.1: version "0.0.1" @@ -6651,12 +6926,19 @@ path-browserify@0.0.1: path-dirname@^1.0.0: version "1.0.2" resolved "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" - integrity sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA= + integrity sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q== + +path-exists@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" + integrity sha512-yTltuKuhtNeFJKa1PiRzfLAU5182q1y4Eb4XCJ3PBqyzEDkAZRzBrKKBct682ls9reBVHf9udYLN5Nd+K1B9BQ== + dependencies: + pinkie-promise "^2.0.0" path-exists@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" - integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= + integrity sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ== path-exists@^4.0.0: version "4.0.0" @@ -6666,9 +6948,9 @@ path-exists@^4.0.0: path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== -path-key@^3.1.0: +path-key@^3.0.0, path-key@^3.1.0: version "3.1.1" resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== @@ -6681,7 +6963,16 @@ path-parse@^1.0.7: path-to-regexp@0.1.7: version "0.1.7" resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" - integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= + integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ== + +path-type@^1.0.0: + version "1.1.0" + resolved "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" + integrity sha512-S4eENJz1pkiQn9Znv33Q+deTOKmbl+jj1Fl+qiP/vYezj+S8x+J3Uo0ISrx/QoEvIlOaDWJhPaRd1flJ9HXZqg== + dependencies: + graceful-fs "^4.1.2" + pify "^2.0.0" + pinkie-promise "^2.0.0" path-type@^3.0.0: version "3.0.0" @@ -6721,16 +7012,33 @@ picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.0, picomatch@^2.3.1: resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== +pify@^2.0.0: + version "2.3.0" + resolved "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== + pify@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" - integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= + integrity sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg== pify@^4.0.1: version "4.0.1" resolved "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== +pinkie-promise@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" + integrity sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw== + dependencies: + pinkie "^2.0.0" + +pinkie@^2.0.0: + version "2.0.4" + resolved "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + integrity sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg== + pirates@^4.0.1: version "4.0.4" resolved "https://registry.npmjs.org/pirates/-/pirates-4.0.4.tgz#07df81e61028e402735cdd49db701e4885b4e6e6" @@ -6769,7 +7077,7 @@ pnp-webpack-plugin@1.6.4: dependencies: ts-pnp "^1.1.6" -polished@^4.0.5: +polished@^4.2.2: version "4.2.2" resolved "https://registry.npmjs.org/polished/-/polished-4.2.2.tgz#2529bb7c3198945373c52e34618c8fe7b1aa84d1" integrity sha512-Sz2Lkdxz6F2Pgnpi9U5Ng/WdWAUZxmHrNPoVlm3aAemxoy2Qy7LGjQg4uf8qKelDAUW94F4np3iH2YPf2qefcQ== @@ -6779,7 +7087,7 @@ polished@^4.0.5: posix-character-classes@^0.1.0: version "0.1.1" resolved "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" - integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= + integrity sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg== postcss-flexbugs-fixes@^4.2.1: version "4.2.1" @@ -6853,6 +7161,11 @@ postcss@^7.0.14, postcss@^7.0.26, postcss@^7.0.32, postcss@^7.0.36, postcss@^7.0 picocolors "^0.2.1" source-map "^0.6.1" +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + integrity sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w== + "prettier@>=2.2.1 <=2.3.0": version "2.3.0" resolved "https://registry.npmjs.org/prettier/-/prettier-2.3.0.tgz#b6a5bf1284026ae640f17f7ff5658a7567fc0d18" @@ -6869,9 +7182,9 @@ pretty-error@^2.1.1: pretty-hrtime@^1.0.3: version "1.0.3" resolved "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1" - integrity sha1-t+PqQkNaTJsnWdmeDyAesZWALuE= + integrity sha512-66hKPCr+72mlfiSjlEB1+45IjXSqvVAIy6mocupoww4tBFE9R9IhwwUGoI4G++Tc9Aq+2rxOt0RFU6gPcrte0A== -prismjs@^1.21.0: +prismjs@^1.27.0: version "1.28.0" resolved "https://registry.npmjs.org/prismjs/-/prismjs-1.28.0.tgz#0d8f561fa0f7cf6ebca901747828b149147044b6" integrity sha512-8aaXdYvl1F7iC7Xm1spqSaY/OJBpYW3v+KJ+F17iYxvdc8sfjW194COK5wVhMZX45tGteiBQgdvD/nhxcRwylw== @@ -6889,12 +7202,12 @@ process-nextick-args@~2.0.0: process@^0.11.10: version "0.11.10" resolved "https://registry.npmjs.org/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" - integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= + integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== promise-inflight@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" - integrity sha1-mEcocL8igTL8vdhoEputEsPAKeM= + integrity sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g== promise.allsettled@^1.0.0: version "1.0.5" @@ -6925,7 +7238,7 @@ prompts@^2.4.0: kleur "^3.0.3" sisteransi "^1.0.5" -prop-types@^15.0.0, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.7.2: +prop-types@^15.0.0, prop-types@^15.6.1, prop-types@^15.7.2: version "15.8.1" resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -6952,7 +7265,7 @@ proxy-addr@~2.0.7: prr@~1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" - integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY= + integrity sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw== public-encrypt@^4.0.0: version "4.0.3" @@ -6994,39 +7307,41 @@ pumpify@^1.3.3: punycode@1.3.2: version "1.3.2" resolved "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" - integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0= + integrity sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw== punycode@^1.2.4: version "1.4.1" resolved "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" - integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= + integrity sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ== punycode@^2.1.0: version "2.1.1" resolved "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== -qs@6.9.7: - version "6.9.7" - resolved "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz#4610846871485e1e048f44ae3b94033f0e675afe" - integrity sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw== - -qs@^6.10.0: +qs@6.10.3: version "6.10.3" resolved "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz#d6cde1b2ffca87b5aa57889816c5f81535e22e8e" integrity sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ== dependencies: side-channel "^1.0.4" +qs@^6.10.0: + version "6.10.5" + resolved "https://registry.npmjs.org/qs/-/qs-6.10.5.tgz#974715920a80ff6a262264acd2c7e6c2a53282b4" + integrity sha512-O5RlPh0VFtR78y79rgcgKK4wbAI0C5zGVLztOIdpWX6ep368q5Hv6XRxDvXuZ9q3C6v+e3n8UfZZJw7IIG27eQ== + dependencies: + side-channel "^1.0.4" + querystring-es3@^0.2.0: version "0.2.1" resolved "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" - integrity sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM= + integrity sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA== querystring@0.2.0: version "0.2.0" resolved "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" - integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= + integrity sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g== querystring@^0.2.0: version "0.2.1" @@ -7038,10 +7353,10 @@ queue-microtask@^1.2.2: resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== -ramda@^0.21.0: - version "0.21.0" - resolved "https://registry.npmjs.org/ramda/-/ramda-0.21.0.tgz#a001abedb3ff61077d4ff1d577d44de77e8d0a35" - integrity sha1-oAGr7bP/YQd9T/HVd9RN536NCjU= +ramda@^0.28.0: + version "0.28.0" + resolved "https://registry.npmjs.org/ramda/-/ramda-0.28.0.tgz#acd785690100337e8b063cab3470019be427cc97" + integrity sha512-9QnLuG/kPVgWvMQ4aODhsBUFKOUmnbUnsSXACv+NCQZcHbeb+v8Lodp8OVxtRULN1/xOyYLLaL6npE6dMq5QTA== randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0: version "2.1.0" @@ -7063,13 +7378,13 @@ range-parser@^1.2.1, range-parser@~1.2.1: resolved "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== -raw-body@2.4.3: - version "2.4.3" - resolved "https://registry.npmjs.org/raw-body/-/raw-body-2.4.3.tgz#8f80305d11c2a0a545c2d9d89d7a0286fcead43c" - integrity sha512-UlTNLIcu0uzb4D2f4WltY6cVjLi+/jEN4lgEUj3E04tpMDpUlkBo/eSn6zou9hum2VMNpCCUone0O0WeJim07g== +raw-body@2.5.1: + version "2.5.1" + resolved "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857" + integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig== dependencies: bytes "3.1.2" - http-errors "1.8.1" + http-errors "2.0.0" iconv-lite "0.4.24" unpipe "1.0.0" @@ -7081,20 +7396,15 @@ raw-loader@^4.0.2: loader-utils "^2.0.0" schema-utils "^3.0.0" -react-colorful@^5.1.2: - version "5.5.1" - resolved "https://registry.npmjs.org/react-colorful/-/react-colorful-5.5.1.tgz#29d9c4e496f2ca784dd2bb5053a3a4340cfaf784" - integrity sha512-M1TJH2X3RXEt12sWkpa6hLc/bbYS0H6F4rIqjQZ+RxNBstpY67d9TrFXtqdZwhpmBXcCwEi7stKqFue3ZRkiOg== - -react-docgen-typescript@^2.0.0: +react-docgen-typescript@^2.1.1: version "2.2.2" resolved "https://registry.npmjs.org/react-docgen-typescript/-/react-docgen-typescript-2.2.2.tgz#4611055e569edc071204aadb20e1c93e1ab1659c" integrity sha512-tvg2ZtOpOi6QDwsb3GZhOjDkkX0h8Z2gipvTg6OVMUyoYoURhEiRNePT8NZItTVCDh39JJHnLdfCOkzoLbFnTg== react-docgen@^5.0.0: - version "5.4.0" - resolved "https://registry.npmjs.org/react-docgen/-/react-docgen-5.4.0.tgz#2cd7236720ec2769252ef0421f23250b39a153a1" - integrity sha512-JBjVQ9cahmNlfjMGxWUxJg919xBBKAoy3hgDgKERbR+BcF4ANpDuzWAScC7j27hZfd8sJNmMPOLWo9+vB/XJEQ== + version "5.4.2" + resolved "https://registry.npmjs.org/react-docgen/-/react-docgen-5.4.2.tgz#697ec899e8dd493bb7ba0d50ec0aa5e49f41d3fa" + integrity sha512-4Z5XYpHsn2bbUfaflxoS30VhUvQLBe4GCwwM5v1e1FUOeDdaoJi6wUGSmYp6OdXYEISEAOEIaSPBk4iezNCKBw== dependencies: "@babel/core" "^7.7.5" "@babel/generator" "^7.12.11" @@ -7116,29 +7426,14 @@ react-dom@^17.0.2: object-assign "^4.1.1" scheduler "^0.20.2" -react-draggable@^4.4.3: - version "4.4.4" - resolved "https://registry.npmjs.org/react-draggable/-/react-draggable-4.4.4.tgz#5b26d9996be63d32d285a426f41055de87e59b2f" - integrity sha512-6e0WdcNLwpBx/YIDpoyd2Xb04PB0elrDrulKUgdrIlwuYvxh5Ok9M+F8cljm8kPXXs43PmMzek9RrB1b7mLMqA== +react-element-to-jsx-string@^14.3.4: + version "14.3.4" + resolved "https://registry.npmjs.org/react-element-to-jsx-string/-/react-element-to-jsx-string-14.3.4.tgz#709125bc72f06800b68f9f4db485f2c7d31218a8" + integrity sha512-t4ZwvV6vwNxzujDQ+37bspnLwA4JlgUPWhLjBJWsNIDceAf6ZKUTCjdm08cN6WeZ5pTMKiCJkmAYnpmR4Bm+dg== dependencies: - clsx "^1.1.1" - prop-types "^15.6.0" - -react-fast-compare@^3.0.1, react-fast-compare@^3.2.0: - version "3.2.0" - resolved "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz#641a9da81b6a6320f270e89724fb45a0b39e43bb" - integrity sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA== - -react-helmet-async@^1.0.7: - version "1.3.0" - resolved "https://registry.npmjs.org/react-helmet-async/-/react-helmet-async-1.3.0.tgz#7bd5bf8c5c69ea9f02f6083f14ce33ef545c222e" - integrity sha512-9jZ57/dAn9t3q6hneQS0wukqC2ENOBgMNVEhb/ZG9ZSxUetzVIw4iAmEU38IaVg3QGYauQPhSeUTuIUtFglWpg== - dependencies: - "@babel/runtime" "^7.12.5" - invariant "^2.2.4" - prop-types "^15.7.2" - react-fast-compare "^3.2.0" - shallowequal "^1.1.0" + "@base2/pretty-print-object" "1.0.1" + is-plain-object "5.0.0" + react-is "17.0.2" react-hot-loader@^4.13.0: version "4.13.0" @@ -7163,58 +7458,26 @@ react-inspector@^5.1.0: is-dom "^1.0.0" prop-types "^15.0.0" +react-is@17.0.2: + version "17.0.2" + resolved "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" + integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== + react-is@^16.13.1, react-is@^16.7.0: version "16.13.1" resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== -react-is@^17.0.2: - version "17.0.2" - resolved "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" - integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== - react-lifecycles-compat@^3.0.4: version "3.0.4" resolved "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== -react-popper-tooltip@^3.1.1: - version "3.1.1" - resolved "https://registry.npmjs.org/react-popper-tooltip/-/react-popper-tooltip-3.1.1.tgz#329569eb7b287008f04fcbddb6370452ad3f9eac" - integrity sha512-EnERAnnKRptQBJyaee5GJScWNUKQPDD2ywvzZyUjst/wj5U64C8/CnSYLNEmP2hG0IJ3ZhtDxE8oDN+KOyavXQ== - dependencies: - "@babel/runtime" "^7.12.5" - "@popperjs/core" "^2.5.4" - react-popper "^2.2.4" - -react-popper@^2.2.4: - version "2.2.5" - resolved "https://registry.npmjs.org/react-popper/-/react-popper-2.2.5.tgz#1214ef3cec86330a171671a4fbcbeeb65ee58e96" - integrity sha512-kxGkS80eQGtLl18+uig1UIf9MKixFSyPxglsgLBxlYnyDf65BiY9B3nZSc6C9XUNDgStROB0fMQlTEz1KxGddw== - dependencies: - react-fast-compare "^3.0.1" - warning "^4.0.2" - react-refresh@^0.11.0: version "0.11.0" resolved "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz#77198b944733f0f1f1a90e791de4541f9f074046" integrity sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A== -react-router-dom@^6.0.0: - version "6.3.0" - resolved "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.3.0.tgz#a0216da813454e521905b5fa55e0e5176123f43d" - integrity sha512-uaJj7LKytRxZNQV8+RbzJWnJ8K2nPsOOEuX7aQstlMZKQT0164C+X2w6bnkqU3sjtLvpd5ojrezAyfZ1+0sStw== - dependencies: - history "^5.2.0" - react-router "6.3.0" - -react-router@6.3.0, react-router@^6.0.0: - version "6.3.0" - resolved "https://registry.npmjs.org/react-router/-/react-router-6.3.0.tgz#3970cc64b4cb4eae0c1ea5203a80334fdd175557" - integrity sha512-7Wh1DzVQ+tlFjkeo+ujvjSqSJmkt1+8JO+T5xklPlgrh70y7ogx75ODRW0ThWhY7S+6yEDks8TYrtQe/aoboBQ== - dependencies: - history "^5.2.0" - react-sizeme@^3.0.1: version "3.0.2" resolved "https://registry.npmjs.org/react-sizeme/-/react-sizeme-3.0.2.tgz#4a2f167905ba8f8b8d932a9e35164e459f9020e4" @@ -7225,25 +7488,16 @@ react-sizeme@^3.0.1: shallowequal "^1.1.0" throttle-debounce "^3.0.1" -react-syntax-highlighter@^13.5.3: - version "13.5.3" - resolved "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-13.5.3.tgz#9712850f883a3e19eb858cf93fad7bb357eea9c6" - integrity sha512-crPaF+QGPeHNIblxxCdf2Lg936NAHKhNhuMzRL3F9ct6aYXL3NcZtCL0Rms9+qVo6Y1EQLdXGypBNSbPL/r+qg== +react-syntax-highlighter@^15.4.5: + version "15.5.0" + resolved "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-15.5.0.tgz#4b3eccc2325fa2ec8eff1e2d6c18fa4a9e07ab20" + integrity sha512-+zq2myprEnQmH5yw6Gqc8lD55QHnpKaU8TOcFeC/Lg/MQSs8UknEA0JC4nTZGFAXC2J2Hyj/ijJ7NlabyPi2gg== dependencies: "@babel/runtime" "^7.3.1" - highlight.js "^10.1.1" - lowlight "^1.14.0" - prismjs "^1.21.0" - refractor "^3.1.0" - -react-textarea-autosize@^8.3.0: - version "8.3.3" - resolved "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.3.3.tgz#f70913945369da453fd554c168f6baacd1fa04d8" - integrity sha512-2XlHXK2TDxS6vbQaoPbMOfQ8GK7+irc2fVK6QFIcC8GOnH3zI/v481n+j1L0WaPVvKxwesnY93fEfH++sus2rQ== - dependencies: - "@babel/runtime" "^7.10.2" - use-composed-ref "^1.0.0" - use-latest "^1.0.0" + highlight.js "^10.4.1" + lowlight "^1.17.0" + prismjs "^1.27.0" + refractor "^3.6.0" react@^17.0.2: version "17.0.2" @@ -7253,6 +7507,14 @@ react@^17.0.2: loose-envify "^1.1.0" object-assign "^4.1.1" +read-pkg-up@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" + integrity sha512-WD9MTlNtI55IwYUS27iHh9tK3YoIVhxis8yKhLpTqWtml739uXc9NWTpxoHkfZf3+DkCCsXox94/VWZniuZm6A== + dependencies: + find-up "^1.0.0" + read-pkg "^1.0.0" + read-pkg-up@^7.0.1: version "7.0.1" resolved "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507" @@ -7262,6 +7524,15 @@ read-pkg-up@^7.0.1: read-pkg "^5.2.0" type-fest "^0.8.1" +read-pkg@^1.0.0: + version "1.1.0" + resolved "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" + integrity sha512-7BGwRHqt4s/uVbuyoeejRn4YmFnYZiFl4AuaeXHlgZf3sONF0SOGlxs2Pw8g6hCKupo08RafIO5YXFNOKTfwsQ== + dependencies: + load-json-file "^1.0.0" + normalize-package-data "^2.3.2" + path-type "^1.0.0" + read-pkg@^5.2.0: version "5.2.0" resolved "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc" @@ -7310,7 +7581,15 @@ readdirp@~3.6.0: dependencies: picomatch "^2.2.1" -refractor@^3.1.0: +redent@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" + integrity sha512-qtW5hKzGQZqKoh6JNSD+4lfitfPKGz42e6QwiRmPM5mmKtR0N41AbJRYu0xJi7nhOJ4WDgRkKvAk6tw4WIwR4g== + dependencies: + indent-string "^2.1.0" + strip-indent "^1.0.1" + +refractor@^3.6.0: version "3.6.0" resolved "https://registry.npmjs.org/refractor/-/refractor-3.6.0.tgz#ac318f5a0715ead790fcfb0c71f4dd83d977935a" integrity sha512-MY9W41IOWxxk31o+YvFCNyNzdkc9M20NoZK5vq6jkv4I/uh2zkWcfudj0Q1fovjUQJrNewS9NMzeTtqPf+n5EA== @@ -7351,7 +7630,7 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" -regexp.prototype.flags@^1.4.1: +regexp.prototype.flags@^1.4.1, regexp.prototype.flags@^1.4.3: version "1.4.3" resolved "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz#87cab30f80f66660181a3bb7bf5981a872b367ac" integrity sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA== @@ -7387,7 +7666,7 @@ regjsparser@^0.8.2: relateurl@^0.2.7: version "0.2.7" resolved "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" - integrity sha1-VNvzd+UUQKypCkzSdGANP/LYiKk= + integrity sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog== remark-footnotes@2.0.0: version "2.0.0" @@ -7440,7 +7719,7 @@ remark-squeeze-paragraphs@4.0.0: remove-trailing-separator@^1.0.1: version "1.1.0" resolved "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" - integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= + integrity sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw== renderkid@^2.0.4: version "2.0.7" @@ -7461,12 +7740,19 @@ repeat-element@^1.1.2: repeat-string@^1.5.4, repeat-string@^1.6.1: version "1.6.1" resolved "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" - integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= + integrity sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w== + +repeating@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" + integrity sha512-ZqtSMuVybkISo2OWvqvm7iHSWngvdaW3IpsT9/uP8v4gMi591LY6h35wdOfvQdWCKFWZWm2Y1Opp4kV7vQKT6A== + dependencies: + is-finite "^1.0.0" require-directory@^2.1.1: version "2.1.1" resolved "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" - integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== resolve-from@^4.0.0: version "4.0.0" @@ -7481,9 +7767,9 @@ resolve-from@^5.0.0: resolve-url@^0.2.1: version "0.2.1" resolved "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" - integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= + integrity sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg== -resolve@^1.10.0, resolve@^1.12.0, resolve@^1.14.2, resolve@^1.19.0, resolve@^1.3.2: +resolve@^1.10.0, resolve@^1.14.2, resolve@^1.19.0, resolve@^1.3.2: version "1.22.0" resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198" integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw== @@ -7502,14 +7788,14 @@ reusify@^1.0.4: resolved "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== -rimraf@^2.2.8, rimraf@^2.5.4, rimraf@^2.6.3: +rimraf@^2.5.4, rimraf@^2.6.3: version "2.7.1" resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== dependencies: glob "^7.1.3" -rimraf@^3.0.0, rimraf@^3.0.2: +rimraf@^3.0.2: version "3.0.2" resolved "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== @@ -7534,7 +7820,7 @@ run-parallel@^1.1.9: run-queue@^1.0.0, run-queue@^1.0.3: version "1.0.3" resolved "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz#e848396f057d223f24386924618e25694161ec47" - integrity sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec= + integrity sha512-ntymy489o0/QQplUDnpYAYUsO50K9SBrIVaKCWDOJzYJts0f9WH9RFJkyagebkw5+y1oi00R7ynNW/d12GBumg== dependencies: aproba "^1.1.1" @@ -7556,7 +7842,7 @@ safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, s safe-regex@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" - integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4= + integrity sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg== dependencies: ret "~0.1.10" @@ -7600,7 +7886,7 @@ schema-utils@^2.6.5, schema-utils@^2.7.0: ajv "^6.12.4" ajv-keywords "^3.5.2" -schema-utils@^3.0.0: +schema-utils@^3.0.0, schema-utils@^3.1.0, schema-utils@^3.1.1: version "3.1.1" resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz#bc74c4b6b6995c1d88f76a8b77bea7219e0c8281" integrity sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw== @@ -7631,24 +7917,24 @@ semver@^7.3.2, semver@^7.3.4, semver@^7.3.5: dependencies: lru-cache "^6.0.0" -send@0.17.2: - version "0.17.2" - resolved "https://registry.npmjs.org/send/-/send-0.17.2.tgz#926622f76601c41808012c8bf1688fe3906f7820" - integrity sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww== +send@0.18.0: + version "0.18.0" + resolved "https://registry.npmjs.org/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" + integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg== dependencies: debug "2.6.9" - depd "~1.1.2" - destroy "~1.0.4" + depd "2.0.0" + destroy "1.2.0" encodeurl "~1.0.2" escape-html "~1.0.3" etag "~1.8.1" fresh "0.5.2" - http-errors "1.8.1" + http-errors "2.0.0" mime "1.6.0" ms "2.1.3" - on-finished "~2.3.0" + on-finished "2.4.1" range-parser "~1.2.1" - statuses "~1.5.0" + statuses "2.0.1" serialize-javascript@^4.0.0: version "4.0.0" @@ -7664,10 +7950,17 @@ serialize-javascript@^5.0.1: dependencies: randombytes "^2.1.0" +serialize-javascript@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8" + integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag== + dependencies: + randombytes "^2.1.0" + serve-favicon@^2.5.0: version "2.5.0" resolved "https://registry.npmjs.org/serve-favicon/-/serve-favicon-2.5.0.tgz#935d240cdfe0f5805307fdfe967d88942a2cbcf0" - integrity sha1-k10kDN/g9YBTB/3+ln2IlCosvPA= + integrity sha512-FMW2RvqNr03x+C0WxTyu6sOv21oOjkq5j8tjquWccwa6ScNyGFOGJVpuS1NmTVGBAHS07xnSKotgf2ehQmf9iA== dependencies: etag "~1.8.1" fresh "0.5.2" @@ -7675,20 +7968,20 @@ serve-favicon@^2.5.0: parseurl "~1.3.2" safe-buffer "5.1.1" -serve-static@1.14.2: - version "1.14.2" - resolved "https://registry.npmjs.org/serve-static/-/serve-static-1.14.2.tgz#722d6294b1d62626d41b43a013ece4598d292bfa" - integrity sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ== +serve-static@1.15.0: + version "1.15.0" + resolved "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540" + integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g== dependencies: encodeurl "~1.0.2" escape-html "~1.0.3" parseurl "~1.3.3" - send "0.17.2" + send "0.18.0" set-blocking@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" - integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= + integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== set-value@^2.0.0, set-value@^2.0.1: version "2.0.1" @@ -7703,7 +7996,7 @@ set-value@^2.0.0, set-value@^2.0.1: setimmediate@^1.0.4: version "1.0.5" resolved "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" - integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= + integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== setprototypeof@1.2.0: version "1.2.0" @@ -7751,7 +8044,7 @@ side-channel@^1.0.4: get-intrinsic "^1.0.2" object-inspect "^1.9.0" -signal-exit@^3.0.0, signal-exit@^3.0.2: +signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3: version "3.0.7" resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== @@ -7830,20 +8123,20 @@ source-map-url@^0.4.0: resolved "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz#0af66605a745a5a2f91cf1bbf8a7afbc283dec56" integrity sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw== -source-map@^0.5.0, source-map@^0.5.6, source-map@^0.5.7: +source-map@^0.5.0, source-map@^0.5.6: version "0.5.7" resolved "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" - integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= + integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: version "0.6.1" resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== -source-map@^0.7.3, source-map@~0.7.2: - version "0.7.3" - resolved "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" - integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== +source-map@^0.7.3: + version "0.7.4" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656" + integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA== space-separated-tokens@^1.0.0: version "1.1.5" @@ -7902,10 +8195,10 @@ stable@^0.1.8: resolved "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== -stackframe@^1.1.1: - version "1.2.1" - resolved "https://registry.npmjs.org/stackframe/-/stackframe-1.2.1.tgz#1033a3473ee67f08e2f2fc8eba6aef4f845124e1" - integrity sha512-h88QkzREN/hy8eRdyNhhsO7RSJ5oyTqxxmmn0dzBIMUclZsjpfmrsg81vp8mjjAs2vAZ72nyWxRUwSwmh0e4xg== +stackframe@^1.3.4: + version "1.3.4" + resolved "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz#b881a004c8c149a5e8efef37d51b16e412943310" + integrity sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw== state-toggle@^1.0.0: version "1.0.3" @@ -7915,15 +8208,15 @@ state-toggle@^1.0.0: static-extend@^0.1.1: version "0.1.2" resolved "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" - integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY= + integrity sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g== dependencies: define-property "^0.2.5" object-copy "^0.1.0" -"statuses@>= 1.5.0 < 2", statuses@~1.5.0: - version "1.5.0" - resolved "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" - integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= +statuses@2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" + integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== store2@^2.12.0: version "2.13.2" @@ -8011,21 +8304,23 @@ string.prototype.padstart@^3.0.0: define-properties "^1.1.3" es-abstract "^1.19.1" -string.prototype.trimend@^1.0.4: - version "1.0.4" - resolved "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80" - integrity sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A== +string.prototype.trimend@^1.0.5: + version "1.0.5" + resolved "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz#914a65baaab25fbdd4ee291ca7dde57e869cb8d0" + integrity sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog== dependencies: call-bind "^1.0.2" - define-properties "^1.1.3" + define-properties "^1.1.4" + es-abstract "^1.19.5" -string.prototype.trimstart@^1.0.4: - version "1.0.4" - resolved "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz#b36399af4ab2999b4c9c648bd7a3fb2bb26feeed" - integrity sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw== +string.prototype.trimstart@^1.0.5: + version "1.0.5" + resolved "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz#5466d93ba58cfa2134839f81d7f42437e8c01fef" + integrity sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg== dependencies: call-bind "^1.0.2" - define-properties "^1.1.3" + define-properties "^1.1.4" + es-abstract "^1.19.5" string_decoder@^1.0.0, string_decoder@^1.1.1: version "1.3.0" @@ -8044,7 +8339,7 @@ string_decoder@~1.1.1: strip-ansi@^3.0.1: version "3.0.1" resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" - integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= + integrity sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg== dependencies: ansi-regex "^2.0.0" @@ -8055,6 +8350,25 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1: dependencies: ansi-regex "^5.0.1" +strip-bom@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" + integrity sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g== + dependencies: + is-utf8 "^0.2.0" + +strip-final-newline@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== + +strip-indent@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2" + integrity sha512-I5iQq6aFMM62fBEAIB/hXzwJD6EEZ0xEGCX2t7oXqaKPIRgt4WruAQ285BISgdkP+HLGWyeGmNJcpIwFeRYRUA== + dependencies: + get-stdin "^4.0.1" + strip-indent@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001" @@ -8103,6 +8417,13 @@ supports-color@^7.0.0, supports-color@^7.1.0: dependencies: has-flag "^4.0.0" +supports-color@^8.0.0: + version "8.1.1" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + supports-preserve-symlinks-flag@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" @@ -8128,6 +8449,11 @@ tapable@^1.0.0, tapable@^1.1.3: resolved "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== +tapable@^2.1.1, tapable@^2.2.0: + version "2.2.1" + resolved "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" + integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== + tar@^6.0.2: version "6.1.11" resolved "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621" @@ -8140,10 +8466,10 @@ tar@^6.0.2: mkdirp "^1.0.3" yallist "^4.0.0" -telejson@^5.3.2, telejson@^5.3.3: - version "5.3.3" - resolved "https://registry.npmjs.org/telejson/-/telejson-5.3.3.tgz#fa8ca84543e336576d8734123876a9f02bf41d2e" - integrity sha512-PjqkJZpzEggA9TBpVtJi1LVptP7tYtXB6rEubwlHap76AMjzvOdKX41CxyaW7ahhzDU1aftXnMCx5kAPDZTQBA== +telejson@^6.0.8: + version "6.0.8" + resolved "https://registry.npmjs.org/telejson/-/telejson-6.0.8.tgz#1c432db7e7a9212c1fbd941c3e5174ec385148f7" + integrity sha512-nerNXi+j8NK1QEfBHtZUN/aLdDcyupA//9kAboYLrtzZlPLpUfqbVGWb9zz91f/mIjRbAYhbgtnJHY8I1b5MBg== dependencies: "@types/is-function" "^1.0.0" global "^4.4.0" @@ -8184,6 +8510,17 @@ terser-webpack-plugin@^4.2.3: terser "^5.3.4" webpack-sources "^1.4.3" +terser-webpack-plugin@^5.1.3: + version "5.3.3" + resolved "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.3.tgz#8033db876dd5875487213e87c627bca323e5ed90" + integrity sha512-Fx60G5HNYknNTNQnzQ1VePRuu89ZVYWfjRAeT5rITuCY/1b08s49e5kSQwHDirKZWuoKOBRFS98EUUoZ9kLEwQ== + dependencies: + "@jridgewell/trace-mapping" "^0.3.7" + jest-worker "^27.4.5" + schema-utils "^3.1.1" + serialize-javascript "^6.0.0" + terser "^5.7.2" + terser@^4.1.2, terser@^4.6.3: version "4.8.0" resolved "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz#63056343d7c70bb29f3af665865a46fe03a0df17" @@ -8193,14 +8530,14 @@ terser@^4.1.2, terser@^4.6.3: source-map "~0.6.1" source-map-support "~0.5.12" -terser@^5.3.4: - version "5.12.1" - resolved "https://registry.npmjs.org/terser/-/terser-5.12.1.tgz#4cf2ebed1f5bceef5c83b9f60104ac4a78b49e9c" - integrity sha512-NXbs+7nisos5E+yXwAD+y7zrcTkMqb0dEJxIGtSKPdCBzopf7ni4odPul2aechpV7EXNvOudYOX2bb5tln1jbQ== +terser@^5.3.4, terser@^5.7.2: + version "5.14.1" + resolved "https://registry.npmjs.org/terser/-/terser-5.14.1.tgz#7c95eec36436cb11cf1902cc79ac564741d19eca" + integrity sha512-+ahUAE+iheqBTDxXhTisdA8hgvbEG1hHOQ9xmNjeUJSoi6DU/gMrKNcfZjHkyY6Alnuyc+ikYJaxxfHkT3+WuQ== dependencies: + "@jridgewell/source-map" "^0.3.2" acorn "^8.5.0" commander "^2.20.0" - source-map "~0.7.2" source-map-support "~0.5.20" test-exclude@^6.0.0: @@ -8249,24 +8586,24 @@ timers-browserify@^2.0.4: to-arraybuffer@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" - integrity sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M= + integrity sha512-okFlQcoGTi4LQBG/PgSYblw9VOyptsz2KJZqc6qtgGdes8VktzUQkj4BI2blit072iS8VODNcMA+tvnS9dnuMA== to-fast-properties@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" - integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= + integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== to-object-path@^0.3.0: version "0.3.0" resolved "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" - integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68= + integrity sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg== dependencies: kind-of "^3.0.2" to-regex-range@^2.1.0: version "2.1.1" resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" - integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg= + integrity sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg== dependencies: is-number "^3.0.0" repeat-string "^1.6.1" @@ -8288,11 +8625,6 @@ to-regex@^3.0.1, to-regex@^3.0.2: regex-not "^1.0.2" safe-regex "^1.1.0" -toggle-selection@^1.0.6: - version "1.0.6" - resolved "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz#6e45b1263f2017fa0acc7d89d78b15b8bf77da32" - integrity sha1-bkWxJj8gF/oKzH2J14sVuL932jI= - toidentifier@1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" @@ -8301,7 +8633,12 @@ toidentifier@1.0.1: tr46@~0.0.3: version "0.0.3" resolved "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" - integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= + integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== + +trim-newlines@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" + integrity sha512-Nm4cF79FhSTzrLKGDMi3I4utBtFv8qKy4sq1enftf2gMdpqI8oVQTAfySkTz5r49giVzDj88SVZXP4CeYQwjaw== trim-trailing-lines@^1.0.0: version "1.1.4" @@ -8311,7 +8648,7 @@ trim-trailing-lines@^1.0.0: trim@0.0.1: version "0.0.1" resolved "https://registry.npmjs.org/trim/-/trim-0.0.1.tgz#5858547f6b290757ee95cccc666fb50084c460dd" - integrity sha1-WFhUf2spB1fulczMZm+1AITEYN0= + integrity sha512-YzQV+TZg4AxpKxaTHK3c3D+kRDCGVEE7LemdlQZoQXn0iennk10RsIoY6ikzAqJTc9Xjl9C1/waHom/J86ziAQ== trough@^1.0.0: version "1.0.5" @@ -8333,15 +8670,22 @@ ts-pnp@^1.1.6: resolved "https://registry.npmjs.org/ts-pnp/-/ts-pnp-1.2.0.tgz#a500ad084b0798f1c3071af391e65912c86bca92" integrity sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw== -tslib@^2.0.0, tslib@^2.0.1, tslib@^2.0.3, tslib@^2.3.0: - version "2.3.1" - resolved "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" - integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== +tslib@^2.0.0, tslib@^2.0.1, tslib@^2.0.3: + version "2.4.0" + resolved "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" + integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== tty-browserify@0.0.0: version "0.0.0" resolved "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" - integrity sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY= + integrity sha512-JVa5ijo+j/sOoHGjw0sxw734b1LhBkQ3bvUGNdxnVXDCX81Yx7TFgnZygxrIIWn23hbfTaMYLwRmAxFyDuFmIw== + +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + integrity sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg== + dependencies: + prelude-ls "~1.1.2" type-fest@^0.20.2: version "0.20.2" @@ -8369,21 +8713,21 @@ type-is@~1.6.18: typedarray@^0.0.6: version "0.0.6" resolved "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" - integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= + integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== uglify-js@^3.1.4: - version "3.15.4" - resolved "https://registry.npmjs.org/uglify-js/-/uglify-js-3.15.4.tgz#fa95c257e88f85614915b906204b9623d4fa340d" - integrity sha512-vMOPGDuvXecPs34V74qDKk4iJ/SN4vL3Ow/23ixafENYvtrNvtbcgUeugTcUGRGsOF/5fU8/NYSL5Hyb3l1OJA== + version "3.16.0" + resolved "https://registry.npmjs.org/uglify-js/-/uglify-js-3.16.0.tgz#b778ba0831ca102c1d8ecbdec2d2bdfcc7353190" + integrity sha512-FEikl6bR30n0T3amyBh3LoiBdqHRy/f4H80+My34HOesOKyHfOsxAPAxOoqC0JUnC1amnO0IwkYC3sko51caSw== -unbox-primitive@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz#085e215625ec3162574dc8859abee78a59b14471" - integrity sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw== +unbox-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" + integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== dependencies: - function-bind "^1.1.1" - has-bigints "^1.0.1" - has-symbols "^1.0.2" + call-bind "^1.0.2" + has-bigints "^1.0.2" + has-symbols "^1.0.3" which-boxed-primitive "^1.0.2" unfetch@^4.2.0: @@ -8524,16 +8868,23 @@ universalify@^2.0.0: unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" - integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= + integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== unset-value@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" - integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk= + integrity sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ== dependencies: has-value "^0.3.1" isobject "^3.0.0" +untildify@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/untildify/-/untildify-2.1.0.tgz#17eb2807987f76952e9c0485fc311d06a826a2e0" + integrity sha512-sJjbDp2GodvkB0FZZcn7k6afVisqX5BZD7Yq3xp4nN2O15BBK0cLm3Vwn2vQaF7UDS0UUsrQMkkplmDI5fskig== + dependencies: + os-homedir "^1.0.0" + upath@^1.1.1: version "1.2.0" resolved "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" @@ -8549,7 +8900,7 @@ uri-js@^4.2.2: urix@^0.1.0: version "0.1.0" resolved "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" - integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= + integrity sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg== url-loader@^4.1.1: version "4.1.1" @@ -8563,28 +8914,11 @@ url-loader@^4.1.1: url@^0.11.0: version "0.11.0" resolved "https://registry.npmjs.org/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" - integrity sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE= + integrity sha512-kbailJa29QrtXnxgq+DdCEGlbTeYM2eJUxsz6vjZavrCYPMIFHMKQmSKYAIuUK2i7hgPm28a8piX5NTUtM/LKQ== dependencies: punycode "1.3.2" querystring "0.2.0" -use-composed-ref@^1.0.0: - version "1.2.1" - resolved "https://registry.npmjs.org/use-composed-ref/-/use-composed-ref-1.2.1.tgz#9bdcb5ccd894289105da2325e1210079f56bf849" - integrity sha512-6+X1FLlIcjvFMAeAD/hcxDT8tmyrWnbSPMU0EnxQuDLIxokuFzWliXBiYZuGIx+mrAMLBw0WFfCkaPw8ebzAhw== - -use-isomorphic-layout-effect@^1.0.0: - version "1.1.2" - resolved "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz#497cefb13d863d687b08477d9e5a164ad8c1a6fb" - integrity sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA== - -use-latest@^1.0.0: - version "1.2.0" - resolved "https://registry.npmjs.org/use-latest/-/use-latest-1.2.0.tgz#a44f6572b8288e0972ec411bdd0840ada366f232" - integrity sha512-d2TEuG6nSLKQLAfW3By8mKr8HurOlTkul0sOpxbClIv4SQ4iOd7BYr7VIzdbktUCnv7dua/60xzd8igMU6jmyw== - dependencies: - use-isomorphic-layout-effect "^1.0.0" - use@^3.1.0: version "3.1.1" resolved "https://registry.npmjs.org/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" @@ -8593,7 +8927,7 @@ use@^3.1.0: util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== util.promisify@1.0.0: version "1.0.0" @@ -8606,7 +8940,7 @@ util.promisify@1.0.0: util@0.10.3: version "0.10.3" resolved "https://registry.npmjs.org/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" - integrity sha1-evsa/lCAUkZInj23/g7TeTNqwPk= + integrity sha512-5KiHfsmkqacuKjkRkdV7SsfDJ2EGiPsK92s2MhNSY0craxjTdKTtqKsJaCWp4LW33ZZ0OPUv1WO/TFvNQRiQxQ== dependencies: inherits "2.0.1" @@ -8620,31 +8954,31 @@ util@^0.11.0: utila@~0.4: version "0.4.0" resolved "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c" - integrity sha1-ihagXURWV6Oupe7MWxKk+lN5dyw= + integrity sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA== utils-merge@1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" - integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= + integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== uuid-browser@^3.1.0: version "3.1.0" resolved "https://registry.npmjs.org/uuid-browser/-/uuid-browser-3.1.0.tgz#0f05a40aef74f9e5951e20efbf44b11871e56410" - integrity sha1-DwWkCu90+eWVHiDvv0SxGHHlZBA= + integrity sha512-dsNgbLaTrd6l3MMxTtouOCFw4CBFc/3a+GgYA2YyrJvyQ1u6q4pcu3ktLoUZ/VN/Aw9WsauazbgsgdfVWgAKQg== uuid@^3.3.2: version "3.4.0" resolved "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== -v8-to-istanbul@^8.0.0: - version "8.1.1" - resolved "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz#77b752fd3975e31bbcef938f85e9bd1c7a8d60ed" - integrity sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w== +v8-to-istanbul@^9.0.0: + version "9.0.0" + resolved "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.0.0.tgz#be0dae58719fc53cb97e5c7ac1d7e6d4f5b19511" + integrity sha512-HcvgY/xaRm7isYmyx+lFKA4uQmfUbN0J4M0nNItvzTvH/iQ9kW5j/t4YSR+Ge323/lrgDAWJoF46tzGQHwBHFw== dependencies: + "@jridgewell/trace-mapping" "^0.3.7" "@types/istanbul-lib-coverage" "^2.0.1" convert-source-map "^1.6.0" - source-map "^0.7.3" validate-npm-package-license@^3.0.1: version "3.0.4" @@ -8657,7 +8991,7 @@ validate-npm-package-license@^3.0.1: vary@~1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" - integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= + integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== vfile-location@^3.0.0, vfile-location@^3.2.0: version "3.2.0" @@ -8687,13 +9021,6 @@ vm-browserify@^1.0.1: resolved "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== -warning@^4.0.2: - version "4.0.3" - resolved "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3" - integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w== - dependencies: - loose-envify "^1.0.0" - watchpack-chokidar2@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz#38500072ee6ece66f3769936950ea1771be1c957" @@ -8712,10 +9039,10 @@ watchpack@^1.7.4: chokidar "^3.4.1" watchpack-chokidar2 "^2.0.1" -watchpack@^2.2.0: - version "2.3.1" - resolved "https://registry.npmjs.org/watchpack/-/watchpack-2.3.1.tgz#4200d9447b401156eeca7767ee610f8809bc9d25" - integrity sha512-x0t0JuydIo8qCNctdDrn1OzH/qDzk2+rdCOC3YzumZ42fiMqmQ7T3xQurykYMhYfHaPHTp4ZxAx2NfUo1K6QaA== +watchpack@^2.2.0, watchpack@^2.3.1: + version "2.4.0" + resolved "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d" + integrity sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg== dependencies: glob-to-regexp "^0.4.1" graceful-fs "^4.1.2" @@ -8728,7 +9055,7 @@ web-namespaces@^1.0.0: webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" - integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= + integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== webpack-dev-middleware@^3.7.3: version "3.7.3" @@ -8772,6 +9099,11 @@ webpack-sources@^1.4.0, webpack-sources@^1.4.1, webpack-sources@^1.4.3: source-list-map "^2.0.0" source-map "~0.6.1" +webpack-sources@^3.2.3: + version "3.2.3" + resolved "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" + integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== + webpack-virtual-modules@^0.2.2: version "0.2.2" resolved "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.2.2.tgz#20863dc3cb6bb2104729fff951fbe14b18bd0299" @@ -8808,10 +9140,40 @@ webpack@4: watchpack "^1.7.4" webpack-sources "^1.4.1" +"webpack@>=4.43.0 <6.0.0": + version "5.73.0" + resolved "https://registry.npmjs.org/webpack/-/webpack-5.73.0.tgz#bbd17738f8a53ee5760ea2f59dce7f3431d35d38" + integrity sha512-svjudQRPPa0YiOYa2lM/Gacw0r6PvxptHj4FuEKQ2kX05ZLkjbVc5MnPs6its5j7IZljnIqSVo/OsY2X0IpHGA== + dependencies: + "@types/eslint-scope" "^3.7.3" + "@types/estree" "^0.0.51" + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/wasm-edit" "1.11.1" + "@webassemblyjs/wasm-parser" "1.11.1" + acorn "^8.4.1" + acorn-import-assertions "^1.7.6" + browserslist "^4.14.5" + chrome-trace-event "^1.0.2" + enhanced-resolve "^5.9.3" + es-module-lexer "^0.9.0" + eslint-scope "5.1.1" + events "^3.2.0" + glob-to-regexp "^0.4.1" + graceful-fs "^4.2.9" + json-parse-even-better-errors "^2.3.1" + loader-runner "^4.2.0" + mime-types "^2.1.27" + neo-async "^2.6.2" + schema-utils "^3.1.0" + tapable "^2.1.1" + terser-webpack-plugin "^5.1.3" + watchpack "^2.3.1" + webpack-sources "^3.2.3" + whatwg-url@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" - integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0= + integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== dependencies: tr46 "~0.0.3" webidl-conversions "^3.0.0" @@ -8848,10 +9210,15 @@ widest-line@^3.1.0: dependencies: string-width "^4.0.0" +word-wrap@~1.2.3: + version "1.2.3" + resolved "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" + integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== + wordwrap@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" - integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= + integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q== worker-farm@^1.7.0: version "1.7.0" @@ -8879,12 +9246,19 @@ wrap-ansi@^7.0.0: wrappy@1: version "1.0.2" resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== ws@^8.2.3: - version "8.5.0" - resolved "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz#bfb4be96600757fe5382de12c670dab984a1ed4f" - integrity sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg== + version "8.8.0" + resolved "https://registry.npmjs.org/ws/-/ws-8.8.0.tgz#8e71c75e2f6348dbf8d78005107297056cb77769" + integrity sha512-JDAgSYQ1ksuwqfChJusw1LSJ8BizJ2e/vVu5Lxjq3YvNJNlROv1ui4i+c/kUUrPheBvQl4c5UbERhTwKa6QBJQ== + +x-default-browser@^0.4.0: + version "0.4.0" + resolved "https://registry.npmjs.org/x-default-browser/-/x-default-browser-0.4.0.tgz#70cf0da85da7c0ab5cb0f15a897f2322a6bdd481" + integrity sha512-7LKo7RtWfoFN/rHx1UELv/2zHGMx8MkZKDq1xENmOCTkfIqZJ0zZ26NEJX8czhnPXVcqS0ARjjfJB+eJ0/5Cvw== + optionalDependencies: + default-browser-id "^1.0.4" xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.1: version "4.0.2" @@ -8916,7 +9290,7 @@ yaml@^1.10.0, yaml@^1.7.2: resolved "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== -yargs-parser@^20.2.2, yargs-parser@^20.2.7: +yargs-parser@^20.2.2, yargs-parser@^20.2.9: version "20.2.9" resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== diff --git a/yarn.lock b/yarn.lock index 9311ad5a3b..a2ffb79d08 100644 --- a/yarn.lock +++ b/yarn.lock @@ -44,11 +44,58 @@ "@types/node" "^10.1.0" long "^4.0.0" +"@apollo/utils.dropunuseddefinitions@^1.1.0": + version "1.1.0" + resolved "https://registry.npmjs.org/@apollo/utils.dropunuseddefinitions/-/utils.dropunuseddefinitions-1.1.0.tgz#02b04006442eaf037f4c4624146b12775d70d929" + integrity sha512-jU1XjMr6ec9pPoL+BFWzEPW7VHHulVdGKMkPAMiCigpVIT11VmCbnij0bWob8uS3ODJ65tZLYKAh/55vLw2rbg== + +"@apollo/utils.keyvaluecache@^1.0.1": + version "1.0.1" + resolved "https://registry.npmjs.org/@apollo/utils.keyvaluecache/-/utils.keyvaluecache-1.0.1.tgz#46f310f859067efe9fa126156c6954f8381080d2" + integrity sha512-nLgYLomqjVimEzQ4cdvVQkcryi970NDvcRVPfd0OPeXhBfda38WjBq+WhQFk+czSHrmrSp34YHBxpat0EtiowA== + dependencies: + "@apollo/utils.logger" "^1.0.0" + lru-cache "^7.10.1" + "@apollo/utils.logger@^1.0.0": version "1.0.0" resolved "https://registry.npmjs.org/@apollo/utils.logger/-/utils.logger-1.0.0.tgz#6e3460a2250c2ef7c2c3b0be6b5e148a1596f12b" integrity sha512-dx9XrjyisD2pOa+KsB5RcDbWIAdgC91gJfeyLCgy0ctJMjQe7yZK5kdWaWlaOoCeX0z6YI9iYlg7vMPyMpQF3Q== +"@apollo/utils.printwithreducedwhitespace@^1.1.0": + version "1.1.0" + resolved "https://registry.npmjs.org/@apollo/utils.printwithreducedwhitespace/-/utils.printwithreducedwhitespace-1.1.0.tgz#c466299a4766eef8577a2a64c8f27712e8bd7e30" + integrity sha512-GfFSkAv3n1toDZ4V6u2d7L4xMwLA+lv+6hqXicMN9KELSJ9yy9RzuEXaX73c/Ry+GzRsBy/fdSUGayGqdHfT2Q== + +"@apollo/utils.removealiases@1.0.0": + version "1.0.0" + resolved "https://registry.npmjs.org/@apollo/utils.removealiases/-/utils.removealiases-1.0.0.tgz#75f6d83098af1fcae2d3beb4f515ad4a8452a8c1" + integrity sha512-6cM8sEOJW2LaGjL/0vHV0GtRaSekrPQR4DiywaApQlL9EdROASZU5PsQibe2MWeZCOhNrPRuHh4wDMwPsWTn8A== + +"@apollo/utils.sortast@^1.1.0": + version "1.1.0" + resolved "https://registry.npmjs.org/@apollo/utils.sortast/-/utils.sortast-1.1.0.tgz#93218c7008daf3e2a0725196085a33f5aab5ad07" + integrity sha512-VPlTsmUnOwzPK5yGZENN069y6uUHgeiSlpEhRnLFYwYNoJHsuJq2vXVwIaSmts015WTPa2fpz1inkLYByeuRQA== + dependencies: + lodash.sortby "^4.7.0" + +"@apollo/utils.stripsensitiveliterals@^1.2.0": + version "1.2.0" + resolved "https://registry.npmjs.org/@apollo/utils.stripsensitiveliterals/-/utils.stripsensitiveliterals-1.2.0.tgz#4920651f36beee8e260e12031a0c5863ad0c7b28" + integrity sha512-E41rDUzkz/cdikM5147d8nfCFVKovXxKBcjvLEQ7bjZm/cg9zEcXvS6vFY8ugTubI3fn6zoqo0CyU8zT+BGP9w== + +"@apollo/utils.usagereporting@^1.0.0": + version "1.0.0" + resolved "https://registry.npmjs.org/@apollo/utils.usagereporting/-/utils.usagereporting-1.0.0.tgz#b81df180f4ca78b91a22cb49105174a7f070db1e" + integrity sha512-5PL7hJMkTPmdo3oxPtigRrIyPxDk/ddrUryHPDaezL1lSFExpNzsDd2f1j0XJoHOg350GRd3LyD64caLA2PU1w== + dependencies: + "@apollo/utils.dropunuseddefinitions" "^1.1.0" + "@apollo/utils.printwithreducedwhitespace" "^1.1.0" + "@apollo/utils.removealiases" "1.0.0" + "@apollo/utils.sortast" "^1.1.0" + "@apollo/utils.stripsensitiveliterals" "^1.2.0" + apollo-reporting-protobuf "^3.3.1" + "@apollographql/apollo-tools@^0.5.3": version "0.5.3" resolved "https://registry.npmjs.org/@apollographql/apollo-tools/-/apollo-tools-0.5.3.tgz#ba241d50f0849150ca0de54fd2927160033bc0bc" @@ -61,6 +108,29 @@ dependencies: xss "^1.0.8" +"@ardatan/relay-compiler@12.0.0": + version "12.0.0" + resolved "https://registry.npmjs.org/@ardatan/relay-compiler/-/relay-compiler-12.0.0.tgz#2e4cca43088e807adc63450e8cab037020e91106" + integrity sha512-9anThAaj1dQr6IGmzBMcfzOQKTa5artjuPmw8NYK/fiGEMjADbSguBY2FMDykt+QhilR3wc9VA/3yVju7JHg7Q== + dependencies: + "@babel/core" "^7.14.0" + "@babel/generator" "^7.14.0" + "@babel/parser" "^7.14.0" + "@babel/runtime" "^7.0.0" + "@babel/traverse" "^7.14.0" + "@babel/types" "^7.0.0" + babel-preset-fbjs "^3.4.0" + chalk "^4.0.0" + fb-watchman "^2.0.0" + fbjs "^3.0.0" + glob "^7.1.1" + immutable "~3.7.6" + invariant "^2.2.4" + nullthrows "^1.1.1" + relay-runtime "12.0.0" + signedsource "^1.0.0" + yargs "^15.3.1" + "@asyncapi/avro-schema-parser@^0.3.0": version "0.3.0" resolved "https://registry.npmjs.org/@asyncapi/avro-schema-parser/-/avro-schema-parser-0.3.0.tgz#6922acc559ef999c57e81297d78ffe680fc92b3c" @@ -209,9 +279,9 @@ tslib "^2.0.0" "@azure/identity@^2.0.1", "@azure/identity@^2.0.4": - version "2.0.4" - resolved "https://registry.npmjs.org/@azure/identity/-/identity-2.0.4.tgz#f5cfde0daf1b9ebaaff3ed6c504f50d7d7c939a5" - integrity sha512-ZgFubAsmo7dji63NLPaot6O7pmDfceAUPY57uphSCr0hmRj+Cakqb4SUz5SohCHFtscrhcmejRU903Fowz6iXg== + version "2.0.5" + resolved "https://registry.npmjs.org/@azure/identity/-/identity-2.0.5.tgz#4475f1222a3cbf5c5ddec644dd3fb293256703d3" + integrity sha512-fSQTu9dS0P+lw1Gfct6t7TuRYybULL/E3wJjXLc1xr6RQXpmenJspi0lKzq3XFjLP5MzBlToKY3ZkYoAXPz1zA== dependencies: "@azure/abort-controller" "^1.0.0" "@azure/core-auth" "^1.3.0" @@ -258,19 +328,17 @@ dependencies: debug "^4.1.1" -"@azure/msal-common@^6.3.0": - version "6.3.0" - resolved "https://registry.npmjs.org/@azure/msal-common/-/msal-common-6.3.0.tgz#a0cdb6be1ae3cfdc5acf3c32979375a0fef433b2" - integrity sha512-ZyLq9GdnLBi/83YpysE86TFKbA0TuvfNAN5Psqu20cdAjLo/4rw4ttiItdh1G//XeGErHk9qn57gi2AYU1b5/Q== +"@azure/msal-common@^7.0.0": + version "7.0.0" + resolved "https://registry.npmjs.org/@azure/msal-common/-/msal-common-7.0.0.tgz#f4b52c6d9591cf8720dcb24c1d21fce2d186f871" + integrity sha512-EkaHGjv0kw1RljhboeffM91b+v9d5VtmyG+0a/gvdqjbLu3kDzEfoaS5BNM9QqMzbxgZylsjAjQDtxdHLX/ziA== "@azure/msal-node@^1.1.0", "@azure/msal-node@^1.3.0": - version "1.9.0" - resolved "https://registry.npmjs.org/@azure/msal-node/-/msal-node-1.9.0.tgz#d42d848d2997aa9559d6bd6e1ec4f602b29dc1ba" - integrity sha512-lw6ejz1WPqcdjkwp91Gidte98+kfGxHk9eYSmmpUChzrUUrZMFGvrtrvG3Qnr6bp5d4WijVge9LMe+2QQUMhoA== + version "1.10.0" + resolved "https://registry.npmjs.org/@azure/msal-node/-/msal-node-1.10.0.tgz#ee3a26201c4899cc6928cc331c31d3568d3cb728" + integrity sha512-oSv9mg199FpRTe+fZ3o9NDYpKShOHqeceaNcCHJcKUaAaCojAbfbxD1Cvsti8BEsLKE6x0HcnjilnM1MKmZekA== dependencies: - "@azure/msal-common" "^6.3.0" - axios "^0.21.4" - https-proxy-agent "^5.0.0" + "@azure/msal-common" "^7.0.0" jsonwebtoken "^8.5.1" uuid "^8.3.0" @@ -372,7 +440,7 @@ jsesc "^2.5.1" source-map "^0.5.0" -"@babel/helper-annotate-as-pure@^7.16.7": +"@babel/helper-annotate-as-pure@^7.16.0", "@babel/helper-annotate-as-pure@^7.16.7": version "7.16.7" resolved "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz#bb2339a7534a9c128e3102024c60760a3a7f3862" integrity sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw== @@ -486,7 +554,7 @@ dependencies: "@babel/types" "^7.16.7" -"@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.16.7": +"@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.16.0", "@babel/helper-module-imports@^7.16.7": version "7.16.7" resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz#25612a8091a999704461c8a222d0efec5d091437" integrity sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg== @@ -619,7 +687,7 @@ "@babel/traverse" "^7.17.3" "@babel/types" "^7.17.0" -"@babel/highlight@^7.0.0", "@babel/highlight@^7.16.7": +"@babel/highlight@^7.0.0": version "7.16.10" resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz#744f2eb81579d6eea753c227b0f570ad785aba88" integrity sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw== @@ -628,6 +696,15 @@ chalk "^2.0.0" js-tokens "^4.0.0" +"@babel/highlight@^7.16.7": + version "7.17.12" + resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.17.12.tgz#257de56ee5afbd20451ac0a75686b6b404257351" + integrity sha512-7yykMVF3hfZY2jsHZEEgLc+3x4o1O+fYyULu11GynEUQNwB6lua+IIQn1FiJxNucd5UlyJryrwsOh8PL9Sn8Qg== + dependencies: + "@babel/helper-validator-identifier" "^7.16.7" + chalk "^2.0.0" + js-tokens "^4.0.0" + "@babel/parser@^7.1.0", "@babel/parser@^7.13.16", "@babel/parser@^7.14.0", "@babel/parser@^7.14.7", "@babel/parser@^7.16.10", "@babel/parser@^7.16.12", "@babel/parser@^7.16.7", "@babel/parser@^7.16.8", "@babel/parser@^7.17.3", "@babel/parser@^7.17.8": version "7.17.9" resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.17.9.tgz#9c94189a6062f0291418ca021077983058e171ef" @@ -1364,13 +1441,20 @@ dependencies: regenerator-runtime "^0.13.4" -"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.1", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.4", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.14.6", "@babel/runtime@^7.15.4", "@babel/runtime@^7.16.3", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.0", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": +"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.1", "@babel/runtime@^7.10.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.14.6", "@babel/runtime@^7.15.4", "@babel/runtime@^7.16.3", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.0", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": version "7.17.7" resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.7.tgz#a5f3328dc41ff39d803f311cfe17703418cf9825" integrity sha512-L6rvG9GDxaLgFjg41K+5Yv9OMrU98sWe+Ykmc6FDJW/+vYZMhdOMKkISgzptMaERHvS2Y2lw9MDRm2gHhlQQoA== dependencies: regenerator-runtime "^0.13.4" +"@babel/runtime@^7.10.4": + version "7.18.3" + resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.3.tgz#c7b654b57f6f63cf7f8b418ac9ca04408c4579f4" + integrity sha512-38Y8f7YUhce/K7RMwTp7m0uCumpv9hZkitCbBClqQIow1qSbCvGkcegKOXpEWCQLfWmevgRiWokZ1GkpfhbZug== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/template@^7.16.7", "@babel/template@^7.3.3": version "7.16.7" resolved "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz#8d126c8701fde4d66b264b3eba3d96f07666d155" @@ -1462,10 +1546,10 @@ resolve-from "^5.0.0" semver "^5.4.1" -"@changesets/assemble-release-plan@^5.1.2": - version "5.1.2" - resolved "https://registry.npmjs.org/@changesets/assemble-release-plan/-/assemble-release-plan-5.1.2.tgz#63ed3a00f62b5af08a82e83801a252ac9726c625" - integrity sha512-nOFyDw4APSkY/vh5WNwGEtThPgEjVShp03PKVdId6wZTJALVcAALCSLmDRfeqjE2z9EsGJb7hZdDlziKlnqZgw== +"@changesets/assemble-release-plan@^5.1.3": + version "5.1.3" + resolved "https://registry.npmjs.org/@changesets/assemble-release-plan/-/assemble-release-plan-5.1.3.tgz#b415c5db64e5a30c53aed8c1adc5ab4c4aaad283" + integrity sha512-I+TTkUoqvxBEuDLoJfJYKDXIJ+nyiTbVJ8KGhpXEsLq4N/ms/AStSbouJwF2d/p3cB+RCPr5+gXh31GSN4kA7w== dependencies: "@babel/runtime" "^7.10.4" "@changesets/errors" "^0.1.4" @@ -1482,18 +1566,18 @@ "@changesets/types" "^5.0.0" "@changesets/cli@^2.14.0": - version "2.22.0" - resolved "https://registry.npmjs.org/@changesets/cli/-/cli-2.22.0.tgz#3bdbfb4a7a81ef37f63114e77da84e23f906b763" - integrity sha512-4bA3YoBkd5cm5WUxmrR2N9WYE7EeQcM+R3bVYMUj2NvffkQVpU3ckAI+z8UICoojq+HRl2OEwtz+S5UBmYY4zw== + version "2.23.0" + resolved "https://registry.npmjs.org/@changesets/cli/-/cli-2.23.0.tgz#e325b2d1b0484188671f684773b8cd5d42d068f1" + integrity sha512-Gi3tMi0Vr6eNd8GX6q73tbOm9XOzGfuLEm4PYVeWG2neg5DlRGNOjYwrFULJ/An3N9MHtHn4r5h1Qvnju9Ijug== dependencies: "@babel/runtime" "^7.10.4" "@changesets/apply-release-plan" "^6.0.0" - "@changesets/assemble-release-plan" "^5.1.2" + "@changesets/assemble-release-plan" "^5.1.3" "@changesets/changelog-git" "^0.1.11" "@changesets/config" "^2.0.0" "@changesets/errors" "^0.1.4" "@changesets/get-dependents-graph" "^1.3.2" - "@changesets/get-release-plan" "^3.0.8" + "@changesets/get-release-plan" "^3.0.9" "@changesets/git" "^1.3.2" "@changesets/logger" "^0.0.5" "@changesets/pre" "^1.0.11" @@ -1503,6 +1587,7 @@ "@manypkg/get-packages" "^1.1.3" "@types/is-ci" "^3.0.0" "@types/semver" "^6.0.0" + ansi-colors "^4.1.3" chalk "^2.1.0" enquirer "^2.3.0" external-editor "^3.1.0" @@ -1517,7 +1602,7 @@ semver "^5.4.1" spawndamnit "^2.0.0" term-size "^2.1.0" - tty-table "^2.8.10" + tty-table "^4.1.5" "@changesets/config@^2.0.0": version "2.0.0" @@ -1550,13 +1635,13 @@ fs-extra "^7.0.1" semver "^5.4.1" -"@changesets/get-release-plan@^3.0.8": - version "3.0.8" - resolved "https://registry.npmjs.org/@changesets/get-release-plan/-/get-release-plan-3.0.8.tgz#2ac7c4f7903aedede3d27af66311ad1db7937e5d" - integrity sha512-TJYiWNuP0Lzu2dL/KHuk75w7TkiE5HqoYirrXF7SJIxkhlgH9toQf2C7IapiFTObtuF1qDN8HJAX1CuIOwXldg== +"@changesets/get-release-plan@^3.0.9": + version "3.0.9" + resolved "https://registry.npmjs.org/@changesets/get-release-plan/-/get-release-plan-3.0.9.tgz#d445660f3679cb65e05e02adfbca037a25b45943" + integrity sha512-5C1r4DcOjVxcCvPmXpymeyT6mdSTLCNiB2L+5uf19BRkDKndJdIQorH5Fe2XBR2nHUcZQFT+2TXDzCepat969w== dependencies: "@babel/runtime" "^7.10.4" - "@changesets/assemble-release-plan" "^5.1.2" + "@changesets/assemble-release-plan" "^5.1.3" "@changesets/config" "^2.0.0" "@changesets/pre" "^1.0.11" "@changesets/read" "^0.5.5" @@ -1675,15 +1760,16 @@ "@lezer/common" "^0.16.0" "@codemirror/language@^0.20.0": - version "0.20.1" - resolved "https://registry.npmjs.org/@codemirror/language/-/language-0.20.1.tgz#27b41f9a5234455af083cbc90db531ef296899e7" - integrity sha512-AqaFeI3hbsuxU0IpVrR8dhHkrEAAEuVcwEO66JwAbJaE7IPOov+Y38KWqcQNgE1/osQGKHx9Zag0hng9V6OLsw== + version "0.20.2" + resolved "https://registry.npmjs.org/@codemirror/language/-/language-0.20.2.tgz#31c3712eac2251810986272dcd6a50510e0c1529" + integrity sha512-WB3Bnuusw0xhVvhBocieYKwJm04SOk5bPoOEYksVHKHcGHFOaYaw+eZVxR4gIqMMcGzOIUil0FsCmFk8yrhHpw== dependencies: "@codemirror/state" "^0.20.0" "@codemirror/view" "^0.20.0" "@lezer/common" "^0.16.0" "@lezer/highlight" "^0.16.0" "@lezer/lr" "^0.16.0" + style-mod "^4.0.0" "@codemirror/legacy-modes@^0.20.0": version "0.20.0" @@ -1726,9 +1812,9 @@ "@lezer/highlight" "^0.16.0" "@codemirror/view@^0.20.0", "@codemirror/view@^0.20.2": - version "0.20.5" - resolved "https://registry.npmjs.org/@codemirror/view/-/view-0.20.5.tgz#ba0b46c3f6c9a2282accb196cdd5fd9bb11f3470" - integrity sha512-oUKoSLIKo7ps3dRc2pE6U80TOOPPA/s+fWBxoiy6BL1lUXegmqnFp2iVKKSJGoI8k08BHwW5knEsN6ebuZjc9w== + version "0.20.7" + resolved "https://registry.npmjs.org/@codemirror/view/-/view-0.20.7.tgz#1d0acc740f71f92abef4b437c030d4e6c39ab6dc" + integrity sha512-pqEPCb9QFTOtHgAH5XU/oVy9UR/Anj6r+tG5CRmkNVcqSKEPmBU05WtN/jxJCFZBXf6HumzWC9ydE4qstO3TxQ== dependencies: "@codemirror/state" "^0.20.0" style-mod "^4.0.0" @@ -1739,17 +1825,12 @@ resolved "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== -"@cspotcode/source-map-consumer@0.8.0": - version "0.8.0" - resolved "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz#33bf4b7b39c178821606f669bbc447a6a629786b" - integrity sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg== - -"@cspotcode/source-map-support@0.7.0": - version "0.7.0" - resolved "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz#4789840aa859e46d2f3173727ab707c66bf344f5" - integrity sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA== +"@cspotcode/source-map-support@^0.8.0": + version "0.8.1" + resolved "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" + integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== dependencies: - "@cspotcode/source-map-consumer" "0.8.0" + "@jridgewell/trace-mapping" "0.3.9" "@cypress/request@^2.88.10": version "2.88.10" @@ -1840,6 +1921,28 @@ resolved "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz#bbbff68978fefdbe68ccb533bc8cbe1d1afb5413" integrity sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow== +"@emotion/is-prop-valid@^1.1.0": + version "1.1.2" + resolved "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.1.2.tgz#34ad6e98e871aa6f7a20469b602911b8b11b3a95" + integrity sha512-3QnhqeL+WW88YjYbQL5gUIkthuMw7a0NGbZ7wfFVk2kg/CK5w8w5FFa0RzWjyY1+sujN0NWbtSHH6OJmWHtJpQ== + dependencies: + "@emotion/memoize" "^0.7.4" + +"@emotion/memoize@^0.7.4": + version "0.7.5" + resolved "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.5.tgz#2c40f81449a4e554e9fc6396910ed4843ec2be50" + integrity sha512-igX9a37DR2ZPGYtV6suZ6whr8pTFtyHL3K/oLUotxpSVO2ASaprmAe2Dkq7tBo7CRY7MMDrAa9nuQP9/YG8FxQ== + +"@emotion/stylis@^0.8.4": + version "0.8.5" + resolved "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz#deacb389bd6ee77d1e7fcaccce9e16c5c7e78e04" + integrity sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ== + +"@emotion/unitless@^0.7.4": + version "0.7.5" + resolved "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz#77211291c1900a700b8a78cfafda3160d76949ed" + integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg== + "@endemolshinegroup/cosmiconfig-typescript-loader@3.0.2": version "3.0.2" resolved "https://registry.npmjs.org/@endemolshinegroup/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-3.0.2.tgz#eea4635828dde372838b0909693ebd9aafeec22d" @@ -1881,15 +1984,15 @@ dependencies: tiny-lru "7.0.6" -"@eslint/eslintrc@^1.2.3": - version "1.2.3" - resolved "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.3.tgz#fcaa2bcef39e13d6e9e7f6271f4cc7cae1174886" - integrity sha512-uGo44hIwoLGNyduRpjdEpovcbMdd+Nv7amtmJxnKmI8xj6yd5LncmSwDa5NgX/41lIFJtkjD6YdVfgEzPfJ5UA== +"@eslint/eslintrc@^1.3.0": + version "1.3.0" + resolved "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz#29f92c30bb3e771e4a2048c95fa6855392dfac4f" + integrity sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw== dependencies: ajv "^6.12.4" debug "^4.3.2" espree "^9.3.2" - globals "^13.9.0" + globals "^13.15.0" ignore "^5.2.0" import-fresh "^3.2.1" js-yaml "^4.1.0" @@ -1908,6 +2011,11 @@ resolved "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.2.tgz#30aa825f11d438671d585bd44e7fd564535fc210" integrity sha512-82cpyJyKRoQoRi+14ibCeGPu0CwypgtBAdBhq1WfvagpCZNKqwXbKwXllYSMG91DhmG4jt9gN8eP6lGOtozuaw== +"@gar/promisify@^1.1.3": + version "1.1.3" + resolved "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6" + integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw== + "@gitbeaker/browser@^35.6.0": version "35.6.0" resolved "https://registry.npmjs.org/@gitbeaker/browser/-/browser-35.6.0.tgz#a639ae57eeb4d4829b73a634f757ef5841ad4116" @@ -1950,12 +2058,12 @@ qs "^6.10.1" xcase "^2.0.1" -"@google-cloud/container@^3.0.0": - version "3.0.1" - resolved "https://registry.npmjs.org/@google-cloud/container/-/container-3.0.1.tgz#46bcbc05e1d45e0f182f6eda49d82edcc2c0118e" - integrity sha512-5Ae76MFhGJm+PtQM8Wn/2BCOzLH9Aej8HdGRcsF/DA1ejUy+Q6HzwsHPTcyNExRrtxC4jjcB59IQn0fwEVayZQ== +"@google-cloud/container@^4.0.0": + version "4.0.1" + resolved "https://registry.npmjs.org/@google-cloud/container/-/container-4.0.1.tgz#a3206d3030539e09b7beee8f351fad86c77af79d" + integrity sha512-JpsOsdKnCQm1b5aVNnO/2Urdg+qbCLi/srEnTupRj73Pu5wXLHXOCe3vy8Z2GvyCqu7qr89fMREQ4kO4QijnHA== dependencies: - google-gax "^2.24.1" + google-gax "^3.0.1" "@google-cloud/firestore@^5.0.2": version "5.0.2" @@ -1980,43 +2088,55 @@ resolved "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-2.0.1.tgz#13350ee609346435c795bbfe133a08dfeab78d65" integrity sha512-ZDG38U/Yy6Zr21LaR3BTiiLtpJl6RkPS/JwoRT453G+6Q1DhlV0waNf8Lfu+YVYGIIxgKnLayJRfYlFJfiI8iQ== -"@google-cloud/promisify@^2.0.0": - version "2.0.3" - resolved "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-2.0.3.tgz#f934b5cdc939e3c7039ff62b9caaf59a9d89e3a8" - integrity sha512-d4VSA86eL/AFTe5xtyZX+ePUjE8dIFu2T8zmdeNBSa5/kNgXPCx/o/wbFNHAGLJdGnk1vddRuMESD9HbOC8irw== +"@google-cloud/promisify@^3.0.0": + version "3.0.0" + resolved "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-3.0.0.tgz#5cd6941fc30c4acac18051706aa5af96069bd3e3" + integrity sha512-91ArYvRgXWb73YvEOBMmOcJc0bDRs5yiVHnqkwoG0f3nm7nZuipllz6e7BvFESBvjkDTBC0zMD8QxedUwNLc1A== -"@google-cloud/storage@^5.6.0", "@google-cloud/storage@^5.8.0": - version "5.20.4" - resolved "https://registry.npmjs.org/@google-cloud/storage/-/storage-5.20.4.tgz#9f5eaab85bbbc8656e660cc4fb2056443a71dcc9" - integrity sha512-FAquqI1imd6Nq1ifdismGenYzC65DENEzIFAq4uwZMulxh32RvdzWdzx28fKzUDsoAkEfBpXJuptC31hLaHZYg== +"@google-cloud/storage@^6.0.0": + version "6.1.0" + resolved "https://registry.npmjs.org/@google-cloud/storage/-/storage-6.1.0.tgz#f882a969c5637ff445764d7b172f68fd0d9e63f8" + integrity sha512-zqZwzpRWCJuPne7x9Vc2H79zANl0uh9bNPGis0xAuC88ZEvBXfQqYCAVyiL1YIxi7rf51l8wy9vBr1pONMfxxA== dependencies: "@google-cloud/paginator" "^3.0.7" "@google-cloud/projectify" "^2.0.0" - "@google-cloud/promisify" "^2.0.0" + "@google-cloud/promisify" "^3.0.0" abort-controller "^3.0.0" arrify "^2.0.0" async-retry "^1.3.3" compressible "^2.0.12" - configstore "^5.0.0" duplexify "^4.0.0" ent "^2.2.0" extend "^3.0.2" - gaxios "^4.0.0" - google-auth-library "^7.14.1" - hash-stream-validation "^0.2.2" + gaxios "^5.0.0" + google-auth-library "^8.0.1" mime "^3.0.0" mime-types "^2.0.8" p-limit "^3.0.1" pumpify "^2.0.0" - retry-request "^4.2.2" + retry-request "^5.0.0" stream-events "^1.0.4" - teeny-request "^7.1.3" - xdg-basedir "^4.0.0" + teeny-request "^8.0.0" + uuid "^8.0.0" -"@graphiql/toolkit@^0.4.5": - version "0.4.5" - resolved "https://registry.npmjs.org/@graphiql/toolkit/-/toolkit-0.4.5.tgz#9fb5020444712562eae9581724695c235f610836" - integrity sha512-QXuuMSSK/0GfBS7tltrGZdyhIvm6oe9TK4VW9pfa8dALYttpzyJ64Q4Sx9I1Ng++yOMJWziM/ksa043zkNHsjQ== +"@graphiql/react@^0.4.2": + version "0.4.2" + resolved "https://registry.npmjs.org/@graphiql/react/-/react-0.4.2.tgz#2a12c7cf0f3945022c2231ce4e814561f2363ef7" + integrity sha512-Df2BaA0HGRkzETCaUw4ZSDe///sfIAGZ6H436A08hmg7SU3yoWJboWGWGc/QD2NyBKVgVBMmzmHcgWhsYh03EA== + dependencies: + "@graphiql/toolkit" "^0.6.0" + codemirror "^5.65.3" + codemirror-graphql "^1.3.2" + copy-to-clipboard "^3.2.0" + escape-html "^1.0.3" + graphql-language-service "^5.0.6" + markdown-it "^12.2.0" + set-value "^4.1.0" + +"@graphiql/toolkit@^0.6.0": + version "0.6.0" + resolved "https://registry.npmjs.org/@graphiql/toolkit/-/toolkit-0.6.0.tgz#e508e58f305a976222f6adb84ab0ec748c26ab60" + integrity sha512-mbAwbt4lw2bFp9QW3NLVmh/r/ymczlcqvXk1HfvuC7Ma4h0yzrJiQauJyh1/gF5kjqevcRSquvP/YQJSvh1u6w== dependencies: "@n1ru4l/push-pull-async-iterable-iterator" "^3.1.0" meros "^1.1.4" @@ -2078,13 +2198,13 @@ tslib "~2.3.0" "@graphql-codegen/graphql-modules-preset@^2.3.2": - version "2.3.11" - resolved "https://registry.npmjs.org/@graphql-codegen/graphql-modules-preset/-/graphql-modules-preset-2.3.11.tgz#ba3c8435badfd6ffea0906a53e3fcb20e00602d9" - integrity sha512-nhh2C7Aq98pbVkD9gwwTIBO8GYbw13ztaaJn11YW4X515OcxYbrDTldqoNerBNlQvghiYYSSW/hUGll9f5sIvg== + version "2.3.13" + resolved "https://registry.npmjs.org/@graphql-codegen/graphql-modules-preset/-/graphql-modules-preset-2.3.13.tgz#181ab8acacf376c38d572d6d50aad609aba3cd3d" + integrity sha512-hi/vKUrVa62d5W2euSapUqTNf2ysfq4RjRc0Pcren7zPqMzD3QgbYRLjDtPsXXQntZDmDxoJVd1BEzjpPGD2/g== dependencies: "@graphql-codegen/plugin-helpers" "^2.4.0" - "@graphql-codegen/visitor-plugin-common" "2.8.0" - "@graphql-tools/utils" "8.6.9" + "@graphql-codegen/visitor-plugin-common" "2.9.1" + "@graphql-tools/utils" "8.6.13" change-case-all "1.0.14" parse-filepath "^1.0.2" tslib "~2.4.0" @@ -2111,36 +2231,36 @@ tslib "~2.3.0" "@graphql-codegen/typescript-resolvers@^2.4.3": - version "2.6.4" - resolved "https://registry.npmjs.org/@graphql-codegen/typescript-resolvers/-/typescript-resolvers-2.6.4.tgz#857e24279603f4e8f7293385a90a1f2498eb672e" - integrity sha512-BTV5q9d7V+4mVyYRnE8N7HyArmvDbUEY5JwyV94jhlN0AOq2zKQBrs8MRvx8v1VenjTGV+PlnDpF3aEtQu8o+g== + version "2.6.6" + resolved "https://registry.npmjs.org/@graphql-codegen/typescript-resolvers/-/typescript-resolvers-2.6.6.tgz#c42cabbc05103b44a29df5ac4a015f2a8e8ab4aa" + integrity sha512-6yVU0rojI64qZbAtGy4+5kA1xJbolcWbefd4y/nwLOn2g99QBHt9nQRCVl96rppfBNinLPkiJCtQ0thOHu9glg== dependencies: "@graphql-codegen/plugin-helpers" "^2.4.0" - "@graphql-codegen/typescript" "^2.4.11" - "@graphql-codegen/visitor-plugin-common" "2.8.0" + "@graphql-codegen/typescript" "^2.5.1" + "@graphql-codegen/visitor-plugin-common" "2.9.1" "@graphql-tools/utils" "^8.1.1" auto-bind "~4.0.0" tslib "~2.4.0" -"@graphql-codegen/typescript@^2.4.11", "@graphql-codegen/typescript@^2.4.2": - version "2.4.11" - resolved "https://registry.npmjs.org/@graphql-codegen/typescript/-/typescript-2.4.11.tgz#b9d8bddaeb79ff4a85e1d0f9c774afba7423177c" - integrity sha512-K3oDLPJRH9Wgpg9TOvb7L+xrJZ8HxkIzV2umqGn54c+8DQjvnRFBIYRO0THgUBMnEauE2sEy6RZkGHGfgQUruA== +"@graphql-codegen/typescript@^2.4.2", "@graphql-codegen/typescript@^2.5.1": + version "2.5.1" + resolved "https://registry.npmjs.org/@graphql-codegen/typescript/-/typescript-2.5.1.tgz#5131485ff3ac83d5bc0aae11a2af0c9bdc08854c" + integrity sha512-D/9V2VfVIE4Mu5UiMGQtxyFU5xe1ZkAZi8g/IsqymW8rqlhTwsGhtk4JR55qPfOYxR8G94RJSJpzgNakRneytw== dependencies: "@graphql-codegen/plugin-helpers" "^2.4.0" "@graphql-codegen/schema-ast" "^2.4.1" - "@graphql-codegen/visitor-plugin-common" "2.8.0" + "@graphql-codegen/visitor-plugin-common" "2.9.1" auto-bind "~4.0.0" tslib "~2.4.0" -"@graphql-codegen/visitor-plugin-common@2.8.0": - version "2.8.0" - resolved "https://registry.npmjs.org/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-2.8.0.tgz#f1de3bd5ee123e6f72c06423912a3a83a6044938" - integrity sha512-29MOaxBog7qaEhmeCzJn2mONSbcA+slCTzHN4nJ3aZl4KrC9V32rXlQpG5x0qHbFQ1LaG1f5gPO83xbiAeMBIw== +"@graphql-codegen/visitor-plugin-common@2.9.1": + version "2.9.1" + resolved "https://registry.npmjs.org/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-2.9.1.tgz#17dfe33e19e846e7475ab9d4ff43de5130e18397" + integrity sha512-j9eGOSGt+sJcwv0ijhZiQ2cF/0ponscekNVoF+vHdOT4RB0qgOQxykPBk6EbKxIHECnkdV8ARdPVTA21A93/QQ== dependencies: "@graphql-codegen/plugin-helpers" "^2.4.0" "@graphql-tools/optimize" "^1.0.1" - "@graphql-tools/relay-operation-optimizer" "^6.3.7" + "@graphql-tools/relay-operation-optimizer" "^6.4.14" "@graphql-tools/utils" "^8.3.0" auto-bind "~4.0.0" change-case-all "1.0.14" @@ -2318,14 +2438,14 @@ tslib "~2.3.0" yaml-ast-parser "^0.0.43" -"@graphql-tools/relay-operation-optimizer@^6.3.7": - version "6.4.9" - resolved "https://registry.npmjs.org/@graphql-tools/relay-operation-optimizer/-/relay-operation-optimizer-6.4.9.tgz#f3e1960ac8e6429a09156836701a03ebf523f0e1" - integrity sha512-fHMR1bNyx+ryBnmtXWnYmxvQLEx0O3yIG0MTVB1Mp6GlsyKTGI3O9njvjv0EI2JmCszs1uakT/6BW0LO37E6tQ== +"@graphql-tools/relay-operation-optimizer@^6.4.14": + version "6.4.14" + resolved "https://registry.npmjs.org/@graphql-tools/relay-operation-optimizer/-/relay-operation-optimizer-6.4.14.tgz#812f32123a1351615a72b4e832852daac7bd64b3" + integrity sha512-vqch2M/sIUfMmlRJ4eCupiHlVPXWOPVpHX9yCZwRrpNg0Eaokyc57NSjJuKVV3KcvcJKf03qfMK2PxFbfDvN9A== dependencies: - "@graphql-tools/utils" "8.6.9" - relay-compiler "12.0.0" - tslib "~2.3.0" + "@ardatan/relay-compiler" "12.0.0" + "@graphql-tools/utils" "8.6.13" + tslib "^2.4.0" "@graphql-tools/schema@8.3.10", "@graphql-tools/schema@^8.0.0", "@graphql-tools/schema@^8.1.1", "@graphql-tools/schema@^8.1.2", "@graphql-tools/schema@^8.3.1": version "8.3.10" @@ -2359,6 +2479,13 @@ value-or-promise "^1.0.11" ws "^8.3.0" +"@graphql-tools/utils@8.6.13": + version "8.6.13" + resolved "https://registry.npmjs.org/@graphql-tools/utils/-/utils-8.6.13.tgz#2b4fb7f9f8a29b25eecd44551fb95974de32f969" + integrity sha512-FiVqrQzj4cgz0HcZ3CxUs8NtBGPZFpmsVyIgwmL6YCwIhjJQnT72h8G3/vk5zVfjfesht85YGp0inWWuoCKWzg== + dependencies: + tslib "^2.4.0" + "@graphql-tools/utils@8.6.9", "@graphql-tools/utils@^8.1.1", "@graphql-tools/utils@^8.3.0", "@graphql-tools/utils@^8.5.1", "@graphql-tools/utils@^8.5.2", "@graphql-tools/utils@^8.6.0": version "8.6.9" resolved "https://registry.npmjs.org/@graphql-tools/utils/-/utils-8.6.9.tgz#fe1b81df29c9418b41b7a1ffe731710b93d3a1fe" @@ -2419,24 +2546,24 @@ "@repeaterjs/repeater" "^3.0.4" tslib "^2.3.1" -"@grpc/grpc-js@~1.4.0": - version "1.4.6" - resolved "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.4.6.tgz#8108d7ab7c0c21b38c538c1a48583edbbf2c2412" - integrity sha512-Byau4xiXfIixb1PnW30V/P9mkrZ05lknyNqiK+cVY9J5hj3gecxd/anwaUbAM8j834zg1x78NvAbwGnMfWEu7A== +"@grpc/grpc-js@~1.6.0": + version "1.6.7" + resolved "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.6.7.tgz#4c4fa998ff719fe859ac19fe977fdef097bb99aa" + integrity sha512-eBM03pu9hd3VqDQG+kHahiG1x80RGkkqqRb1Pchcwqej/KkAH95gAvKs6laqaHCycYaPK+TKuNQnOz9UXYA8qw== dependencies: "@grpc/proto-loader" "^0.6.4" "@types/node" ">=12.12.47" -"@grpc/proto-loader@^0.6.1", "@grpc/proto-loader@^0.6.4": - version "0.6.7" - resolved "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.6.7.tgz#e62a202f4cf5897bdd0e244dec1dbc80d84bdfa1" - integrity sha512-QzTPIyJxU0u+r2qGe8VMl3j/W2ryhEvBv7hc42OjYfthSj370fUrb7na65rG6w3YLZS/fb8p89iTBobfWGDgdw== +"@grpc/proto-loader@^0.6.12", "@grpc/proto-loader@^0.6.4": + version "0.6.12" + resolved "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.6.12.tgz#459b619b8b9b67794bf0d1cb819653a38c63e164" + integrity sha512-filTVbETFnxb9CyRX98zN18ilChTuf/C5scZ2xyaOTp0EHGq0/ufX8rjqXUcSb1Gpv7eZq4M2jDvbh9BogKnrg== dependencies: "@types/long" "^4.0.1" lodash.camelcase "^4.3.0" long "^4.0.0" protobufjs "^6.10.0" - yargs "^16.1.1" + yargs "^16.2.0" "@hapi/hoek@^9.0.0": version "9.0.4" @@ -2493,6 +2620,20 @@ resolved "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz#b32366c89b43c6f8cefbdefac778b9c828e3ba8c" integrity sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg== +"@improbable-eng/grpc-web@^0.14.0": + version "0.14.1" + resolved "https://registry.npmjs.org/@improbable-eng/grpc-web/-/grpc-web-0.14.1.tgz#f4662f64dc89c0f956a94bb8a3b576556c74589c" + integrity sha512-XaIYuunepPxoiGVLLHmlnVminUGzBTnXr8Wv7khzmLWbNw4TCwJKX09GSMJlKhu/TRk6gms0ySFxewaETSBqgw== + dependencies: + browser-headers "^0.4.1" + +"@improbable-eng/grpc-web@^0.15.0": + version "0.15.0" + resolved "https://registry.npmjs.org/@improbable-eng/grpc-web/-/grpc-web-0.15.0.tgz#3e47e9fdd90381a74abd4b7d26e67422a2a04bef" + integrity sha512-ERft9/0/8CmYalqOVnJnpdDry28q+j+nAlFFARdjyxXDJ+Mhgv9+F600QC8BR9ygOfrXRlAk6CvST2j+JCpQPg== + dependencies: + browser-headers "^0.4.1" + "@ioredis/commands@^1.1.1": version "1.1.1" resolved "https://registry.npmjs.org/@ioredis/commands/-/commands-1.1.1.tgz#2ba4299ea624a6bfac15b35f6df90b0015691ec3" @@ -3035,14 +3176,22 @@ integrity sha512-CtzORUwWTTOTqfVtHaKRJ0I1kNQd1bpn3sUh8I3nJDVY+5/M/Oe1DnEWzPQvqq/xPIIkzzzIP7mfCoAjFRvDhg== "@jridgewell/resolve-uri@^3.0.3": - version "3.0.5" - resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz#68eb521368db76d040a6315cdb24bf2483037b9c" - integrity sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew== + version "3.0.7" + resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.7.tgz#30cd49820a962aff48c8fffc5cd760151fca61fe" + integrity sha512-8cXDaBBHOr2pQ7j77Y6Vp5VDT2sIqWyWQ56TjEq4ih/a4iST3dItRe8Q9fp0rrIl9DoKhWQtUQz/YpOxLkXbNA== "@jridgewell/sourcemap-codec@^1.4.10": - version "1.4.11" - resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz#771a1d8d744eeb71b6adb35808e1a6c7b9b8c8ec" - integrity sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg== + version "1.4.13" + resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.13.tgz#b6461fb0c2964356c469e115f504c95ad97ab88c" + integrity sha512-GryiOJmNcWbovBxTfZSF71V/mXbgcV3MewDe3kIMCLyIh5e7SKAeUZs+rMnJ8jkMolZ/4/VsdBmMrw3l+VdZ3w== + +"@jridgewell/trace-mapping@0.3.9": + version "0.3.9" + resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" + integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/trace-mapping@^0.3.0": version "0.3.4" @@ -3052,6 +3201,14 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" +"@jridgewell/trace-mapping@^0.3.7": + version "0.3.13" + resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.13.tgz#dcfe3e95f224c8fe97a87a5235defec999aa92ea" + integrity sha512-o1xbKhp9qnIAoHJSWd6KlCZfqslL4valSF81H8ImioOAxluWYWOpWkpyktY2vnt4tbrX9XYaxovq6cgowaJp2w== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jsdevtools/ono@^7.1.3": version "7.1.3" resolved "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz#9df03bbd7c696a5c58885c34aa06da41c8543796" @@ -3063,9 +3220,9 @@ integrity sha512-zMM9Ds+SawiUkakS7y94Ymqx+S0ORzpG3frZirN3l+UlXUmSUR7hF4wxCVqW+ei94JzV5kt0uXBcoOEAuiydrw== "@keyv/redis@^2.2.3": - version "2.3.5" - resolved "https://registry.npmjs.org/@keyv/redis/-/redis-2.3.5.tgz#8ff4fecf9b5520cac2a346e6216db59474df3385" - integrity sha512-dO9sn1HEPVkh1b7Cn/jcLyi7sDFKmnGQIaXw4wTIhJzkwcev24CffvEU26B+KRUNBu/2PgNVNRfFmEpy1smb4w== + version "2.3.6" + resolved "https://registry.npmjs.org/@keyv/redis/-/redis-2.3.6.tgz#462991af60b5b35af4e9ac8d05ad99a08ae2cfc2" + integrity sha512-MKRAeWaKI7zpGb+lSgAAlznQi725NEN18I/Uj2CJysk1o6hBqyXyxCvyI+b9qKwoFAkzxoogdvONIpstzI7t/Q== dependencies: ioredis "^5.0.4" @@ -3102,39 +3259,40 @@ resolved "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.3.tgz#0300943770e04231041a51bd39f0439b5c7ab4f0" integrity sha512-nkalE/f1RvRGChwBnEIoBfSEYOXnCRdleKuv6+lePbMDrMZXeDQnqak5XDOeBgrPPyPfAdcCu/B5z+v3VhplGg== -"@lerna/add@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/add/-/add-4.0.0.tgz#c36f57d132502a57b9e7058d1548b7a565ef183f" - integrity sha512-cpmAH1iS3k8JBxNvnMqrGTTjbY/ZAiKa1ChJzFevMYY3eeqbvhsBKnBcxjRXtdrJ6bd3dCQM+ZtK+0i682Fhng== +"@lerna/add@5.0.0": + version "5.0.0" + resolved "https://registry.npmjs.org/@lerna/add/-/add-5.0.0.tgz#0545e2eef157c142d82ba765467c27b36fe53ce8" + integrity sha512-KdIOQL+88iHU9zuAU8Be1AL4cOVmm77nlckylsNaVVTiomNipr/h7lStiBO52BoMkwKzNwOH6He5HGY0Yo7s2w== dependencies: - "@lerna/bootstrap" "4.0.0" - "@lerna/command" "4.0.0" - "@lerna/filter-options" "4.0.0" - "@lerna/npm-conf" "4.0.0" - "@lerna/validation-error" "4.0.0" + "@lerna/bootstrap" "5.0.0" + "@lerna/command" "5.0.0" + "@lerna/filter-options" "5.0.0" + "@lerna/npm-conf" "5.0.0" + "@lerna/validation-error" "5.0.0" dedent "^0.7.0" npm-package-arg "^8.1.0" p-map "^4.0.0" - pacote "^11.2.6" + pacote "^13.4.1" semver "^7.3.4" -"@lerna/bootstrap@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/bootstrap/-/bootstrap-4.0.0.tgz#5f5c5e2c6cfc8fcec50cb2fbe569a8c607101891" - integrity sha512-RkS7UbeM2vu+kJnHzxNRCLvoOP9yGNgkzRdy4UV2hNalD7EP41bLvRVOwRYQ7fhc2QcbhnKNdOBihYRL0LcKtw== +"@lerna/bootstrap@5.0.0": + version "5.0.0" + resolved "https://registry.npmjs.org/@lerna/bootstrap/-/bootstrap-5.0.0.tgz#624b67a4631c7455b98cfed4dbb2e38b27025a7a" + integrity sha512-2m1BxKbYwDABy+uE/Da3EQM61R58bI3YQ0o1rsFQq1u0ltL9CJxw1o0lMg84hwMsBb4D+kLIXLqetYlLVgbr0Q== dependencies: - "@lerna/command" "4.0.0" - "@lerna/filter-options" "4.0.0" - "@lerna/has-npm-version" "4.0.0" - "@lerna/npm-install" "4.0.0" - "@lerna/package-graph" "4.0.0" - "@lerna/pulse-till-done" "4.0.0" - "@lerna/rimraf-dir" "4.0.0" - "@lerna/run-lifecycle" "4.0.0" - "@lerna/run-topologically" "4.0.0" - "@lerna/symlink-binary" "4.0.0" - "@lerna/symlink-dependencies" "4.0.0" - "@lerna/validation-error" "4.0.0" + "@lerna/command" "5.0.0" + "@lerna/filter-options" "5.0.0" + "@lerna/has-npm-version" "5.0.0" + "@lerna/npm-install" "5.0.0" + "@lerna/package-graph" "5.0.0" + "@lerna/pulse-till-done" "5.0.0" + "@lerna/rimraf-dir" "5.0.0" + "@lerna/run-lifecycle" "5.0.0" + "@lerna/run-topologically" "5.0.0" + "@lerna/symlink-binary" "5.0.0" + "@lerna/symlink-dependencies" "5.0.0" + "@lerna/validation-error" "5.0.0" + "@npmcli/arborist" "5.2.0" dedent "^0.7.0" get-port "^5.1.1" multimatch "^5.0.0" @@ -3143,103 +3301,102 @@ p-map "^4.0.0" p-map-series "^2.1.0" p-waterfall "^2.1.1" - read-package-tree "^5.3.1" semver "^7.3.4" -"@lerna/changed@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/changed/-/changed-4.0.0.tgz#b9fc76cea39b9292a6cd263f03eb57af85c9270b" - integrity sha512-cD+KuPRp6qiPOD+BO6S6SN5cARspIaWSOqGBpGnYzLb4uWT8Vk4JzKyYtc8ym1DIwyoFXHosXt8+GDAgR8QrgQ== +"@lerna/changed@5.0.0": + version "5.0.0" + resolved "https://registry.npmjs.org/@lerna/changed/-/changed-5.0.0.tgz#fb3cdd5f281683a461c3099cbcf0978e23b33140" + integrity sha512-A24MHipPGODmzQBH1uIMPPUUOc1Zm7Qe/eSYzm52bFHtVxWH0nIVXfunadoMX32NhzKQH3Sw8X2rWHPQSRoUvA== dependencies: - "@lerna/collect-updates" "4.0.0" - "@lerna/command" "4.0.0" - "@lerna/listable" "4.0.0" - "@lerna/output" "4.0.0" + "@lerna/collect-updates" "5.0.0" + "@lerna/command" "5.0.0" + "@lerna/listable" "5.0.0" + "@lerna/output" "5.0.0" -"@lerna/check-working-tree@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/check-working-tree/-/check-working-tree-4.0.0.tgz#257e36a602c00142e76082a19358e3e1ae8dbd58" - integrity sha512-/++bxM43jYJCshBiKP5cRlCTwSJdRSxVmcDAXM+1oUewlZJVSVlnks5eO0uLxokVFvLhHlC5kHMc7gbVFPHv6Q== +"@lerna/check-working-tree@5.0.0": + version "5.0.0" + resolved "https://registry.npmjs.org/@lerna/check-working-tree/-/check-working-tree-5.0.0.tgz#e7b653b78c3bb96db7a00f6a74018e2bb88ec088" + integrity sha512-PnUMdpT2qS4o+vs+7l5fFIizstGdqSkhLG+Z9ZiY5OMtnGd+pmAFQFlbLSZSmdvQSOSobl9fhB1St8qhPD60xQ== dependencies: - "@lerna/collect-uncommitted" "4.0.0" - "@lerna/describe-ref" "4.0.0" - "@lerna/validation-error" "4.0.0" + "@lerna/collect-uncommitted" "5.0.0" + "@lerna/describe-ref" "5.0.0" + "@lerna/validation-error" "5.0.0" -"@lerna/child-process@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/child-process/-/child-process-4.0.0.tgz#341b96a57dffbd9705646d316e231df6fa4df6e1" - integrity sha512-XtCnmCT9eyVsUUHx6y/CTBYdV9g2Cr/VxyseTWBgfIur92/YKClfEtJTbOh94jRT62hlKLqSvux/UhxXVh613Q== +"@lerna/child-process@5.0.0": + version "5.0.0" + resolved "https://registry.npmjs.org/@lerna/child-process/-/child-process-5.0.0.tgz#1c7663d2910431f6c25543fd53998ae95b2dac19" + integrity sha512-cFVNkedrlU8XTt15EvUtQ84hqtV4oToQW/elKNv//mhCz06HY8Y+Ia6XevK2zrIhZjS6DT576F/7SmTk3vnpmg== dependencies: chalk "^4.1.0" execa "^5.0.0" strong-log-transformer "^2.1.0" -"@lerna/clean@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/clean/-/clean-4.0.0.tgz#8f778b6f2617aa2a936a6b5e085ae62498e57dc5" - integrity sha512-uugG2iN9k45ITx2jtd8nEOoAtca8hNlDCUM0N3lFgU/b1mEQYAPRkqr1qs4FLRl/Y50ZJ41wUz1eazS+d/0osA== +"@lerna/clean@5.0.0": + version "5.0.0" + resolved "https://registry.npmjs.org/@lerna/clean/-/clean-5.0.0.tgz#2b5cf202ab3eca18a075b292c55e6641d18b1b8f" + integrity sha512-7B+0Nx6MEPmCfnEa1JFyZwJsC7qlGrikWXyLglLb/wcbapYVsuDauOl9AT1iOFoXKw82P77HWYUKWeD9DQgw/w== dependencies: - "@lerna/command" "4.0.0" - "@lerna/filter-options" "4.0.0" - "@lerna/prompt" "4.0.0" - "@lerna/pulse-till-done" "4.0.0" - "@lerna/rimraf-dir" "4.0.0" + "@lerna/command" "5.0.0" + "@lerna/filter-options" "5.0.0" + "@lerna/prompt" "5.0.0" + "@lerna/pulse-till-done" "5.0.0" + "@lerna/rimraf-dir" "5.0.0" p-map "^4.0.0" p-map-series "^2.1.0" p-waterfall "^2.1.1" -"@lerna/cli@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/cli/-/cli-4.0.0.tgz#8eabd334558836c1664df23f19acb95e98b5bbf3" - integrity sha512-Neaw3GzFrwZiRZv2g7g6NwFjs3er1vhraIniEs0jjVLPMNC4eata0na3GfE5yibkM/9d3gZdmihhZdZ3EBdvYA== +"@lerna/cli@5.0.0": + version "5.0.0" + resolved "https://registry.npmjs.org/@lerna/cli/-/cli-5.0.0.tgz#f440f6664aa6c22bb58e69aacfde655c831de2f9" + integrity sha512-g8Nifko8XNySOl8u2molSHVl+fk/E1e5FSn/W2ekeijmc3ezktp+xbPWofNq71N/d297+KPQpLBfwzXSo9ufIQ== dependencies: - "@lerna/global-options" "4.0.0" + "@lerna/global-options" "5.0.0" dedent "^0.7.0" npmlog "^4.1.2" yargs "^16.2.0" -"@lerna/collect-uncommitted@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/collect-uncommitted/-/collect-uncommitted-4.0.0.tgz#855cd64612969371cfc2453b90593053ff1ba779" - integrity sha512-ufSTfHZzbx69YNj7KXQ3o66V4RC76ffOjwLX0q/ab//61bObJ41n03SiQEhSlmpP+gmFbTJ3/7pTe04AHX9m/g== +"@lerna/collect-uncommitted@5.0.0": + version "5.0.0" + resolved "https://registry.npmjs.org/@lerna/collect-uncommitted/-/collect-uncommitted-5.0.0.tgz#2843f98995c8bcc1d783d1d9739122c79378f3c5" + integrity sha512-mga/2S9rK0TP5UCulWiCTrC/uKaiIlOro1n8R3oCw6eRw9eupCSRx5zGI7pdh8CPD82MDL7w0a6OTep3WBSBVA== dependencies: - "@lerna/child-process" "4.0.0" + "@lerna/child-process" "5.0.0" chalk "^4.1.0" npmlog "^4.1.2" -"@lerna/collect-updates@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/collect-updates/-/collect-updates-4.0.0.tgz#8e208b1bafd98a372ff1177f7a5e288f6bea8041" - integrity sha512-bnNGpaj4zuxsEkyaCZLka9s7nMs58uZoxrRIPJ+nrmrZYp1V5rrd+7/NYTuunOhY2ug1sTBvTAxj3NZQ+JKnOw== +"@lerna/collect-updates@5.0.0": + version "5.0.0" + resolved "https://registry.npmjs.org/@lerna/collect-updates/-/collect-updates-5.0.0.tgz#cce16b9e8136e1e7bc33fe0fb12b283e538fa658" + integrity sha512-X82i8SVgBXLCk8vbKWfQPRLTAXROCANL8Z/bU1l6n7yycsHKdjrrlNi1+KprFdfRsMvSm10R4qPNcl9jgsp/IA== dependencies: - "@lerna/child-process" "4.0.0" - "@lerna/describe-ref" "4.0.0" + "@lerna/child-process" "5.0.0" + "@lerna/describe-ref" "5.0.0" minimatch "^3.0.4" npmlog "^4.1.2" slash "^3.0.0" -"@lerna/command@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/command/-/command-4.0.0.tgz#991c7971df8f5bf6ae6e42c808869a55361c1b98" - integrity sha512-LM9g3rt5FsPNFqIHUeRwWXLNHJ5NKzOwmVKZ8anSp4e1SPrv2HNc1V02/9QyDDZK/w+5POXH5lxZUI1CHaOK/A== +"@lerna/command@5.0.0": + version "5.0.0" + resolved "https://registry.npmjs.org/@lerna/command/-/command-5.0.0.tgz#cdc9f32a6b1c7153fe7150d642d2a420a3d0797d" + integrity sha512-j7/apU5d/nhSc1qIZgcV03KyO5jz3y7cwSum3IuK8/XF6rKwt3FVnbue1V3l9sJ6IRJjsRGKyViB1IdP5nSX4Q== dependencies: - "@lerna/child-process" "4.0.0" - "@lerna/package-graph" "4.0.0" - "@lerna/project" "4.0.0" - "@lerna/validation-error" "4.0.0" - "@lerna/write-log-file" "4.0.0" + "@lerna/child-process" "5.0.0" + "@lerna/package-graph" "5.0.0" + "@lerna/project" "5.0.0" + "@lerna/validation-error" "5.0.0" + "@lerna/write-log-file" "5.0.0" clone-deep "^4.0.1" dedent "^0.7.0" execa "^5.0.0" is-ci "^2.0.0" npmlog "^4.1.2" -"@lerna/conventional-commits@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/conventional-commits/-/conventional-commits-4.0.0.tgz#660fb2c7b718cb942ead70110df61f18c6f99750" - integrity sha512-CSUQRjJHFrH8eBn7+wegZLV3OrNc0Y1FehYfYGhjLE2SIfpCL4bmfu/ViYuHh9YjwHaA+4SX6d3hR+xkeseKmw== +"@lerna/conventional-commits@5.0.0": + version "5.0.0" + resolved "https://registry.npmjs.org/@lerna/conventional-commits/-/conventional-commits-5.0.0.tgz#7f9c16fda074c9ed897cb695f5ae23678dd441eb" + integrity sha512-tUCRTAycDCtSlCEI0hublq4uKHeV0UHpwIb3Fdt6iv2AoTSPBSX/Dwu/6VqguysOSEkkR4M2JCOLvJCl4IMxwg== dependencies: - "@lerna/validation-error" "4.0.0" + "@lerna/validation-error" "5.0.0" conventional-changelog-angular "^5.0.12" conventional-changelog-core "^4.2.2" conventional-recommended-bump "^6.1.0" @@ -3251,31 +3408,31 @@ pify "^5.0.0" semver "^7.3.4" -"@lerna/create-symlink@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/create-symlink/-/create-symlink-4.0.0.tgz#8c5317ce5ae89f67825443bd7651bf4121786228" - integrity sha512-I0phtKJJdafUiDwm7BBlEUOtogmu8+taxq6PtIrxZbllV9hWg59qkpuIsiFp+no7nfRVuaasNYHwNUhDAVQBig== +"@lerna/create-symlink@5.0.0": + version "5.0.0" + resolved "https://registry.npmjs.org/@lerna/create-symlink/-/create-symlink-5.0.0.tgz#eccef7f89fdc4d7cd904694d9e2eb0b582073b5e" + integrity sha512-nHYNacrh15Y0yEofVlUVu9dhf4JjIn9hY7v7rOUXzUeQ91iXY5Q3PVHkBeRUigyT5CWP5qozZwraCMwp+lDWYg== dependencies: cmd-shim "^4.1.0" fs-extra "^9.1.0" npmlog "^4.1.2" -"@lerna/create@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/create/-/create-4.0.0.tgz#b6947e9b5dfb6530321952998948c3e63d64d730" - integrity sha512-mVOB1niKByEUfxlbKTM1UNECWAjwUdiioIbRQZEeEabtjCL69r9rscIsjlGyhGWCfsdAG5wfq4t47nlDXdLLag== +"@lerna/create@5.0.0": + version "5.0.0" + resolved "https://registry.npmjs.org/@lerna/create/-/create-5.0.0.tgz#4aac3d1f2c1f6d7fadde49d3663b318fcdd39b06" + integrity sha512-sdFTVTLOVuhHpzIYhFAwK0Ry3p4d7uMe9ZG/Ii128/pB9kEEfCth+1WBq6mBpYZ5mOLLgxJbWalbiJFl0toQRw== dependencies: - "@lerna/child-process" "4.0.0" - "@lerna/command" "4.0.0" - "@lerna/npm-conf" "4.0.0" - "@lerna/validation-error" "4.0.0" + "@lerna/child-process" "5.0.0" + "@lerna/command" "5.0.0" + "@lerna/npm-conf" "5.0.0" + "@lerna/validation-error" "5.0.0" dedent "^0.7.0" fs-extra "^9.1.0" globby "^11.0.2" init-package-json "^2.0.2" npm-package-arg "^8.1.0" p-reduce "^2.1.0" - pacote "^11.2.6" + pacote "^13.4.1" pify "^5.0.0" semver "^7.3.4" slash "^3.0.0" @@ -3284,217 +3441,217 @@ whatwg-url "^8.4.0" yargs-parser "20.2.4" -"@lerna/describe-ref@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/describe-ref/-/describe-ref-4.0.0.tgz#53c53b4ea65fdceffa072a62bfebe6772c45d9ec" - integrity sha512-eTU5+xC4C5Gcgz+Ey4Qiw9nV2B4JJbMulsYJMW8QjGcGh8zudib7Sduj6urgZXUYNyhYpRs+teci9M2J8u+UvQ== +"@lerna/describe-ref@5.0.0": + version "5.0.0" + resolved "https://registry.npmjs.org/@lerna/describe-ref/-/describe-ref-5.0.0.tgz#f0676843642e8880133783a9f059e6cb4c027fe1" + integrity sha512-iLvMHp3nl4wcMR3/lVkz0ng7pAHfLQ7yvz2HsYBq7wllCcEzpchzPgyVzyvbpJ+Ke/MKjQTsrHE/yOGOH67GVw== dependencies: - "@lerna/child-process" "4.0.0" + "@lerna/child-process" "5.0.0" npmlog "^4.1.2" -"@lerna/diff@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/diff/-/diff-4.0.0.tgz#6d3071817aaa4205a07bf77cfc6e932796d48b92" - integrity sha512-jYPKprQVg41+MUMxx6cwtqsNm0Yxx9GDEwdiPLwcUTFx+/qKCEwifKNJ1oGIPBxyEHX2PFCOjkK39lHoj2qiag== +"@lerna/diff@5.0.0": + version "5.0.0" + resolved "https://registry.npmjs.org/@lerna/diff/-/diff-5.0.0.tgz#844333f5478fc4993c4389fee1e0cd8eff9114fe" + integrity sha512-S4XJ6i9oP77cSmJ3oRUJGMgrI+jOTmkYWur2nqgSdyJBE1J2eClgTJknb3WAHg2cHALT18WzFqNghFOGM+9dRA== dependencies: - "@lerna/child-process" "4.0.0" - "@lerna/command" "4.0.0" - "@lerna/validation-error" "4.0.0" + "@lerna/child-process" "5.0.0" + "@lerna/command" "5.0.0" + "@lerna/validation-error" "5.0.0" npmlog "^4.1.2" -"@lerna/exec@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/exec/-/exec-4.0.0.tgz#eb6cb95cb92d42590e9e2d628fcaf4719d4a8be6" - integrity sha512-VGXtL/b/JfY84NB98VWZpIExfhLOzy0ozm/0XaS4a2SmkAJc5CeUfrhvHxxkxiTBLkU+iVQUyYEoAT0ulQ8PCw== +"@lerna/exec@5.0.0": + version "5.0.0" + resolved "https://registry.npmjs.org/@lerna/exec/-/exec-5.0.0.tgz#a59dd094e456ea46cfa8f713da0ea3334a7ec9ac" + integrity sha512-g5i+2RclCGWLsl88m11j99YM2Gqnwa2lxZ5tDeqqWZFno6Dlvop17Yl6/MFH42EgM2DQHUUCammvcLIAJ2XwEA== dependencies: - "@lerna/child-process" "4.0.0" - "@lerna/command" "4.0.0" - "@lerna/filter-options" "4.0.0" - "@lerna/profiler" "4.0.0" - "@lerna/run-topologically" "4.0.0" - "@lerna/validation-error" "4.0.0" + "@lerna/child-process" "5.0.0" + "@lerna/command" "5.0.0" + "@lerna/filter-options" "5.0.0" + "@lerna/profiler" "5.0.0" + "@lerna/run-topologically" "5.0.0" + "@lerna/validation-error" "5.0.0" p-map "^4.0.0" -"@lerna/filter-options@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/filter-options/-/filter-options-4.0.0.tgz#ac94cc515d7fa3b47e2f7d74deddeabb1de5e9e6" - integrity sha512-vV2ANOeZhOqM0rzXnYcFFCJ/kBWy/3OA58irXih9AMTAlQLymWAK0akWybl++sUJ4HB9Hx12TOqaXbYS2NM5uw== +"@lerna/filter-options@5.0.0": + version "5.0.0" + resolved "https://registry.npmjs.org/@lerna/filter-options/-/filter-options-5.0.0.tgz#1d2606e1d2ed106689b43cc5d41a77b239afb837" + integrity sha512-un73aYkXlzKlnDPx2AlqNW+ArCZ20XaX+Y6C0F+av9VZriiBsCgZTnflhih9fiSMnXjN5r9CA8YdWvZqa3oAcQ== dependencies: - "@lerna/collect-updates" "4.0.0" - "@lerna/filter-packages" "4.0.0" + "@lerna/collect-updates" "5.0.0" + "@lerna/filter-packages" "5.0.0" dedent "^0.7.0" npmlog "^4.1.2" -"@lerna/filter-packages@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/filter-packages/-/filter-packages-4.0.0.tgz#b1f70d70e1de9cdd36a4e50caa0ac501f8d012f2" - integrity sha512-+4AJIkK7iIiOaqCiVTYJxh/I9qikk4XjNQLhE3kixaqgMuHl1NQ99qXRR0OZqAWB9mh8Z1HA9bM5K1HZLBTOqA== +"@lerna/filter-packages@5.0.0": + version "5.0.0" + resolved "https://registry.npmjs.org/@lerna/filter-packages/-/filter-packages-5.0.0.tgz#9aae543ab5e45a1b0c3f7ad33e0686ceb8d92c88" + integrity sha512-+EIjVVaMPDZ05F/gZa+kcXjBOLXqEamcEIDr+2ZXRgJmnrLx9BBY1B7sBEFHg7JXbeOKS+fKtMGVveV0SzgH3Q== dependencies: - "@lerna/validation-error" "4.0.0" + "@lerna/validation-error" "5.0.0" multimatch "^5.0.0" npmlog "^4.1.2" -"@lerna/get-npm-exec-opts@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/get-npm-exec-opts/-/get-npm-exec-opts-4.0.0.tgz#dc955be94a4ae75c374ef9bce91320887d34608f" - integrity sha512-yvmkerU31CTWS2c7DvmAWmZVeclPBqI7gPVr5VATUKNWJ/zmVcU4PqbYoLu92I9Qc4gY1TuUplMNdNuZTSL7IQ== +"@lerna/get-npm-exec-opts@5.0.0": + version "5.0.0" + resolved "https://registry.npmjs.org/@lerna/get-npm-exec-opts/-/get-npm-exec-opts-5.0.0.tgz#25c1cd7d2b6c1fe903cd144d9f6e2d5cae47429b" + integrity sha512-ZOg3kc5FXYA1kVFD2hfJOl64hNASWD6panwD0HlyzXgfKKTDRm/P/qtAqS8WGCzQWgEdx4wvsDe/58Lzzh6QzQ== dependencies: npmlog "^4.1.2" -"@lerna/get-packed@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/get-packed/-/get-packed-4.0.0.tgz#0989d61624ac1f97e393bdad2137c49cd7a37823" - integrity sha512-rfWONRsEIGyPJTxFzC8ECb3ZbsDXJbfqWYyeeQQDrJRPnEJErlltRLPLgC2QWbxFgFPsoDLeQmFHJnf0iDfd8w== +"@lerna/get-packed@5.0.0": + version "5.0.0" + resolved "https://registry.npmjs.org/@lerna/get-packed/-/get-packed-5.0.0.tgz#4de7f66184232c805dfca07b9a8c577f6ef02351" + integrity sha512-fks7Tg7DvcCZxRWPS3JAWVuLnwjPC/hLlNsdYmK9nN3+RtPhmYQgBjLSONcENw1E46t4Aph72lA9nLcYBLksqw== dependencies: fs-extra "^9.1.0" ssri "^8.0.1" tar "^6.1.0" -"@lerna/github-client@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/github-client/-/github-client-4.0.0.tgz#2ced67721363ef70f8e12ffafce4410918f4a8a4" - integrity sha512-2jhsldZtTKXYUBnOm23Lb0Fx8G4qfSXF9y7UpyUgWUj+YZYd+cFxSuorwQIgk5P4XXrtVhsUesIsli+BYSThiw== +"@lerna/github-client@5.0.0": + version "5.0.0" + resolved "https://registry.npmjs.org/@lerna/github-client/-/github-client-5.0.0.tgz#65c984a393b1cbe35c2a707059c645bb9a03395e" + integrity sha512-NoEyRkQ8XgBnrjRfC9ph1npfg1/4OdYG+r8lG/1WkJbdt1Wlym4VNZU2BYPMWwSQYMJuppoEr0LL2uuVcS4ZUw== dependencies: - "@lerna/child-process" "4.0.0" + "@lerna/child-process" "5.0.0" "@octokit/plugin-enterprise-rest" "^6.0.1" "@octokit/rest" "^18.1.0" git-url-parse "^11.4.4" npmlog "^4.1.2" -"@lerna/gitlab-client@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/gitlab-client/-/gitlab-client-4.0.0.tgz#00dad73379c7b38951d4b4ded043504c14e2b67d" - integrity sha512-OMUpGSkeDWFf7BxGHlkbb35T7YHqVFCwBPSIR6wRsszY8PAzCYahtH3IaJzEJyUg6vmZsNl0FSr3pdA2skhxqA== +"@lerna/gitlab-client@5.0.0": + version "5.0.0" + resolved "https://registry.npmjs.org/@lerna/gitlab-client/-/gitlab-client-5.0.0.tgz#c4e3d16566a3b07908ee604ce681a09c418481de" + integrity sha512-WREAT7qzta9hxNxktTX0x1/sEMpBP+4Gc00QSJYXt+ZzxY0t5RUx/ZK5pQl+IDhtkajrvXT6fSfZjMxxyE8hhQ== dependencies: node-fetch "^2.6.1" npmlog "^4.1.2" whatwg-url "^8.4.0" -"@lerna/global-options@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/global-options/-/global-options-4.0.0.tgz#c7d8b0de6a01d8a845e2621ea89e7f60f18c6a5f" - integrity sha512-TRMR8afAHxuYBHK7F++Ogop2a82xQjoGna1dvPOY6ltj/pEx59pdgcJfYcynYqMkFIk8bhLJJN9/ndIfX29FTQ== +"@lerna/global-options@5.0.0": + version "5.0.0" + resolved "https://registry.npmjs.org/@lerna/global-options/-/global-options-5.0.0.tgz#02505c9e468188e3a254c262d58739092de93d8d" + integrity sha512-PZYy/3mTZwtA9lNmHHRCc/Ty1W20qGJ/BdDIo4bw/Bk0AOcoBCLT9b3Mjijkl4AbC9+eSGk3flUYapCGVuS32Q== -"@lerna/has-npm-version@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/has-npm-version/-/has-npm-version-4.0.0.tgz#d3fc3292c545eb28bd493b36e6237cf0279f631c" - integrity sha512-LQ3U6XFH8ZmLCsvsgq1zNDqka0Xzjq5ibVN+igAI5ccRWNaUsE/OcmsyMr50xAtNQMYMzmpw5GVLAivT2/YzCg== +"@lerna/has-npm-version@5.0.0": + version "5.0.0" + resolved "https://registry.npmjs.org/@lerna/has-npm-version/-/has-npm-version-5.0.0.tgz#ed62c6ef857f068209663aae9f156f06a93dc1bd" + integrity sha512-zJPgcml86nhJFJTpT+kjkcafuCFvK7PSq3oDC2KJxwB1bhlYwy+SKtAEypHSsHQ2DwP0YgPITcy1pvtHkie1SA== dependencies: - "@lerna/child-process" "4.0.0" + "@lerna/child-process" "5.0.0" semver "^7.3.4" -"@lerna/import@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/import/-/import-4.0.0.tgz#bde656c4a451fa87ae41733ff8a8da60547c5465" - integrity sha512-FaIhd+4aiBousKNqC7TX1Uhe97eNKf5/SC7c5WZANVWtC7aBWdmswwDt3usrzCNpj6/Wwr9EtEbYROzxKH8ffg== +"@lerna/import@5.0.0": + version "5.0.0" + resolved "https://registry.npmjs.org/@lerna/import/-/import-5.0.0.tgz#11cd83ef0fe854c512146fd4165f33519364b97a" + integrity sha512-cD+Is7eV/I+ZU0Wlg+yAgKaZbOvfzA7kBj2Qu1HtxeLhc7joTR8PFW1gNjEsvrWOTiaHAtObbo1A+MKYQ/T12g== dependencies: - "@lerna/child-process" "4.0.0" - "@lerna/command" "4.0.0" - "@lerna/prompt" "4.0.0" - "@lerna/pulse-till-done" "4.0.0" - "@lerna/validation-error" "4.0.0" + "@lerna/child-process" "5.0.0" + "@lerna/command" "5.0.0" + "@lerna/prompt" "5.0.0" + "@lerna/pulse-till-done" "5.0.0" + "@lerna/validation-error" "5.0.0" dedent "^0.7.0" fs-extra "^9.1.0" p-map-series "^2.1.0" -"@lerna/info@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/info/-/info-4.0.0.tgz#b9fb0e479d60efe1623603958a831a88b1d7f1fc" - integrity sha512-8Uboa12kaCSZEn4XRfPz5KU9XXoexSPS4oeYGj76s2UQb1O1GdnEyfjyNWoUl1KlJ2i/8nxUskpXIftoFYH0/Q== +"@lerna/info@5.0.0": + version "5.0.0" + resolved "https://registry.npmjs.org/@lerna/info/-/info-5.0.0.tgz#649566474d0d133c22bb821f88e7d062a2beace5" + integrity sha512-k9TMK81apTjxxpnjfFOABKXndTtHBPgB8UO+I6zKhsfRqVb9FCz2MHOx8cQiSyolvNyGSQdSylSo4p7EBBomQQ== dependencies: - "@lerna/command" "4.0.0" - "@lerna/output" "4.0.0" + "@lerna/command" "5.0.0" + "@lerna/output" "5.0.0" envinfo "^7.7.4" -"@lerna/init@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/init/-/init-4.0.0.tgz#dadff67e6dfb981e8ccbe0e6a310e837962f6c7a" - integrity sha512-wY6kygop0BCXupzWj5eLvTUqdR7vIAm0OgyV9WHpMYQGfs1V22jhztt8mtjCloD/O0nEe4tJhdG62XU5aYmPNQ== +"@lerna/init@5.0.0": + version "5.0.0" + resolved "https://registry.npmjs.org/@lerna/init/-/init-5.0.0.tgz#e35d95a4882aafb4600abf9b32fd1a0056e73ed9" + integrity sha512-2n68x7AIqVa+Vev9xF3NV9ba0C599KYf7JsIrQ5ESv4593ftInJpwgMwjroLT3X/Chi4BK7y2/xGmrfFVwgILg== dependencies: - "@lerna/child-process" "4.0.0" - "@lerna/command" "4.0.0" + "@lerna/child-process" "5.0.0" + "@lerna/command" "5.0.0" fs-extra "^9.1.0" p-map "^4.0.0" write-json-file "^4.3.0" -"@lerna/link@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/link/-/link-4.0.0.tgz#c3a38aabd44279d714e90f2451e31b63f0fb65ba" - integrity sha512-KlvPi7XTAcVOByfaLlOeYOfkkDcd+bejpHMCd1KcArcFTwijOwXOVi24DYomIeHvy6HsX/IUquJ4PPUJIeB4+w== +"@lerna/link@5.0.0": + version "5.0.0" + resolved "https://registry.npmjs.org/@lerna/link/-/link-5.0.0.tgz#dbd5aefa0bb22f2fd9d61ee82009fb34eb946298" + integrity sha512-00YxQ06TVhQJthOjcuxCCJRjkAM+qM/8Lv0ckdCzBBCSr4RdAGBp6QcAX/gjLNasgmNpyiza3ADet7mCH7uodw== dependencies: - "@lerna/command" "4.0.0" - "@lerna/package-graph" "4.0.0" - "@lerna/symlink-dependencies" "4.0.0" + "@lerna/command" "5.0.0" + "@lerna/package-graph" "5.0.0" + "@lerna/symlink-dependencies" "5.0.0" p-map "^4.0.0" slash "^3.0.0" -"@lerna/list@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/list/-/list-4.0.0.tgz#24b4e6995bd73f81c556793fe502b847efd9d1d7" - integrity sha512-L2B5m3P+U4Bif5PultR4TI+KtW+SArwq1i75QZ78mRYxPc0U/piau1DbLOmwrdqr99wzM49t0Dlvl6twd7GHFg== +"@lerna/list@5.0.0": + version "5.0.0" + resolved "https://registry.npmjs.org/@lerna/list/-/list-5.0.0.tgz#0a979dc9c24ca176c7b4b58de80cab2dac2dcb8a" + integrity sha512-+B0yFil2AFdiYO8hyU1bFbKXGBAUUQQ43/fp2XS2jBFCipLme4eTILL5gMKOhr2Xg9AsfYPXRMRer5VW7qTeeQ== dependencies: - "@lerna/command" "4.0.0" - "@lerna/filter-options" "4.0.0" - "@lerna/listable" "4.0.0" - "@lerna/output" "4.0.0" + "@lerna/command" "5.0.0" + "@lerna/filter-options" "5.0.0" + "@lerna/listable" "5.0.0" + "@lerna/output" "5.0.0" -"@lerna/listable@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/listable/-/listable-4.0.0.tgz#d00d6cb4809b403f2b0374fc521a78e318b01214" - integrity sha512-/rPOSDKsOHs5/PBLINZOkRIX1joOXUXEtyUs5DHLM8q6/RP668x/1lFhw6Dx7/U+L0+tbkpGtZ1Yt0LewCLgeQ== +"@lerna/listable@5.0.0": + version "5.0.0" + resolved "https://registry.npmjs.org/@lerna/listable/-/listable-5.0.0.tgz#c1753d9375932b15c4c84cc767fffb3447b8f213" + integrity sha512-Rd5sE7KTbqA8u048qThH5IyBuJIwMcUnEObjFyJyKpc1SEWSumo4yAYmcEeN/9z62tcdud5wHYPSbVgfXJq37g== dependencies: - "@lerna/query-graph" "4.0.0" + "@lerna/query-graph" "5.0.0" chalk "^4.1.0" columnify "^1.5.4" -"@lerna/log-packed@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/log-packed/-/log-packed-4.0.0.tgz#95168fe2e26ac6a71e42f4be857519b77e57a09f" - integrity sha512-+dpCiWbdzgMAtpajLToy9PO713IHoE6GV/aizXycAyA07QlqnkpaBNZ8DW84gHdM1j79TWockGJo9PybVhrrZQ== +"@lerna/log-packed@5.0.0": + version "5.0.0" + resolved "https://registry.npmjs.org/@lerna/log-packed/-/log-packed-5.0.0.tgz#afa35bb6a5736038d7dde039e09828ac1c4945a2" + integrity sha512-0TxKX+XnlEYj0du9U2kg3HEyIb/0QsM0Slt8utuCxALUnXRHTEKohjqVKsBdvh1QmJpnUbL5I+vfoYqno4Y42w== dependencies: byte-size "^7.0.0" columnify "^1.5.4" has-unicode "^2.0.1" npmlog "^4.1.2" -"@lerna/npm-conf@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/npm-conf/-/npm-conf-4.0.0.tgz#b259fd1e1cee2bf5402b236e770140ff9ade7fd2" - integrity sha512-uS7H02yQNq3oejgjxAxqq/jhwGEE0W0ntr8vM3EfpCW1F/wZruwQw+7bleJQ9vUBjmdXST//tk8mXzr5+JXCfw== +"@lerna/npm-conf@5.0.0": + version "5.0.0" + resolved "https://registry.npmjs.org/@lerna/npm-conf/-/npm-conf-5.0.0.tgz#1364270d231d0df5ac079a9a9733ba0dd7f8c2f9" + integrity sha512-KSftxtMNVhLol1JNwFFNgh5jiCG010pewM+uKeSrUe0BCB3lnidiEDzu2CCn8JYYfIXqAiou/pScUiOxVLpcAA== dependencies: config-chain "^1.1.12" pify "^5.0.0" -"@lerna/npm-dist-tag@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/npm-dist-tag/-/npm-dist-tag-4.0.0.tgz#d1e99b4eccd3414142f0548ad331bf2d53f3257a" - integrity sha512-F20sg28FMYTgXqEQihgoqSfwmq+Id3zT23CnOwD+XQMPSy9IzyLf1fFVH319vXIw6NF6Pgs4JZN2Qty6/CQXGw== +"@lerna/npm-dist-tag@5.0.0": + version "5.0.0" + resolved "https://registry.npmjs.org/@lerna/npm-dist-tag/-/npm-dist-tag-5.0.0.tgz#becd7fb0bd963357818c8d4fae955cc9f8885cba" + integrity sha512-ccUFhp9Wu/FHW5/5fL+vLiSTcUZXtKQ7c0RMXtNRzIdTXBxPBkVi1k5QAnBAAffsz6Owc/K++cb+/zQ/asrG3g== dependencies: - "@lerna/otplease" "4.0.0" + "@lerna/otplease" "5.0.0" npm-package-arg "^8.1.0" npm-registry-fetch "^9.0.0" npmlog "^4.1.2" -"@lerna/npm-install@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/npm-install/-/npm-install-4.0.0.tgz#31180be3ab3b7d1818a1a0c206aec156b7094c78" - integrity sha512-aKNxq2j3bCH3eXl3Fmu4D54s/YLL9WSwV8W7X2O25r98wzrO38AUN6AB9EtmAx+LV/SP15et7Yueg9vSaanRWg== +"@lerna/npm-install@5.0.0": + version "5.0.0" + resolved "https://registry.npmjs.org/@lerna/npm-install/-/npm-install-5.0.0.tgz#0ee1750bb26eae3c2b4d742d5c1f055e46d534df" + integrity sha512-72Jf05JCIdeSBWXAiNjd/y2AQH4Ojgas55ojV2sAcEYz2wgyR7wSpiI6fHBRlRP+3XPjV9MXKxI3ZwOnznQxqQ== dependencies: - "@lerna/child-process" "4.0.0" - "@lerna/get-npm-exec-opts" "4.0.0" + "@lerna/child-process" "5.0.0" + "@lerna/get-npm-exec-opts" "5.0.0" fs-extra "^9.1.0" npm-package-arg "^8.1.0" npmlog "^4.1.2" signal-exit "^3.0.3" write-pkg "^4.0.0" -"@lerna/npm-publish@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/npm-publish/-/npm-publish-4.0.0.tgz#84eb62e876fe949ae1fd62c60804423dbc2c4472" - integrity sha512-vQb7yAPRo5G5r77DRjHITc9piR9gvEKWrmfCH7wkfBnGWEqu7n8/4bFQ7lhnkujvc8RXOsYpvbMQkNfkYibD/w== +"@lerna/npm-publish@5.0.0": + version "5.0.0" + resolved "https://registry.npmjs.org/@lerna/npm-publish/-/npm-publish-5.0.0.tgz#a1a06e47e45e56999c85086a40f9b77f801b5a00" + integrity sha512-jnapZ2jRajSzshSfd1Y3rHH5R7QC+JJlYST04FBebIH3VePwDT7uAglDCI4um2THvxkW4420EzE4BUMUwKlnXA== dependencies: - "@lerna/otplease" "4.0.0" - "@lerna/run-lifecycle" "4.0.0" + "@lerna/otplease" "5.0.0" + "@lerna/run-lifecycle" "5.0.0" fs-extra "^9.1.0" libnpmpublish "^4.0.0" npm-package-arg "^8.1.0" @@ -3502,85 +3659,85 @@ pify "^5.0.0" read-package-json "^3.0.0" -"@lerna/npm-run-script@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/npm-run-script/-/npm-run-script-4.0.0.tgz#dfebf4f4601442e7c0b5214f9fb0d96c9350743b" - integrity sha512-Jmyh9/IwXJjOXqKfIgtxi0bxi1pUeKe5bD3S81tkcy+kyng/GNj9WSqD5ZggoNP2NP//s4CLDAtUYLdP7CU9rA== +"@lerna/npm-run-script@5.0.0": + version "5.0.0" + resolved "https://registry.npmjs.org/@lerna/npm-run-script/-/npm-run-script-5.0.0.tgz#114374b89f228c9719bbfacf9f08d6aac2739fb2" + integrity sha512-qgGf0Wc/E2YxPwIiF8kC/OB9ffPf0/HVtPVkqrblVuNE9XVP80WilOH966PIDiXzwXaCo/cTswFoBeseccYRGw== dependencies: - "@lerna/child-process" "4.0.0" - "@lerna/get-npm-exec-opts" "4.0.0" + "@lerna/child-process" "5.0.0" + "@lerna/get-npm-exec-opts" "5.0.0" npmlog "^4.1.2" -"@lerna/otplease@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/otplease/-/otplease-4.0.0.tgz#84972eb43448f8a1077435ba1c5e59233b725850" - integrity sha512-Sgzbqdk1GH4psNiT6hk+BhjOfIr/5KhGBk86CEfHNJTk9BK4aZYyJD4lpDbDdMjIV4g03G7pYoqHzH765T4fxw== +"@lerna/otplease@5.0.0": + version "5.0.0" + resolved "https://registry.npmjs.org/@lerna/otplease/-/otplease-5.0.0.tgz#5b0419f64908d7ad840c2735e0284d67cd37095b" + integrity sha512-QLLkEy1DPN1XFRAAZDHxAD26MHFQDHfzB6KKSzRYxbHc6lH/YbDaMH1RloSWIm7Hwkxl/3NgpokgN4Lj5XFuzg== dependencies: - "@lerna/prompt" "4.0.0" + "@lerna/prompt" "5.0.0" -"@lerna/output@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/output/-/output-4.0.0.tgz#b1d72215c0e35483e4f3e9994debc82c621851f2" - integrity sha512-Un1sHtO1AD7buDQrpnaYTi2EG6sLF+KOPEAMxeUYG5qG3khTs2Zgzq5WE3dt2N/bKh7naESt20JjIW6tBELP0w== +"@lerna/output@5.0.0": + version "5.0.0" + resolved "https://registry.npmjs.org/@lerna/output/-/output-5.0.0.tgz#f3712f0cad3e9ef73c803fe368f6a9ac20403868" + integrity sha512-/7sUJQWPcvnLudjVIdN7t9MlfBLuP4JCDAWgQMqZe+wpQRuKNyKQ5dLBH5NHU/ElJCjAwMPfWuk3mh3GuvuiGA== dependencies: npmlog "^4.1.2" -"@lerna/pack-directory@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/pack-directory/-/pack-directory-4.0.0.tgz#8b617db95d20792f043aaaa13a9ccc0e04cb4c74" - integrity sha512-NJrmZNmBHS+5aM+T8N6FVbaKFScVqKlQFJNY2k7nsJ/uklNKsLLl6VhTQBPwMTbf6Tf7l6bcKzpy7aePuq9UiQ== +"@lerna/pack-directory@5.0.0": + version "5.0.0" + resolved "https://registry.npmjs.org/@lerna/pack-directory/-/pack-directory-5.0.0.tgz#f277418545786ca68ca15647bab52ad29bd57f59" + integrity sha512-E1SNDS7xSWhJrTSmRzJK7DibneljrymviKcsZW3mRl4TmF4CpYJmNXCMlhEtKEy6ghnGQvnl3/4+eslHDJ5J/w== dependencies: - "@lerna/get-packed" "4.0.0" - "@lerna/package" "4.0.0" - "@lerna/run-lifecycle" "4.0.0" + "@lerna/get-packed" "5.0.0" + "@lerna/package" "5.0.0" + "@lerna/run-lifecycle" "5.0.0" + "@lerna/temp-write" "5.0.0" npm-packlist "^2.1.4" npmlog "^4.1.2" tar "^6.1.0" - temp-write "^4.0.0" -"@lerna/package-graph@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/package-graph/-/package-graph-4.0.0.tgz#16a00253a8ac810f72041481cb46bcee8d8123dd" - integrity sha512-QED2ZCTkfXMKFoTGoccwUzjHtZMSf3UKX14A4/kYyBms9xfFsesCZ6SLI5YeySEgcul8iuIWfQFZqRw+Qrjraw== +"@lerna/package-graph@5.0.0": + version "5.0.0" + resolved "https://registry.npmjs.org/@lerna/package-graph/-/package-graph-5.0.0.tgz#53e88ef46359ef7a2f6e3b7c5bab82302a10653f" + integrity sha512-Z3QeUQVjux0Blo64rA3/NivoLDlsQBjsZRIgGLbcQh7l7pJrqLK1WyNCBbPJ0KQNljQqUXthCKzdefnEWe37Ew== dependencies: - "@lerna/prerelease-id-from-version" "4.0.0" - "@lerna/validation-error" "4.0.0" + "@lerna/prerelease-id-from-version" "5.0.0" + "@lerna/validation-error" "5.0.0" npm-package-arg "^8.1.0" npmlog "^4.1.2" semver "^7.3.4" -"@lerna/package@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/package/-/package-4.0.0.tgz#1b4c259c4bcff45c876ee1d591a043aacbc0d6b7" - integrity sha512-l0M/izok6FlyyitxiQKr+gZLVFnvxRQdNhzmQ6nRnN9dvBJWn+IxxpM+cLqGACatTnyo9LDzNTOj2Db3+s0s8Q== +"@lerna/package@5.0.0": + version "5.0.0" + resolved "https://registry.npmjs.org/@lerna/package/-/package-5.0.0.tgz#4beeb3a1e8eed6e7ae9cebca283c7684278cdd28" + integrity sha512-/JiUU88bhbYEUTzPqoGLGwrrdWWTIVMlBb1OPxCGNGDEqYYNySX+OTTSs3zGMcmJnRNI0UyQALiEd0sh3JFN5w== dependencies: load-json-file "^6.2.0" npm-package-arg "^8.1.0" write-pkg "^4.0.0" -"@lerna/prerelease-id-from-version@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/prerelease-id-from-version/-/prerelease-id-from-version-4.0.0.tgz#c7e0676fcee1950d85630e108eddecdd5b48c916" - integrity sha512-GQqguzETdsYRxOSmdFZ6zDBXDErIETWOqomLERRY54f4p+tk4aJjoVdd9xKwehC9TBfIFvlRbL1V9uQGHh1opg== +"@lerna/prerelease-id-from-version@5.0.0": + version "5.0.0" + resolved "https://registry.npmjs.org/@lerna/prerelease-id-from-version/-/prerelease-id-from-version-5.0.0.tgz#3edb90ba9ceace97708d03ff9f650d177f973184" + integrity sha512-bUZwyx6evRn2RxogOQXaiYxRK1U/1Mh/KLO4n49wUhqb8S8Vb9aG3+7lLOgg4ZugHpj9KAlD3YGEKvwYQiWzhg== dependencies: semver "^7.3.4" -"@lerna/profiler@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/profiler/-/profiler-4.0.0.tgz#8a53ab874522eae15d178402bff90a14071908e9" - integrity sha512-/BaEbqnVh1LgW/+qz8wCuI+obzi5/vRE8nlhjPzdEzdmWmZXuCKyWSEzAyHOJWw1ntwMiww5dZHhFQABuoFz9Q== +"@lerna/profiler@5.0.0": + version "5.0.0" + resolved "https://registry.npmjs.org/@lerna/profiler/-/profiler-5.0.0.tgz#e1b74d17dbd6172b5ce9c80426b336bf6ab2e8e9" + integrity sha512-hFX+ZtoH7BdDoGI+bqOYaSptJTFI58wNK9qq/pHwL5ksV7vOhxP2cQAuo1SjgBKHGl0Ex/9ZT080YVV4jP1ehw== dependencies: fs-extra "^9.1.0" npmlog "^4.1.2" upath "^2.0.1" -"@lerna/project@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/project/-/project-4.0.0.tgz#ff84893935833533a74deff30c0e64ddb7f0ba6b" - integrity sha512-o0MlVbDkD5qRPkFKlBZsXZjoNTWPyuL58564nSfZJ6JYNmgAptnWPB2dQlAc7HWRZkmnC2fCkEdoU+jioPavbg== +"@lerna/project@5.0.0": + version "5.0.0" + resolved "https://registry.npmjs.org/@lerna/project/-/project-5.0.0.tgz#31672891236696b2a70226388de0300c6086d75f" + integrity sha512-+izHk7D/Di2b0s69AzKzAa/qBz32H9s67oN9aKntrjNylpY7iN5opU157l60Kh4TprYHU5bLisqzFLZsHHADGw== dependencies: - "@lerna/package" "4.0.0" - "@lerna/validation-error" "4.0.0" + "@lerna/package" "5.0.0" + "@lerna/validation-error" "5.0.0" cosmiconfig "^7.0.0" dedent "^0.7.0" dot-prop "^6.0.1" @@ -3592,38 +3749,38 @@ resolve-from "^5.0.0" write-json-file "^4.3.0" -"@lerna/prompt@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/prompt/-/prompt-4.0.0.tgz#5ec69a803f3f0db0ad9f221dad64664d3daca41b" - integrity sha512-4Ig46oCH1TH5M7YyTt53fT6TuaKMgqUUaqdgxvp6HP6jtdak6+amcsqB8YGz2eQnw/sdxunx84DfI9XpoLj4bQ== +"@lerna/prompt@5.0.0": + version "5.0.0" + resolved "https://registry.npmjs.org/@lerna/prompt/-/prompt-5.0.0.tgz#31d3d82ecd17e863f8b7cc7944accff4f3de3395" + integrity sha512-cq2k04kOPY1yuJNHJn4qfBDDrCi9PF4Q228JICa6bxaONRf/C/TRsEQXHVIdlax8B3l53LnlGv5GECwRuvkQbA== dependencies: inquirer "^7.3.3" npmlog "^4.1.2" -"@lerna/publish@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/publish/-/publish-4.0.0.tgz#f67011305adeba120066a3b6d984a5bb5fceef65" - integrity sha512-K8jpqjHrChH22qtkytA5GRKIVFEtqBF6JWj1I8dWZtHs4Jywn8yB1jQ3BAMLhqmDJjWJtRck0KXhQQKzDK2UPg== +"@lerna/publish@5.0.0": + version "5.0.0" + resolved "https://registry.npmjs.org/@lerna/publish/-/publish-5.0.0.tgz#27c4c469e6abd5b52e977568d328632929e859b1" + integrity sha512-QEWFtN8fW1M+YXEQOWb2XBBCT137CrwHYK29ojMXW9HShvSZezf8Q/niH91nZ4kIhWdpOGz4w3rKopsumAM5SA== dependencies: - "@lerna/check-working-tree" "4.0.0" - "@lerna/child-process" "4.0.0" - "@lerna/collect-updates" "4.0.0" - "@lerna/command" "4.0.0" - "@lerna/describe-ref" "4.0.0" - "@lerna/log-packed" "4.0.0" - "@lerna/npm-conf" "4.0.0" - "@lerna/npm-dist-tag" "4.0.0" - "@lerna/npm-publish" "4.0.0" - "@lerna/otplease" "4.0.0" - "@lerna/output" "4.0.0" - "@lerna/pack-directory" "4.0.0" - "@lerna/prerelease-id-from-version" "4.0.0" - "@lerna/prompt" "4.0.0" - "@lerna/pulse-till-done" "4.0.0" - "@lerna/run-lifecycle" "4.0.0" - "@lerna/run-topologically" "4.0.0" - "@lerna/validation-error" "4.0.0" - "@lerna/version" "4.0.0" + "@lerna/check-working-tree" "5.0.0" + "@lerna/child-process" "5.0.0" + "@lerna/collect-updates" "5.0.0" + "@lerna/command" "5.0.0" + "@lerna/describe-ref" "5.0.0" + "@lerna/log-packed" "5.0.0" + "@lerna/npm-conf" "5.0.0" + "@lerna/npm-dist-tag" "5.0.0" + "@lerna/npm-publish" "5.0.0" + "@lerna/otplease" "5.0.0" + "@lerna/output" "5.0.0" + "@lerna/pack-directory" "5.0.0" + "@lerna/prerelease-id-from-version" "5.0.0" + "@lerna/prompt" "5.0.0" + "@lerna/pulse-till-done" "5.0.0" + "@lerna/run-lifecycle" "5.0.0" + "@lerna/run-topologically" "5.0.0" + "@lerna/validation-error" "5.0.0" + "@lerna/version" "5.0.0" fs-extra "^9.1.0" libnpmaccess "^4.0.1" npm-package-arg "^8.1.0" @@ -3631,126 +3788,138 @@ npmlog "^4.1.2" p-map "^4.0.0" p-pipe "^3.1.0" - pacote "^11.2.6" + pacote "^13.4.1" semver "^7.3.4" -"@lerna/pulse-till-done@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/pulse-till-done/-/pulse-till-done-4.0.0.tgz#04bace7d483a8205c187b806bcd8be23d7bb80a3" - integrity sha512-Frb4F7QGckaybRhbF7aosLsJ5e9WuH7h0KUkjlzSByVycxY91UZgaEIVjS2oN9wQLrheLMHl6SiFY0/Pvo0Cxg== +"@lerna/pulse-till-done@5.0.0": + version "5.0.0" + resolved "https://registry.npmjs.org/@lerna/pulse-till-done/-/pulse-till-done-5.0.0.tgz#df3c32c2d7457362956d997da366f5c060953eef" + integrity sha512-qFeVybGIZbQSWKasWIzZmHsvCQMC/AwTz5B44a0zTt5eSNQuI65HRpKKUgmFFu/Jzd7u+yp7eP+NQ53gjOcQlQ== dependencies: npmlog "^4.1.2" -"@lerna/query-graph@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/query-graph/-/query-graph-4.0.0.tgz#09dd1c819ac5ee3f38db23931143701f8a6eef63" - integrity sha512-YlP6yI3tM4WbBmL9GCmNDoeQyzcyg1e4W96y/PKMZa5GbyUvkS2+Jc2kwPD+5KcXou3wQZxSPzR3Te5OenaDdg== +"@lerna/query-graph@5.0.0": + version "5.0.0" + resolved "https://registry.npmjs.org/@lerna/query-graph/-/query-graph-5.0.0.tgz#76c45f648915ef5c884c32c3d35daa3ebb53440b" + integrity sha512-C/HXssBI8DVsZ/7IDW6JG9xhoHtWywi3L5oZB9q84MBYpQ9otUv6zbB+K4JCj7w9WHcuFWe2T/mc9wsaFuvB5g== dependencies: - "@lerna/package-graph" "4.0.0" + "@lerna/package-graph" "5.0.0" -"@lerna/resolve-symlink@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/resolve-symlink/-/resolve-symlink-4.0.0.tgz#6d006628a210c9b821964657a9e20a8c9a115e14" - integrity sha512-RtX8VEUzqT+uLSCohx8zgmjc6zjyRlh6i/helxtZTMmc4+6O4FS9q5LJas2uGO2wKvBlhcD6siibGt7dIC3xZA== +"@lerna/resolve-symlink@5.0.0": + version "5.0.0" + resolved "https://registry.npmjs.org/@lerna/resolve-symlink/-/resolve-symlink-5.0.0.tgz#edff89908e90a390791ab762305d34aa95e7bdbe" + integrity sha512-O1EMQh3O3nKjLyI2guCCaxmi9xzZXpiMZhrz2ki5ENEDB2N1+f7cZ2THT0lEOIkLRuADI6hrzoN1obJ+TTk+KQ== dependencies: fs-extra "^9.1.0" npmlog "^4.1.2" read-cmd-shim "^2.0.0" -"@lerna/rimraf-dir@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/rimraf-dir/-/rimraf-dir-4.0.0.tgz#2edf3b62d4eb0ef4e44e430f5844667d551ec25a" - integrity sha512-QNH9ABWk9mcMJh2/muD9iYWBk1oQd40y6oH+f3wwmVGKYU5YJD//+zMiBI13jxZRtwBx0vmBZzkBkK1dR11cBg== +"@lerna/rimraf-dir@5.0.0": + version "5.0.0" + resolved "https://registry.npmjs.org/@lerna/rimraf-dir/-/rimraf-dir-5.0.0.tgz#9e7689610415e6d68c9e766a462c8acfdbf04b9a" + integrity sha512-hWJg/13CiSUrWWEek3B/A1mkvBbcPvG5z69/Ugyerdpzlw44ubf02MAZ0/kXPJjkICI2hMrS07YotQ60LdYpCw== dependencies: - "@lerna/child-process" "4.0.0" + "@lerna/child-process" "5.0.0" npmlog "^4.1.2" path-exists "^4.0.0" rimraf "^3.0.2" -"@lerna/run-lifecycle@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/run-lifecycle/-/run-lifecycle-4.0.0.tgz#e648a46f9210a9bcd7c391df6844498cb5079334" - integrity sha512-IwxxsajjCQQEJAeAaxF8QdEixfI7eLKNm4GHhXHrgBu185JcwScFZrj9Bs+PFKxwb+gNLR4iI5rpUdY8Y0UdGQ== +"@lerna/run-lifecycle@5.0.0": + version "5.0.0" + resolved "https://registry.npmjs.org/@lerna/run-lifecycle/-/run-lifecycle-5.0.0.tgz#0f62c2faebc19e4ee247bdfa1e05b2a9f51b0637" + integrity sha512-36mAm9rC5DSliFShI0Y4ICjgrJXdIIVt7VW9rdbdJ8/XYjRHDzhGPB9Sc1neJOVlGL4DmaArvh5tGgo62KPJYQ== dependencies: - "@lerna/npm-conf" "4.0.0" - npm-lifecycle "^3.1.5" + "@lerna/npm-conf" "5.0.0" + "@npmcli/run-script" "^3.0.2" npmlog "^4.1.2" -"@lerna/run-topologically@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/run-topologically/-/run-topologically-4.0.0.tgz#af846eeee1a09b0c2be0d1bfb5ef0f7b04bb1827" - integrity sha512-EVZw9hGwo+5yp+VL94+NXRYisqgAlj0jWKWtAIynDCpghRxCE5GMO3xrQLmQgqkpUl9ZxQFpICgYv5DW4DksQA== +"@lerna/run-topologically@5.0.0": + version "5.0.0" + resolved "https://registry.npmjs.org/@lerna/run-topologically/-/run-topologically-5.0.0.tgz#0b0156e3ebe2bf768b9ba1339e02e947e70d1dd1" + integrity sha512-B2s1N/+r3sfPOLRA2svNk+C52JpXQleMuGap0yhOx5mZzR1M2Lo4vpe9Ody4hCvXQjfdLx/U342fxVmgugUtfQ== dependencies: - "@lerna/query-graph" "4.0.0" + "@lerna/query-graph" "5.0.0" p-queue "^6.6.2" -"@lerna/run@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/run/-/run-4.0.0.tgz#4bc7fda055a729487897c23579694f6183c91262" - integrity sha512-9giulCOzlMPzcZS/6Eov6pxE9gNTyaXk0Man+iCIdGJNMrCnW7Dme0Z229WWP/UoxDKg71F2tMsVVGDiRd8fFQ== +"@lerna/run@5.0.0": + version "5.0.0" + resolved "https://registry.npmjs.org/@lerna/run/-/run-5.0.0.tgz#3af69d1a787866cf85072a0ae9571b9c3bf262e7" + integrity sha512-8nBZstqKSO+7wHlKk1g+iexSYRVVNJq/u5ZbAzBiHNrABtqA6/0G7q9vsAEMsnPZ8ARAUYpwvbfKTipjpWH0VA== dependencies: - "@lerna/command" "4.0.0" - "@lerna/filter-options" "4.0.0" - "@lerna/npm-run-script" "4.0.0" - "@lerna/output" "4.0.0" - "@lerna/profiler" "4.0.0" - "@lerna/run-topologically" "4.0.0" - "@lerna/timer" "4.0.0" - "@lerna/validation-error" "4.0.0" + "@lerna/command" "5.0.0" + "@lerna/filter-options" "5.0.0" + "@lerna/npm-run-script" "5.0.0" + "@lerna/output" "5.0.0" + "@lerna/profiler" "5.0.0" + "@lerna/run-topologically" "5.0.0" + "@lerna/timer" "5.0.0" + "@lerna/validation-error" "5.0.0" p-map "^4.0.0" -"@lerna/symlink-binary@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/symlink-binary/-/symlink-binary-4.0.0.tgz#21009f62d53a425f136cb4c1a32c6b2a0cc02d47" - integrity sha512-zualodWC4q1QQc1pkz969hcFeWXOsVYZC5AWVtAPTDfLl+TwM7eG/O6oP+Rr3fFowspxo6b1TQ6sYfDV6HXNWA== +"@lerna/symlink-binary@5.0.0": + version "5.0.0" + resolved "https://registry.npmjs.org/@lerna/symlink-binary/-/symlink-binary-5.0.0.tgz#f9da5673ed3a44570fa4d2e691759f82bd7ad057" + integrity sha512-uYyiiNjkdL1tWf8MDXIIyCa/a2gmYaUxagqMgEZ4wRtOk+PDypDwMUFVop/EQtUWZqG5CAJBJYOztG3DdapTbA== dependencies: - "@lerna/create-symlink" "4.0.0" - "@lerna/package" "4.0.0" + "@lerna/create-symlink" "5.0.0" + "@lerna/package" "5.0.0" fs-extra "^9.1.0" p-map "^4.0.0" -"@lerna/symlink-dependencies@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/symlink-dependencies/-/symlink-dependencies-4.0.0.tgz#8910eca084ae062642d0490d8972cf2d98e9ebbd" - integrity sha512-BABo0MjeUHNAe2FNGty1eantWp8u83BHSeIMPDxNq0MuW2K3CiQRaeWT3EGPAzXpGt0+hVzBrA6+OT0GPn7Yuw== +"@lerna/symlink-dependencies@5.0.0": + version "5.0.0" + resolved "https://registry.npmjs.org/@lerna/symlink-dependencies/-/symlink-dependencies-5.0.0.tgz#878b0f52737f82bb7014e13afda8efc606fc071c" + integrity sha512-wlZGOOB87XMy278hpF4fOwGNnjTXf1vJ/cFHIdKsJAiDipyhtnuCiJLBDPh4NzEGb02o4rhaqt8Nl5yWRu9CNA== dependencies: - "@lerna/create-symlink" "4.0.0" - "@lerna/resolve-symlink" "4.0.0" - "@lerna/symlink-binary" "4.0.0" + "@lerna/create-symlink" "5.0.0" + "@lerna/resolve-symlink" "5.0.0" + "@lerna/symlink-binary" "5.0.0" fs-extra "^9.1.0" p-map "^4.0.0" p-map-series "^2.1.0" -"@lerna/timer@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/timer/-/timer-4.0.0.tgz#a52e51bfcd39bfd768988049ace7b15c1fd7a6da" - integrity sha512-WFsnlaE7SdOvjuyd05oKt8Leg3ENHICnvX3uYKKdByA+S3g+TCz38JsNs7OUZVt+ba63nC2nbXDlUnuT2Xbsfg== +"@lerna/temp-write@5.0.0": + version "5.0.0" + resolved "https://registry.npmjs.org/@lerna/temp-write/-/temp-write-5.0.0.tgz#44f8c7c82f498e15db33c166d063be117b819162" + integrity sha512-JOkRR6xyASuBy1udyS/VD52Wgywnz7cSKppD+QKIDseNzTq27I9mNmb702BSXNXIdD19lLVQ7q6WoAlpnelnZg== + dependencies: + graceful-fs "^4.1.15" + is-stream "^2.0.0" + make-dir "^3.0.0" + temp-dir "^1.0.0" + uuid "^8.3.2" -"@lerna/validation-error@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/validation-error/-/validation-error-4.0.0.tgz#af9d62fe8304eaa2eb9a6ba1394f9aa807026d35" - integrity sha512-1rBOM5/koiVWlRi3V6dB863E1YzJS8v41UtsHgMr6gB2ncJ2LsQtMKlJpi3voqcgh41H8UsPXR58RrrpPpufyw== +"@lerna/timer@5.0.0": + version "5.0.0" + resolved "https://registry.npmjs.org/@lerna/timer/-/timer-5.0.0.tgz#ab8fba29f90de21b0eb02406916269122deb2e41" + integrity sha512-p2vevkpB6V/b0aR8VyMLDfg0Arp9VvMxcZOEu+IfZ9XKTtnbwjWPHKUOS34x/VGa6bnOIWjE046ixWymOs/fTw== + +"@lerna/validation-error@5.0.0": + version "5.0.0" + resolved "https://registry.npmjs.org/@lerna/validation-error/-/validation-error-5.0.0.tgz#3d3557023e3eb2fd3d8fc9c89f7352a1b6e5bd3e" + integrity sha512-fu/MhqRXiRQM2cirP/HoSkfwc5XtJ21G60WHv74RnanKBqWEZAUALWa3MQN2sYhVV/FpDW3GLkO008IW5NWzdg== dependencies: npmlog "^4.1.2" -"@lerna/version@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/version/-/version-4.0.0.tgz#532659ec6154d8a8789c5ab53878663e244e3228" - integrity sha512-otUgiqs5W9zGWJZSCCMRV/2Zm2A9q9JwSDS7s/tlKq4mWCYriWo7+wsHEA/nPTMDyYyBO5oyZDj+3X50KDUzeA== +"@lerna/version@5.0.0": + version "5.0.0" + resolved "https://registry.npmjs.org/@lerna/version/-/version-5.0.0.tgz#36a808e8b4458febd58a6b76852f2ce30e740ca1" + integrity sha512-M8KvdyG5kR/d3wgg5S46Q2YMf0L9iw9MiumTvlDP4ckysTt+04kS74Vp4+aClgPM4xaoI5OuMrs6wy5ICcd3Pw== dependencies: - "@lerna/check-working-tree" "4.0.0" - "@lerna/child-process" "4.0.0" - "@lerna/collect-updates" "4.0.0" - "@lerna/command" "4.0.0" - "@lerna/conventional-commits" "4.0.0" - "@lerna/github-client" "4.0.0" - "@lerna/gitlab-client" "4.0.0" - "@lerna/output" "4.0.0" - "@lerna/prerelease-id-from-version" "4.0.0" - "@lerna/prompt" "4.0.0" - "@lerna/run-lifecycle" "4.0.0" - "@lerna/run-topologically" "4.0.0" - "@lerna/validation-error" "4.0.0" + "@lerna/check-working-tree" "5.0.0" + "@lerna/child-process" "5.0.0" + "@lerna/collect-updates" "5.0.0" + "@lerna/command" "5.0.0" + "@lerna/conventional-commits" "5.0.0" + "@lerna/github-client" "5.0.0" + "@lerna/gitlab-client" "5.0.0" + "@lerna/output" "5.0.0" + "@lerna/prerelease-id-from-version" "5.0.0" + "@lerna/prompt" "5.0.0" + "@lerna/run-lifecycle" "5.0.0" + "@lerna/run-topologically" "5.0.0" + "@lerna/temp-write" "5.0.0" + "@lerna/validation-error" "5.0.0" chalk "^4.1.0" dedent "^0.7.0" load-json-file "^6.2.0" @@ -3762,13 +3931,12 @@ p-waterfall "^2.1.1" semver "^7.3.4" slash "^3.0.0" - temp-write "^4.0.0" write-json-file "^4.3.0" -"@lerna/write-log-file@4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@lerna/write-log-file/-/write-log-file-4.0.0.tgz#18221a38a6a307d6b0a5844dd592ad53fa27091e" - integrity sha512-XRG5BloiArpXRakcnPHmEHJp+4AtnhRtpDIHSghmXD5EichI1uD73J7FgPp30mm2pDRq3FdqB0NbwSEsJ9xFQg== +"@lerna/write-log-file@5.0.0": + version "5.0.0" + resolved "https://registry.npmjs.org/@lerna/write-log-file/-/write-log-file-5.0.0.tgz#ad3d33d6153b962beef48442ab6472233b5d5197" + integrity sha512-kpPNxe9xm36QbCWY7DwO96Na6FpCHzZinJtw6ttBHslIcdR38lZuCp+/2KfJcVsRIPNOsp1VvgP7EZIKiBhgjw== dependencies: npmlog "^4.1.2" write-file-atomic "^3.0.3" @@ -3968,9 +4136,9 @@ react-is "^16.8.0 || ^17.0.0" "@maxim_mazurok/gapi.client.calendar@^3.0.20220408": - version "3.0.20220505" - resolved "https://registry.npmjs.org/@maxim_mazurok/gapi.client.calendar/-/gapi.client.calendar-3.0.20220505.tgz#14a72876308481cb97a2ffa859543b1fa0ee258e" - integrity sha512-ZzsJdybuX/RvcDU8eF7XCyHdKYuHU6Z12326hB2+f4IWkMrDTQCayu05cRXZEuvP2YwoPm8gglPpV+flYk1d3g== + version "3.0.20220610" + resolved "https://registry.npmjs.org/@maxim_mazurok/gapi.client.calendar/-/gapi.client.calendar-3.0.20220610.tgz#46e0092da54fc8eb1342a3d1af04a071a761faac" + integrity sha512-pZwIaTw+PizFSXrF5WqP4dj+b1Vlj/hNwBY4ocWpJ2uhBDoBPAVIc8lEUNwNZVDbAgtbWHD2Kl84UsP1wmBS+w== dependencies: "@types/gapi.client" "*" @@ -4015,9 +4183,9 @@ typescript "~4.6.3" "@microsoft/microsoft-graph-types@^2.6.0": - version "2.20.0" - resolved "https://registry.npmjs.org/@microsoft/microsoft-graph-types/-/microsoft-graph-types-2.20.0.tgz#fc532a5dc146c0b8a6f78a633fe930535f8cdc42" - integrity sha512-T1EPoYdH4124Rg+h261dvW2qAeSRAotHM/6lv10fDsYX4DWsppvresmfhGbPrLApK/lRTRj93bn4/Nwwmdv4Zg== + version "2.21.0" + resolved "https://registry.npmjs.org/@microsoft/microsoft-graph-types/-/microsoft-graph-types-2.21.0.tgz#3244b41374307f590948e25fb2de8dad2a8a81c9" + integrity sha512-ZbYetXyUqHZliM8exZzF7IPEKVMvR+PaY+P9bjNHpxeiacF3U9+c40YRv0TJHpF6+50GWmJmPD6BsKHVRBZiCA== "@microsoft/tsdoc-config@~0.16.1": version "0.16.1" @@ -4034,14 +4202,6 @@ resolved "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.14.1.tgz#155ef21065427901994e765da8a0ba0eaae8b8bd" integrity sha512-6Wci+Tp3CgPt/B9B0a3J4s3yMgLNSku6w5TV6mN+61C71UqsRBv2FUibBf3tPGlNxebgPHMEUzKpb1ggE8KCKw== -"@mswjs/cookies@^0.1.6", "@mswjs/cookies@^0.1.7": - version "0.1.7" - resolved "https://registry.npmjs.org/@mswjs/cookies/-/cookies-0.1.7.tgz#d334081b2c51057a61c1dd7b76ca3cac02251651" - integrity sha512-bDg1ReMBx+PYDB4Pk7y1Q07Zz1iKIEUWQpkEXiA2lEWg9gvOZ8UBmGXilCEUvyYoRFlmr/9iXTRR69TrgSwX/Q== - dependencies: - "@types/set-cookie-parser" "^2.4.0" - set-cookie-parser "^2.4.6" - "@mswjs/cookies@^0.2.0": version "0.2.0" resolved "https://registry.npmjs.org/@mswjs/cookies/-/cookies-0.2.0.tgz#7ef2b5d7e444498bb27cf57720e61f76a4ce9f23" @@ -4050,22 +4210,10 @@ "@types/set-cookie-parser" "^2.4.0" set-cookie-parser "^2.4.6" -"@mswjs/interceptors@^0.12.6", "@mswjs/interceptors@^0.12.7": - version "0.12.7" - resolved "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.12.7.tgz#0d1cd4cd31a0f663e0455993951201faa09d0909" - integrity sha512-eGjZ3JRAt0Fzi5FgXiV/P3bJGj0NqsN7vBS0J0FO2AQRQ0jCKQS4lEFm4wvlSgKQNfeuc/Vz6d81VtU3Gkx/zg== - dependencies: - "@open-draft/until" "^1.0.3" - "@xmldom/xmldom" "^0.7.2" - debug "^4.3.2" - headers-utils "^3.0.2" - outvariant "^1.2.0" - strict-event-emitter "^0.2.0" - "@mswjs/interceptors@^0.15.1": - version "0.15.1" - resolved "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.15.1.tgz#4a0009f56e51bc2cd3176f1507065c7d2f6c0d5e" - integrity sha512-D5B+ZJNlfvBm6ZctAfRBdNJdCHYAe2Ix4My5qfbHV5WH+3lkt3mmsjiWJzEh5ZwGDauzY487TldI275If7DJVw== + version "0.15.3" + resolved "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.15.3.tgz#bcd17b5d7558d4f598007a4bb383b42dc9264f8d" + integrity sha512-GJ1qzBq82EQ3bwhsvw5nScbrLzOSI5H/TyB2CGd1K7dDqX58DJDLJHexiN+S5Ucvl6/84FjRdIysz0RxE/L8MA== dependencies: "@open-draft/until" "^1.0.3" "@xmldom/xmldom" "^0.7.5" @@ -4074,6 +4222,18 @@ outvariant "^1.2.1" strict-event-emitter "^0.2.0" +"@mswjs/interceptors@^0.16.3": + version "0.16.4" + resolved "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.16.4.tgz#fa41de240dd094e6ad19e3a6829651589551d159" + integrity sha512-McPKUFlZNS/wo+OAor15k0fv2skK+EdWl9CEcdxAqsN4vKajlxCxDU4B5W/pn1y0TJPSAOmxR6LYFe/8esePrg== + dependencies: + "@open-draft/until" "^1.0.3" + "@xmldom/xmldom" "^0.7.5" + debug "^4.3.3" + headers-polyfill "^3.0.4" + outvariant "^1.2.1" + strict-event-emitter "^0.2.4" + "@n1ru4l/graphql-live-query@^0.9.0": version "0.9.0" resolved "https://registry.npmjs.org/@n1ru4l/graphql-live-query/-/graphql-live-query-0.9.0.tgz#defaebdd31f625bee49e6745934f36312532b2bc" @@ -4084,6 +4244,29 @@ resolved "https://registry.npmjs.org/@n1ru4l/push-pull-async-iterable-iterator/-/push-pull-async-iterable-iterator-3.1.0.tgz#be450c97d1c7cd6af1a992d53232704454345df9" integrity sha512-K4scWxGhdQM0masHHy4gIQs2iGiLEXCrXttumknyPJqtdl4J179BjpibWSSQ1fxKdCcHgIlCTKXJU6cMM6D6Wg== +"@nestjs/common@8.4.4": + version "8.4.4" + resolved "https://registry.npmjs.org/@nestjs/common/-/common-8.4.4.tgz#0914c6c0540b5a344c7c8fd6072faa1a49af1158" + integrity sha512-QHi7QcgH/5Jinz+SCfIZJkFHc6Cch1YsAEGFEhi6wSp6MILb0sJMQ1CX06e9tCOAjSlBwaJj4PH0eFCVau5v9Q== + dependencies: + axios "0.26.1" + iterare "1.2.1" + tslib "2.3.1" + uuid "8.3.2" + +"@nestjs/core@8.4.4": + version "8.4.4" + resolved "https://registry.npmjs.org/@nestjs/core/-/core-8.4.4.tgz#94fd2d63fd77791f616fbecafb79faa2235eeeff" + integrity sha512-Ef3yJPuzAttpNfehnGqIV5kHIL9SHptB5F4ERxoU7pT61H3xiYpZw6hSjx68cJO7cc6rm7/N+b4zeuJvFHtvBg== + dependencies: + "@nuxtjs/opencollective" "0.3.2" + fast-safe-stringify "2.1.1" + iterare "1.2.1" + object-hash "3.0.0" + path-to-regexp "3.2.0" + tslib "2.3.1" + uuid "8.3.2" + "@nodelib/fs.scandir@2.1.3": version "2.1.3" resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz#3a582bdb53804c6ba6d146579c46e52130cf4a3b" @@ -4105,6 +4288,46 @@ "@nodelib/fs.scandir" "2.1.3" fastq "^1.6.0" +"@npmcli/arborist@5.2.0": + version "5.2.0" + resolved "https://registry.npmjs.org/@npmcli/arborist/-/arborist-5.2.0.tgz#ee40dfe1f81ae1524819ee39c8f3e7022b0d6269" + integrity sha512-zWV7scFGL0SmpvfQyIWnMFbU/0YgtMNyvJiJwR98kyjUSntJGWFFR0O600d5W+TrDcTg0GyDbY+HdzGEg+GXLg== + dependencies: + "@isaacs/string-locale-compare" "^1.1.0" + "@npmcli/installed-package-contents" "^1.0.7" + "@npmcli/map-workspaces" "^2.0.3" + "@npmcli/metavuln-calculator" "^3.0.1" + "@npmcli/move-file" "^2.0.0" + "@npmcli/name-from-folder" "^1.0.1" + "@npmcli/node-gyp" "^2.0.0" + "@npmcli/package-json" "^2.0.0" + "@npmcli/run-script" "^3.0.0" + bin-links "^3.0.0" + cacache "^16.0.6" + common-ancestor-path "^1.0.1" + json-parse-even-better-errors "^2.3.1" + json-stringify-nice "^1.1.4" + mkdirp "^1.0.4" + mkdirp-infer-owner "^2.0.0" + nopt "^5.0.0" + npm-install-checks "^5.0.0" + npm-package-arg "^9.0.0" + npm-pick-manifest "^7.0.0" + npm-registry-fetch "^13.0.0" + npmlog "^6.0.2" + pacote "^13.0.5" + parse-conflict-json "^2.0.1" + proc-log "^2.0.0" + promise-all-reject-late "^1.0.0" + promise-call-limit "^1.0.1" + read-package-json-fast "^2.0.2" + readdir-scoped-modules "^1.1.0" + rimraf "^3.0.2" + semver "^7.3.7" + ssri "^9.0.0" + treeverse "^2.0.0" + walk-up-path "^1.0.0" + "@npmcli/arborist@^4.0.4": version "4.3.1" resolved "https://registry.npmjs.org/@npmcli/arborist/-/arborist-4.3.1.tgz#a08cddce3339882f688c1dea1651f6971e781c44" @@ -4156,7 +4379,15 @@ "@gar/promisify" "^1.0.1" semver "^7.3.5" -"@npmcli/git@^2.0.1", "@npmcli/git@^2.1.0": +"@npmcli/fs@^2.1.0": + version "2.1.0" + resolved "https://registry.npmjs.org/@npmcli/fs/-/fs-2.1.0.tgz#f2a21c28386e299d1a9fae8051d35ad180e33109" + integrity sha512-DmfBvNXGaetMxj9LTp8NAN9vEidXURrf5ZTslQzEAi/6GbW+4yjaLFQc6Tue5cpZ9Frlk4OBo/Snf1Bh/S7qTQ== + dependencies: + "@gar/promisify" "^1.1.3" + semver "^7.3.5" + +"@npmcli/git@^2.1.0": version "2.1.0" resolved "https://registry.npmjs.org/@npmcli/git/-/git-2.1.0.tgz#2fbd77e147530247d37f325930d457b3ebe894f6" integrity sha512-/hBFX/QG1b+N7PZBFs0bi+evgRZcK9nWBxQKZkGoXUT5hJSwl5c4d7y8/hm+NQZRPhQ67RzFaj5UM9YeyKoryw== @@ -4170,6 +4401,21 @@ semver "^7.3.5" which "^2.0.2" +"@npmcli/git@^3.0.0": + version "3.0.1" + resolved "https://registry.npmjs.org/@npmcli/git/-/git-3.0.1.tgz#049b99b1381a2ddf7dc56ba3e91eaf76ca803a8d" + integrity sha512-UU85F/T+F1oVn3IsB/L6k9zXIMpXBuUBE25QDH0SsURwT6IOBqkC7M16uqo2vVZIyji3X1K4XH9luip7YekH1A== + dependencies: + "@npmcli/promise-spawn" "^3.0.0" + lru-cache "^7.4.4" + mkdirp "^1.0.4" + npm-pick-manifest "^7.0.0" + proc-log "^2.0.0" + promise-inflight "^1.0.1" + promise-retry "^2.0.1" + semver "^7.3.5" + which "^2.0.2" + "@npmcli/installed-package-contents@^1.0.6", "@npmcli/installed-package-contents@^1.0.7": version "1.0.7" resolved "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-1.0.7.tgz#ab7408c6147911b970a8abe261ce512232a3f4fa" @@ -4188,6 +4434,16 @@ minimatch "^3.0.4" read-package-json-fast "^2.0.1" +"@npmcli/map-workspaces@^2.0.3": + version "2.0.3" + resolved "https://registry.npmjs.org/@npmcli/map-workspaces/-/map-workspaces-2.0.3.tgz#2d3c75119ee53246e9aa75bc469a55281cd5f08f" + integrity sha512-X6suAun5QyupNM8iHkNPh0AHdRC2rb1W+MTdMvvA/2ixgmqZwlq5cGUBgmKHUHT2LgrkKJMAXbfAoTxOigpK8Q== + dependencies: + "@npmcli/name-from-folder" "^1.0.1" + glob "^8.0.1" + minimatch "^5.0.1" + read-package-json-fast "^2.0.3" + "@npmcli/metavuln-calculator@^2.0.0": version "2.0.0" resolved "https://registry.npmjs.org/@npmcli/metavuln-calculator/-/metavuln-calculator-2.0.0.tgz#70937b8b5a5cad5c588c8a7b38c4a8bd6f62c84c" @@ -4198,6 +4454,16 @@ pacote "^12.0.0" semver "^7.3.2" +"@npmcli/metavuln-calculator@^3.0.1": + version "3.1.0" + resolved "https://registry.npmjs.org/@npmcli/metavuln-calculator/-/metavuln-calculator-3.1.0.tgz#b1c2f0991c4f2d992b1615a54d4358c05efc3702" + integrity sha512-Q5fbQqGDlYqk7kWrbg6E2j/mtqQjZop0ZE6735wYA1tYNHguIDjAuWs+kFb5rJCkLIlXllfapvsyotYKiZOTBA== + dependencies: + cacache "^16.0.0" + json-parse-even-better-errors "^2.3.1" + pacote "^13.0.3" + semver "^7.3.5" + "@npmcli/move-file@^1.0.1": version "1.0.1" resolved "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.0.1.tgz#de103070dac0f48ce49cf6693c23af59c0f70464" @@ -4213,6 +4479,14 @@ mkdirp "^1.0.4" rimraf "^3.0.2" +"@npmcli/move-file@^2.0.0": + version "2.0.0" + resolved "https://registry.npmjs.org/@npmcli/move-file/-/move-file-2.0.0.tgz#417f585016081a0184cef3e38902cd917a9bbd02" + integrity sha512-UR6D5f4KEGWJV6BGPH3Qb2EtgH+t+1XQ1Tt85c7qicN6cezzuHPdZwwAxqZr4JLtnQu0LZsTza/5gmNmSl8XLg== + dependencies: + mkdirp "^1.0.4" + rimraf "^3.0.2" + "@npmcli/name-from-folder@^1.0.1": version "1.0.1" resolved "https://registry.npmjs.org/@npmcli/name-from-folder/-/name-from-folder-1.0.1.tgz#77ecd0a4fcb772ba6fe927e2e2e155fbec2e6b1a" @@ -4223,6 +4497,11 @@ resolved "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-1.0.3.tgz#a912e637418ffc5f2db375e93b85837691a43a33" integrity sha512-fnkhw+fmX65kiLqk6E3BFLXNC26rUhK90zVwe2yncPliVT/Qos3xjhTLE59Df8KnPlcwIERXKVlU1bXoUQ+liA== +"@npmcli/node-gyp@^2.0.0": + version "2.0.0" + resolved "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-2.0.0.tgz#8c20e53e34e9078d18815c1d2dda6f2420d75e35" + integrity sha512-doNI35wIe3bBaEgrlPfdJPaCpUR89pJWep4Hq3aRdh6gKazIVWfs0jHttvSSoq47ZXgC7h73kDsUl8AoIQUB+A== + "@npmcli/package-json@^1.0.1": version "1.0.1" resolved "https://registry.npmjs.org/@npmcli/package-json/-/package-json-1.0.1.tgz#1ed42f00febe5293c3502fd0ef785647355f6e89" @@ -4230,6 +4509,13 @@ dependencies: json-parse-even-better-errors "^2.3.1" +"@npmcli/package-json@^2.0.0": + version "2.0.0" + resolved "https://registry.npmjs.org/@npmcli/package-json/-/package-json-2.0.0.tgz#3bbcf4677e21055adbe673d9f08c9f9cde942e4a" + integrity sha512-42jnZ6yl16GzjWSH7vtrmWyJDGVa/LXPdpN2rcUWolFjc9ON2N3uz0qdBbQACfmhuJZ2lbKYtmK5qx68ZPLHMA== + dependencies: + json-parse-even-better-errors "^2.3.1" + "@npmcli/promise-spawn@^1.2.0", "@npmcli/promise-spawn@^1.3.2": version "1.3.2" resolved "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-1.3.2.tgz#42d4e56a8e9274fba180dabc0aea6e38f29274f5" @@ -4237,17 +4523,12 @@ dependencies: infer-owner "^1.0.4" -"@npmcli/run-script@^1.8.2": - version "1.8.3" - resolved "https://registry.npmjs.org/@npmcli/run-script/-/run-script-1.8.3.tgz#07f440ed492400bb1114369bc37315eeaaae2bb3" - integrity sha512-ELPGWAVU/xyU+A+H3pEPj0QOvYwLTX71RArXcClFzeiyJ/b/McsZ+d0QxpznvfFtZzxGN/gz/1cvlqICR4/suQ== +"@npmcli/promise-spawn@^3.0.0": + version "3.0.0" + resolved "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-3.0.0.tgz#53283b5f18f855c6925f23c24e67c911501ef573" + integrity sha512-s9SgS+p3a9Eohe68cSI3fi+hpcZUmXq5P7w0kMlAsWVtR7XbK3ptkZqKT2cK1zLDObJ3sR+8P59sJE0w/KTL1g== dependencies: - "@npmcli/node-gyp" "^1.0.2" - "@npmcli/promise-spawn" "^1.3.2" infer-owner "^1.0.4" - node-gyp "^7.1.0" - puka "^1.0.1" - read-package-json-fast "^2.0.1" "@npmcli/run-script@^2.0.0": version "2.0.0" @@ -4259,6 +4540,25 @@ node-gyp "^8.2.0" read-package-json-fast "^2.0.1" +"@npmcli/run-script@^3.0.0", "@npmcli/run-script@^3.0.1", "@npmcli/run-script@^3.0.2": + version "3.0.3" + resolved "https://registry.npmjs.org/@npmcli/run-script/-/run-script-3.0.3.tgz#66afa6e0c4c3484056195f295fa6c1d1a45ddf58" + integrity sha512-ZXL6qgC5NjwfZJ2nET+ZSLEz/PJgJ/5CU90C2S66dZY4Jw73DasS4ZCXuy/KHWYP0imjJ4VtA+Gebb5BxxKp9Q== + dependencies: + "@npmcli/node-gyp" "^2.0.0" + "@npmcli/promise-spawn" "^3.0.0" + node-gyp "^8.4.1" + read-package-json-fast "^2.0.3" + +"@nuxtjs/opencollective@0.3.2": + version "0.3.2" + resolved "https://registry.npmjs.org/@nuxtjs/opencollective/-/opencollective-0.3.2.tgz#620ce1044f7ac77185e825e1936115bb38e2681c" + integrity sha512-um0xL3fO7Mf4fDxcqx9KryrB7zgRM5JSlvGN5AGkP6JLM5XEKyjeAiPbNxdXVXQ16isuAhYpvP88NgL2BGd6aA== + dependencies: + chalk "^4.1.0" + consola "^2.15.0" + node-fetch "^2.6.1" + "@octokit/app@^12.0.4": version "12.0.5" resolved "https://registry.npmjs.org/@octokit/app/-/app-12.0.5.tgz#0b25446daffcb36967b26944410eab1ccbba0c06" @@ -4578,19 +4878,19 @@ resolved "https://registry.npmjs.org/@octokit/webhooks-methods/-/webhooks-methods-2.0.0.tgz#1108b9ea661ca6c81e4a8bfa63a09eb27d5bc2db" integrity sha512-35cfQ4YWlnZnmZKmIxlGPUPLtbkF8lr/A/1Sk1eC0ddLMwQN06dOuLc+dI3YLQS+T+MoNt3DIQ0NynwgKPilig== -"@octokit/webhooks-types@5.6.0": - version "5.6.0" - resolved "https://registry.npmjs.org/@octokit/webhooks-types/-/webhooks-types-5.6.0.tgz#cbc908ef0df997de7f65da4e4003ecb8430410d3" - integrity sha512-y3MqE6N6Ksg1+YV0sXVpW2WP7Y24h7rUp2hDJuzoqWdKGr7owmRDyHC72INwfCYNzura/vsNPXvc6Xbfp4wGGw== +"@octokit/webhooks-types@5.8.0": + version "5.8.0" + resolved "https://registry.npmjs.org/@octokit/webhooks-types/-/webhooks-types-5.8.0.tgz#b76d1a3e3ad82cec5680d3c6c3443a620047a6ef" + integrity sha512-8adktjIb76A7viIdayQSFuBEwOzwhDC+9yxZpKNHjfzrlostHCw0/N7JWpWMObfElwvJMk2fY2l1noENCk9wmw== "@octokit/webhooks@^9.0.1", "@octokit/webhooks@^9.14.1": - version "9.24.0" - resolved "https://registry.npmjs.org/@octokit/webhooks/-/webhooks-9.24.0.tgz#3e3b194ee67151f674e8d0d565c8179828a12b3d" - integrity sha512-s1nqplA+j4sP7Cz40jn/Q2ipkKRKJ7Gi+NzZiSnwNfisYNduLHEwMUJVBEKc5I0r6GMcZZRJOe6PJ2rJXYW66g== + version "9.26.0" + resolved "https://registry.npmjs.org/@octokit/webhooks/-/webhooks-9.26.0.tgz#cf453bb313da3b66f1a90c84464d978e1c625cce" + integrity sha512-foZlsgrTDwAmD5j2Czn6ji10lbWjGDVsUxTIydjG9KTkAWKJrFapXJgO5SbGxRwfPd3OJdhK3nA2YPqVhxLXqA== dependencies: "@octokit/request-error" "^2.0.2" "@octokit/webhooks-methods" "^2.0.0" - "@octokit/webhooks-types" "5.6.0" + "@octokit/webhooks-types" "5.8.0" aggregate-error "^3.1.0" "@open-draft/until@^1.0.3": @@ -4606,6 +4906,27 @@ fast-deep-equal "^3.1.3" lodash.clonedeep "^4.5.0" +"@openapitools/openapi-generator-cli@^2.4.26": + version "2.5.1" + resolved "https://registry.npmjs.org/@openapitools/openapi-generator-cli/-/openapi-generator-cli-2.5.1.tgz#3825be4f7317199183fbd0d464dd8a7ba055fcf5" + integrity sha512-WSRQBU0dCSVD+0Qv8iCsv0C4iMaZe/NpJ/CT4SmrEYLH3txoKTE8wEfbdj/kqShS8Or0YEGDPUzhSIKY151L0w== + dependencies: + "@nestjs/common" "8.4.4" + "@nestjs/core" "8.4.4" + "@nuxtjs/opencollective" "0.3.2" + chalk "4.1.2" + commander "8.3.0" + compare-versions "4.1.3" + concurrently "6.5.1" + console.table "0.10.0" + fs-extra "10.0.1" + glob "7.1.6" + inquirer "8.2.2" + lodash "4.17.21" + reflect-metadata "0.1.13" + rxjs "7.5.5" + tslib "2.0.3" + "@opentelemetry/api@^1.0.1": version "1.0.4" resolved "https://registry.npmjs.org/@opentelemetry/api/-/api-1.0.4.tgz#a167e46c10d05a07ab299fc518793b0cff8f6924" @@ -4669,11 +4990,18 @@ resolved "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" integrity sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA= -"@react-hookz/deep-equal@^1.0.2": +"@react-hookz/deep-equal@^1.0.1", "@react-hookz/deep-equal@^1.0.2": version "1.0.2" resolved "https://registry.npmjs.org/@react-hookz/deep-equal/-/deep-equal-1.0.2.tgz#4e8bdeda027379dcf8b62a42e5f75f0351b11b35" integrity sha512-cM5kPFb6EFH5q52WzRxfRX9+8g5kq78McWOYs6e1seo+nK6NpfLupT5uOCIJp37jU8ayd4Su8ni3HRFTN2C2kg== +"@react-hookz/web@^13.0.0": + version "13.3.0" + resolved "https://registry.npmjs.org/@react-hookz/web/-/web-13.3.0.tgz#257e31049e92a121912fe1e67bdd01dbec5a203b" + integrity sha512-KswgkmqBVVDo6UnBFfssrojmDisogxC4jGZmd976R8YHoS3zdQJxjqICpOBSRohbRyYhYS9Cprw7BuV/CZcMaw== + dependencies: + "@react-hookz/deep-equal" "^1.0.1" + "@react-hookz/web@^14.0.0": version "14.2.2" resolved "https://registry.npmjs.org/@react-hookz/web/-/web-14.2.2.tgz#eee0085f954e5b62d0a6c5b20d8786c28abfb9ad" @@ -4707,9 +5035,9 @@ integrity sha512-8UiDeDbjCImFSfOegGu13otQ7OdP9FOYpcLjeouppnhs+MPeIEAtYS+jCcBKmi3reyTagC15/KVSRhde1wS1vg== "@roadiehq/backstage-plugin-buildkite@^2.0.0": - version "2.0.3" - resolved "https://registry.npmjs.org/@roadiehq/backstage-plugin-buildkite/-/backstage-plugin-buildkite-2.0.3.tgz#73f3586c176c4c2ffe55af78ee8ce86c3c363dc6" - integrity sha512-Jna1m/pj52G7qz0PKmldQUXHsw5IUTxtgxVh2n1X2CTq+o10xySVspmEyEmLdNxkeQdYO+a280fYWjEKo9xoyQ== + version "2.0.4" + resolved "https://registry.npmjs.org/@roadiehq/backstage-plugin-buildkite/-/backstage-plugin-buildkite-2.0.4.tgz#cd72fd35a9f8b6bb1c2ccd3c13d2aece2ecaf86e" + integrity sha512-bOrqKO9MmRB5jgue+S8WEmJk83h6g4EdGp2qmirVODQddL03gtFNgMTauQkKAV7T8N+C948xDcgdL3pjvLYJdA== dependencies: "@backstage/catalog-model" "^1.0.0" "@backstage/core-components" "^0.9.0" @@ -4750,9 +5078,9 @@ zustand "3.6.9" "@roadiehq/backstage-plugin-github-pull-requests@^2.0.0": - version "2.1.2" - resolved "https://registry.npmjs.org/@roadiehq/backstage-plugin-github-pull-requests/-/backstage-plugin-github-pull-requests-2.1.2.tgz#1c8289679bb109a70c30b5ba090ea7d751553764" - integrity sha512-VXv1D1JhifX0B9nukxFa61MyXouPTXEYkeNwYB+Y3e0VfoPEGzAeE/Ub23rP8aVcYsbuyCA+6u/HQsrw6Vgpxw== + version "2.2.0" + resolved "https://registry.npmjs.org/@roadiehq/backstage-plugin-github-pull-requests/-/backstage-plugin-github-pull-requests-2.2.0.tgz#6c62c965842819bae185f96e1fbd1c26cf64c564" + integrity sha512-UNpfVMEx3WmUqIpIWPvf8uEZzCClff5H2krT/8iS3yaEP1ng9ZfLQPV7JgF8Bvaad37M08mCuvdmurtL6L3xSw== dependencies: "@backstage/catalog-model" "^1.0.0" "@backstage/core-components" "^0.9.0" @@ -4794,10 +5122,10 @@ react-router-dom "6.0.0-beta.0" react-use "^17.2.4" -"@rollup/plugin-commonjs@^21.0.1": - version "21.1.0" - resolved "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-21.1.0.tgz#45576d7b47609af2db87f55a6d4b46e44fc3a553" - integrity sha512-6ZtHx3VHIp2ReNNDxHjuUml6ur+WcQ28N1yHgCQwsbNkQg2suhxGMDQGJOn/KuDxKtd1xuZP5xSTwBA4GQ8hbA== +"@rollup/plugin-commonjs@^22.0.0": + version "22.0.0" + resolved "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-22.0.0.tgz#f4d87016e2fbf187a593ab9f46626fe05b59e8bd" + integrity sha512-Ktvf2j+bAO+30awhbYoCaXpBcyPmJbaEUYClQns/+6SNCYFURbvBiNbWgHITEsIgDDWCDUclWRKEuf8cwZCFoQ== dependencies: "@rollup/pluginutils" "^3.1.0" commondir "^1.0.1" @@ -5122,9 +5450,9 @@ defer-to-connect "^2.0.0" "@testing-library/cypress@^8.0.2": - version "8.0.2" - resolved "https://registry.npmjs.org/@testing-library/cypress/-/cypress-8.0.2.tgz#b13f0ff2424dec4368b6670dfbfb7e43af8eefc9" - integrity sha512-KVdm7n37sg/A4e3wKMD4zUl0NpzzVhx06V9Tf0hZHZ7nrZ4yFva6Zwg2EFF1VzHkEfN/ahUzRtT1qiW+vuWnJw== + version "8.0.3" + resolved "https://registry.npmjs.org/@testing-library/cypress/-/cypress-8.0.3.tgz#24ab34df34d7896866603ade705afbdd186e273c" + integrity sha512-nY2YaSbmuPo5k6kL0iLj/pGPPfka3iwb3kpTx8QN/vOCns92Saz9wfACqB8FJzcR7+lfA4d5HUOWqmTddBzczg== dependencies: "@babel/runtime" "^7.14.6" "@testing-library/dom" "^8.1.0" @@ -5143,7 +5471,7 @@ lz-string "^1.4.4" pretty-format "^27.0.2" -"@testing-library/jest-dom@^5.10.1": +"@testing-library/jest-dom@^5.10.1", "@testing-library/jest-dom@^5.16.4": version "5.16.4" resolved "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.16.4.tgz#938302d7b8b483963a3ae821f1c0808f872245cd" integrity sha512-Gy+IoFutbMQcky0k+bqqumXZ1cTGswLsFqmNLzNdSKkU9KGV2u9oXhukCbbJ9/LRPKiqwxEE8VpV/+YZlfkPUA== @@ -5159,9 +5487,9 @@ redent "^3.0.0" "@testing-library/react-hooks@^8.0.0": - version "8.0.0" - resolved "https://registry.npmjs.org/@testing-library/react-hooks/-/react-hooks-8.0.0.tgz#7d0164bffce4647f506039de0a97f6fcbd20f4bf" - integrity sha512-uZqcgtcUUtw7Z9N32W13qQhVAD+Xki2hxbTR461MKax8T6Jr8nsUvZB+vcBTkzY2nFvsUet434CsgF0ncW2yFw== + version "8.0.1" + resolved "https://registry.npmjs.org/@testing-library/react-hooks/-/react-hooks-8.0.1.tgz#0924bbd5b55e0c0c0502d1754657ada66947ca12" + integrity sha512-Aqhl2IVmLt8IovEVarNDFuJDVWVvhnr9/GCU6UUnrYXwgDFF9h2L2o2P9KBni1AST5sT6riAyoukFLyjQUgD/g== dependencies: "@babel/runtime" "^7.12.5" react-error-boundary "^3.1.0" @@ -5176,9 +5504,9 @@ "@types/react-dom" "<18.0.0" "@testing-library/user-event@^14.0.0": - version "14.2.0" - resolved "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.2.0.tgz#8293560f8f80a00383d6c755ec3e0b918acb1683" - integrity sha512-+hIlG4nJS6ivZrKnOP7OGsDu9Fxmryj9vCl8x0ZINtTJcCHs2zLsYif5GzuRiBF2ck5GZG2aQr7Msg+EHlnYVQ== + version "14.2.1" + resolved "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.2.1.tgz#8c5ff2d004544bb2220e1d864f7267fe7eb6c556" + integrity sha512-HOr1QiODrq+0j9lKU5i10y9TbhxMBMRMGimNx10asdmau9cb8Xb1Vyg0GvTwyIL2ziQyh2kAloOtAQFBQVuecA== "@tokenizer/token@^0.3.0": version "0.3.0" @@ -5210,6 +5538,16 @@ resolved "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad" integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA== +"@ts-morph/common@~0.16.0": + version "0.16.0" + resolved "https://registry.npmjs.org/@ts-morph/common/-/common-0.16.0.tgz#57e27d4b3fd65a4cd72cb36679ed08acb40fa3ba" + integrity sha512-SgJpzkTgZKLKqQniCjLaE3c2L2sdL7UShvmTmPBejAKd2OKV/yfMpQ2IWpAuA+VY5wy7PkSUaEObIqEK6afFuw== + dependencies: + fast-glob "^3.2.11" + minimatch "^5.1.0" + mkdirp "^1.0.4" + path-browserify "^1.0.1" + "@tsconfig/node10@^1.0.7": version "1.0.8" resolved "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz#c1e4e80d6f964fbecb3359c43bd48b40f7cadad9" @@ -5404,7 +5742,7 @@ dependencies: commander "*" -"@types/compression@^1.7.0": +"@types/compression@^1.7.0", "@types/compression@^1.7.2": version "1.7.2" resolved "https://registry.npmjs.org/@types/compression/-/compression-1.7.2.tgz#7cc1cdb01b4730eea284615a68fc70a2cdfd5e71" integrity sha512-lwEL4M/uAGWngWFLSG87ZDr2kLrbuR8p7X+QZB1OQlT+qkHsCPDVFnHPyXf4Vyl4yDDorNY+mAhosxkCvppatg== @@ -5460,13 +5798,13 @@ resolved "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz#6b2c510a7ad7039e98e7b8d3d6598f4359e5c080" integrity sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw== -"@types/cron@^1.7.3": - version "1.7.3" - resolved "https://registry.npmjs.org/@types/cron/-/cron-1.7.3.tgz#993db7d54646f61128c851607b64ba4495deae93" - integrity sha512-iPmUXyIJG1Js+ldPYhOQcYU3kCAQ2FWrSkm1FJPoii2eYSn6wEW6onPukNTT0bfiflexNSRPl6KWmAIqS+36YA== +"@types/cron@^2.0.0": + version "2.0.0" + resolved "https://registry.npmjs.org/@types/cron/-/cron-2.0.0.tgz#4fe75f2720a3b69a1f7b80e656749f4c2c96d727" + integrity sha512-xZM08fqvwIXgghtPVkSPKNgC+JoMQ2OHazEvyTKnNf7aWu1aB6/4lBbQFrb03Td2cUGG7ITzMv3mFYnMu6xRaQ== dependencies: + "@types/luxon" "*" "@types/node" "*" - moment ">=2.14.0" "@types/d3-color@*": version "3.0.2" @@ -5478,10 +5816,10 @@ resolved "https://registry.npmjs.org/@types/d3-color/-/d3-color-2.0.3.tgz#8bc4589073c80e33d126345542f588056511fe82" integrity sha512-+0EtEjBfKEDtH9Rk3u3kLOUXM5F+iZK+WvASPb0MhIZl8J8NUvGeZRwKCXl+P3HkYx5TdU4YtcibpqHkSR9n7w== -"@types/d3-force@^2.1.1": - version "2.1.4" - resolved "https://registry.npmjs.org/@types/d3-force/-/d3-force-2.1.4.tgz#98919b87db8a0ca5011d189c598d69251d20344d" - integrity sha512-1XVRc2QbeUSL1FRVE53Irdz7jY+drTwESHIMVirCwkAAMB/yVC8ezAfx/1Alq0t0uOnphoyhRle1ht5CuPgSJQ== +"@types/d3-force@^3.0.0": + version "3.0.3" + resolved "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.3.tgz#76cb20d04ae798afede1ea6e41750763ff5a9c82" + integrity sha512-z8GteGVfkWJMKsx6hwC3SiTSLspL98VNpmvLpEFJQpZPq6xpA1I8HNBDNSpukfK0Vb0l64zGFhzunLgEAcBWSA== "@types/d3-interpolate@*": version "3.0.1" @@ -5539,9 +5877,9 @@ "@types/d3-path" "^2" "@types/d3-shape@^3.0.1": - version "3.0.2" - resolved "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.0.2.tgz#4b1ca4ddaac294e76b712429726d40365cd1e8ca" - integrity sha512-5+ButCmIfNX8id5seZ7jKj3igdcxx+S9IDBiT35fQGTLZUfkFgTv+oBH34xgeoWDKpWcMITSzBILWQtBoN5Piw== + version "3.1.0" + resolved "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.0.tgz#1d87a6ddcf28285ef1e5c278ca4bdbc0658f3505" + integrity sha512-jYIYxFFA9vrJ8Hd4Se83YI6XF+gzDL1aC5DCsldai4XYYiVNdhtpGbA/GM6iyQ8ayhSp3a148LY34hy7A4TxZA== dependencies: "@types/d3-path" "*" @@ -5634,10 +5972,10 @@ resolved "https://registry.npmjs.org/@types/expect/-/expect-1.20.4.tgz#8288e51737bf7e3ab5d7c77bfa695883745264e5" integrity sha512-Q5Vn3yjTDyCMV50TB6VRIbQNxSE4OmZR86VSbGaNpfUolm0iePBB4KdEEHmxoY5sT2+2DIvXW0rvMDP2nHZ4Mg== -"@types/express-serve-static-core@*", "@types/express-serve-static-core@4.17.28", "@types/express-serve-static-core@^4.17.18", "@types/express-serve-static-core@^4.17.5": - version "4.17.28" - resolved "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.28.tgz#c47def9f34ec81dc6328d0b1b5303d1ec98d86b8" - integrity sha512-P1BJAEAW3E2DJUlkgq4tOL3RyMunoWXqbSCygWo5ZIWTjUgN1YnaXWW4VWl/oc8vs/XoYibEGBKP0uZyF4AHig== +"@types/express-serve-static-core@*", "@types/express-serve-static-core@4.17.29", "@types/express-serve-static-core@^4.17.18", "@types/express-serve-static-core@^4.17.5": + version "4.17.29" + resolved "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.29.tgz#2a1795ea8e9e9c91b4a4bbe475034b20c1ec711c" + integrity sha512-uMd++6dMKS32EOuw1Uli3e3BPgdLIXmezcfHv7N4c1s3gkhikBplORPpMq3fuWkxncZN1reb16d5n8yhQ80x7Q== dependencies: "@types/node" "*" "@types/qs" "*" @@ -5763,14 +6101,6 @@ resolved "https://registry.npmjs.org/@types/humanize-duration/-/humanize-duration-3.27.1.tgz#f14740d1f585a0a8e3f46359b62fda8b0eaa31e7" integrity sha512-K3e+NZlpCKd6Bd/EIdqjFJRFHbrq5TzPPLwREk5Iv/YoIjQrs6ljdAUCo+Lb2xFlGNOjGSE0dqsVD19cZL137w== -"@types/inquirer@^7.3.3": - version "7.3.3" - resolved "https://registry.npmjs.org/@types/inquirer/-/inquirer-7.3.3.tgz#92e6676efb67fa6925c69a2ee638f67a822952ac" - integrity sha512-HhxyLejTHMfohAuhRun4csWigAMjXTmRyiJTU1Y/I1xmggikFMkOUoMQRlFm+zQcPEGHSs3io/0FAmNZf8EymQ== - dependencies: - "@types/through" "*" - rxjs "^6.4.0" - "@types/inquirer@^8.1.3": version "8.2.1" resolved "https://registry.npmjs.org/@types/inquirer/-/inquirer-8.2.1.tgz#28a139be3105a1175e205537e8ac10830e38dbf4" @@ -5813,9 +6143,9 @@ "@types/node" "*" "@types/jest-when@^3.5.0": - version "3.5.0" - resolved "https://registry.npmjs.org/@types/jest-when/-/jest-when-3.5.0.tgz#6a573cd521da131e6801f0991b4f1d4dee2ebab5" - integrity sha512-rNUuZ3Mn/HDzpImPXDeOtW18zqyerPoOS2aKU0zUFbirWgJ7sN7LnRv73RmbBQ/uzw28sxf/nUofxbhJ5DWB0w== + version "3.5.1" + resolved "https://registry.npmjs.org/@types/jest-when/-/jest-when-3.5.1.tgz#296a0d62b576615046773daf123d518b10adee32" + integrity sha512-8pzJWwU4h7KyCgwAB2Hs9xuy+a1YgVcdh0vE4LT4DZx5Yo7rFO9FAPIsSORVBVCk8QfjuGZaaU9HTTh3mJnEAg== dependencies: "@types/jest" "*" @@ -5847,11 +6177,6 @@ resolved "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-2.2.6.tgz#f1a1cb35aff47bc5cfb05cb0c441ca91e914c26f" integrity sha512-+oY0FDTO2GYKEV0YPvSshGq9t7YozVkgvXLty7zogQNuCxBhT9/3INX9Q7H1aRZ4SUDRXAKlJuA4EA5nTt7SNw== -"@types/js-levenshtein@^1.1.0": - version "1.1.0" - resolved "https://registry.npmjs.org/@types/js-levenshtein/-/js-levenshtein-1.1.0.tgz#9541eec4ad6e3ec5633270a3a2b55d981edc44a9" - integrity sha512-14t0v1ICYRtRVcHASzes0v/O+TIeASb8aD55cWF1PidtInhFWSXcmhzhHqGjUWf9SUq1w70cvd1cWKUULubAfQ== - "@types/js-levenshtein@^1.1.1": version "1.1.1" resolved "https://registry.npmjs.org/@types/js-levenshtein/-/js-levenshtein-1.1.1.tgz#ba05426a43f9e4e30b631941e0aa17bf0c890ed5" @@ -5945,7 +6270,7 @@ resolved "https://registry.npmjs.org/@types/lunr/-/lunr-2.3.4.tgz#728f445855818fb17776d10ef4678f278072eb03" integrity sha512-j4x4XJwZvorEUbA519VdQ5b9AOU9TSvfi8tvxMAfP8XzNLtFex7A8vFQwqOx3WACbV0KMXbACV3cZl4/gynQ7g== -"@types/luxon@^2.0.4", "@types/luxon@^2.0.5", "@types/luxon@^2.0.9": +"@types/luxon@*", "@types/luxon@^2.0.4", "@types/luxon@^2.0.5", "@types/luxon@^2.0.9": version "2.3.2" resolved "https://registry.npmjs.org/@types/luxon/-/luxon-2.3.2.tgz#8a3f2cdd4858ce698b56cd8597d9243b8e9d3c65" integrity sha512-WOehptuhKIXukSUUkRgGbj2c997Uv/iUgYgII8U7XLJqq9W2oF0kQ6frEznRQbdurioz+L/cdaIm4GutTQfgmA== @@ -6013,10 +6338,18 @@ resolved "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz#31b7ca6407128a3d2bbc27fe2d21b345397f6197" integrity sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA== +"@types/ndjson@^2.0.1": + version "2.0.1" + resolved "https://registry.npmjs.org/@types/ndjson/-/ndjson-2.0.1.tgz#0279bc20949bfb861d69ac3de5292775b169a2d0" + integrity sha512-xSRLa/CtPjEo0plSQj+nMKjVBkYh5MeMwOXa1y//jFELdmy9AmVQgWKWQgZ+/XrNlAYxXtmKR8OHaizPgEpUEw== + dependencies: + "@types/node" "*" + "@types/through" "*" + "@types/node-fetch@^2.5.0", "@types/node-fetch@^2.5.12", "@types/node-fetch@^2.5.7": - version "2.6.1" - resolved "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.1.tgz#8f127c50481db65886800ef496f20bbf15518975" - integrity sha512-oMqjURCaxoSIsHSr1E47QHzbmzNR5rK8McHuNb11BOM9cHcIK3Avy0s/b2JlXHoQGTYS3NsvWzV1M0iK7l0wbA== + version "2.6.2" + resolved "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.2.tgz#d1a9c5fd049d9415dce61571557104dec3ec81da" + integrity sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A== dependencies: "@types/node" "*" form-data "^3.0.0" @@ -6052,9 +6385,9 @@ integrity sha512-qjd88DrCxupx/kJD5yQgZdcYKZKSIGBVDIBE1/LTGcNm3d2Np/jxojkdePDdfnBHJc5W7vSMpbJ1aB7p/Py69A== "@types/node@^16.0.0", "@types/node@^16.11.26", "@types/node@^16.9.2": - version "16.11.36" - resolved "https://registry.npmjs.org/@types/node/-/node-16.11.36.tgz#9ab9f8276987132ed2b225cace2218ba794fc751" - integrity sha512-FR5QJe+TaoZ2GsMHkjuwoNabr+UrJNRr2HNOo+r/7vhcuntM6Ee/pRPOnRhhL2XE9OOvX9VLEq+BcXl3VjNoWA== + version "16.11.41" + resolved "https://registry.npmjs.org/@types/node/-/node-16.11.41.tgz#88eb485b1bfdb4c224d878b7832239536aa2f813" + integrity sha512-mqoYK2TnVjdkGk8qXAVGc/x9nSaTpSrFaGFm43BUH3IdoBV0nta6hYaGmdOvIMlbHJbUEVen3gvwpwovAZKNdQ== "@types/normalize-package-data@^2.4.0": version "2.4.1" @@ -6139,9 +6472,9 @@ "@types/passport" "*" "@types/passport@*", "@types/passport@^1.0.3": - version "1.0.7" - resolved "https://registry.npmjs.org/@types/passport/-/passport-1.0.7.tgz#85892f14932168158c86aecafd06b12f5439467a" - integrity sha512-JtswU8N3kxBYgo+n9of7C97YQBT+AYPP2aBfNGTzABqPAZnK/WOAaKfh3XesUYMZRrXFuoPc2Hv0/G/nQFveHw== + version "1.0.9" + resolved "https://registry.npmjs.org/@types/passport/-/passport-1.0.9.tgz#b32fa8f7485dace77a9b58e82d0c92908f6e8387" + integrity sha512-9+ilzUhmZQR4JP49GdC2O4UdDE3POPLwpmaTC/iLkW7l0TZCXOo1zsTnnlXPq6rP1UsUZPfbAV4IUdiwiXyC7g== dependencies: "@types/express" "*" @@ -6209,9 +6542,9 @@ "@types/react" "*" "@types/react-syntax-highlighter@^15.0.0": - version "15.5.1" - resolved "https://registry.npmjs.org/@types/react-syntax-highlighter/-/react-syntax-highlighter-15.5.1.tgz#0c459cdd94a882df05778e93a1514430cc641043" - integrity sha512-+yD6D8y21JqLf89cRFEyRfptVMqo2ROHyAlysRvFwT28gT5gDo3KOiXHwGilHcq9y/OKTjlWK0f/hZUicrBFPQ== + version "15.5.2" + resolved "https://registry.npmjs.org/@types/react-syntax-highlighter/-/react-syntax-highlighter-15.5.2.tgz#b3450851c11b8526a27edf51d00769b69d8fbf20" + integrity sha512-cJJvwU8lQv/efGSo/LmPoaOqWi/B0AG4CNKKCn7HPUL25SqiPn1Vl+fV1JiUigJv97ruTZ8mo08+b8/0zoYufA== dependencies: "@types/react" "*" @@ -6334,14 +6667,14 @@ integrity sha512-EaCxbanVeyxDRTQBkdLb3Bvl/HK7PBK6UJjsSixB0iHKoWxE5uu2Q/DgtpOhPIojN0Zl1whvOd7PoHs2P0s5eA== "@types/semver@^6.0.0": - version "6.2.1" - resolved "https://registry.npmjs.org/@types/semver/-/semver-6.2.1.tgz#a236185670a7860f1597cf73bea2e16d001461ba" - integrity sha512-+beqKQOh9PYxuHvijhVl+tIHvT6tuwOrE9m14zd+MT2A38KoKZhh7pYJ0SNleLtwDsiIxHDsIk9bv01oOxvSvA== + version "6.2.3" + resolved "https://registry.npmjs.org/@types/semver/-/semver-6.2.3.tgz#5798ecf1bec94eaa64db39ee52808ec0693315aa" + integrity sha512-KQf+QAMWKMrtBMsB8/24w53tEsxllMj6TuA80TT/5igJalLI/zm0L3oXRbIAl4Ohfc85gyHX/jhMwsVkmhLU4A== "@types/semver@^7.3.8": - version "7.3.9" - resolved "https://registry.npmjs.org/@types/semver/-/semver-7.3.9.tgz#152c6c20a7688c30b967ec1841d31ace569863fc" - integrity sha512-L/TMpyURfBkf+o/526Zb6kd/tchUP3iBDEPjqjb+U2MAJhVRxxrmr2fwpe08E7QsV7YLcpq0tUaQ9O9x97ZIxQ== + version "7.3.10" + resolved "https://registry.npmjs.org/@types/semver/-/semver-7.3.10.tgz#5f19ee40cbeff87d916eedc8c2bfe2305d957f73" + integrity sha512-zsv3fsC7S84NN6nPK06u79oWgrPVd0NvOyqgghV1haPaFcVxIrP4DLomRwGAXk0ui4HZA7mOcSFL98sMVW9viw== "@types/serve-handler@^6.1.0": version "6.1.1" @@ -6365,6 +6698,14 @@ "@types/mime" "^1" "@types/node" "*" +"@types/serve-static@^1.13.10": + version "1.13.10" + resolved "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz#f5e0ce8797d2d7cc5ebeda48a52c96c4fa47a8d9" + integrity sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ== + dependencies: + "@types/mime" "^1" + "@types/node" "*" + "@types/set-cookie-parser@^2.4.0": version "2.4.0" resolved "https://registry.npmjs.org/@types/set-cookie-parser/-/set-cookie-parser-2.4.0.tgz#10cc0446bad372827671a5195fbd14ebce4a9baf" @@ -6490,9 +6831,9 @@ terser-webpack-plugin "*" "@types/testing-library__jest-dom@^5.9.1": - version "5.14.3" - resolved "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.3.tgz#ee6c7ffe9f8595882ee7bda8af33ae7b8789ef17" - integrity sha512-oKZe+Mf4ioWlMuzVBaXQ9WDnEm1+umLx0InILg+yvZVBBDmzV5KfZyLrCvadtWcx8+916jLmHafcmqqffl+iIw== + version "5.14.4" + resolved "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.4.tgz#21567ec845f4efee55923842c79728dc49c1650b" + integrity sha512-EUCs9PTBOEyfRtLKkKd31YrRCx/9Wxjy2Uqb6IH/KAOr7/vP0i8iClOyxQqjm/UxMGU5r5s2vOBM7vSPQVmabg== dependencies: "@types/jest" "*" @@ -6551,9 +6892,9 @@ "@types/node" "*" "@types/webpack-env@^1.15.2", "@types/webpack-env@^1.15.3": - version "1.16.4" - resolved "https://registry.npmjs.org/@types/webpack-env/-/webpack-env-1.16.4.tgz#1f4969042bf76d7ef7b5914f59b3b60073f4e1f4" - integrity sha512-llS8qveOUX3wxHnSykP5hlYFFuMfJ9p5JvIyCiBgp7WTfl6K5ZcyHj8r8JsN/J6QODkAsRRCLIcTuOCu8etkUw== + version "1.17.0" + resolved "https://registry.npmjs.org/@types/webpack-env/-/webpack-env-1.17.0.tgz#f99ce359f1bfd87da90cc4a57cab0a18f34a48d0" + integrity sha512-eHSaNYEyxRA5IAG0Ym/yCyf86niZUIF/TpWKofQI/CVfh5HsMEUyfE2kwFxha4ow0s5g0LfISQxpDKjbRDrizw== "@types/webpack@^5.28.0": version "5.28.0" @@ -6627,13 +6968,13 @@ integrity sha512-fbF6oTd4sGGy0xjHPKAt+eS2CrxJ3+6gQ3FGcBoIJR2TLAyCkCyI8JqZNy+FeON0AhVgNJoUumVoZQjBFUqHkw== "@typescript-eslint/eslint-plugin@^5.9.0": - version "5.24.0" - resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.24.0.tgz#59f9ed21fc4490444b75b250e4ce81e4b7bba18f" - integrity sha512-6bqFGk6wa9+6RrU++eLknKyDqXU1Oc8nyoLu5a1fU17PNRJd9UBr56rMF7c4DRaRtnarlkQ4jwxUbvBo8cNlpw== + version "5.27.1" + resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.27.1.tgz#fdf59c905354139046b41b3ed95d1609913d0758" + integrity sha512-6dM5NKT57ZduNnJfpY81Phe9nc9wolnMCnknb1im6brWi1RYv84nbMS3olJa27B6+irUVV1X/Wb+Am0FjJdGFw== dependencies: - "@typescript-eslint/scope-manager" "5.24.0" - "@typescript-eslint/type-utils" "5.24.0" - "@typescript-eslint/utils" "5.24.0" + "@typescript-eslint/scope-manager" "5.27.1" + "@typescript-eslint/type-utils" "5.27.1" + "@typescript-eslint/utils" "5.27.1" debug "^4.3.4" functional-red-black-tree "^1.0.1" ignore "^5.2.0" @@ -6654,14 +6995,14 @@ eslint-utils "^3.0.0" "@typescript-eslint/parser@^5.9.0": - version "5.23.0" - resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.23.0.tgz#443778e1afc9a8ff180f91b5e260ac3bec5e2de1" - integrity sha512-V06cYUkqcGqpFjb8ttVgzNF53tgbB/KoQT/iB++DOIExKmzI9vBJKjZKt/6FuV9c+zrDsvJKbJ2DOCYwX91cbw== + version "5.27.1" + resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.27.1.tgz#3a4dcaa67e45e0427b6ca7bb7165122c8b569639" + integrity sha512-7Va2ZOkHi5NP+AZwb5ReLgNF6nWLGTeUJfxdkVUAPPSaAdbWNnFZzLZ4EGGmmiCTg+AwlbE1KyUYTBglosSLHQ== dependencies: - "@typescript-eslint/scope-manager" "5.23.0" - "@typescript-eslint/types" "5.23.0" - "@typescript-eslint/typescript-estree" "5.23.0" - debug "^4.3.2" + "@typescript-eslint/scope-manager" "5.27.1" + "@typescript-eslint/types" "5.27.1" + "@typescript-eslint/typescript-estree" "5.27.1" + debug "^4.3.4" "@typescript-eslint/scope-manager@5.20.0": version "5.20.0" @@ -6671,21 +7012,13 @@ "@typescript-eslint/types" "5.20.0" "@typescript-eslint/visitor-keys" "5.20.0" -"@typescript-eslint/scope-manager@5.23.0": - version "5.23.0" - resolved "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.23.0.tgz#4305e61c2c8e3cfa3787d30f54e79430cc17ce1b" - integrity sha512-EhjaFELQHCRb5wTwlGsNMvzK9b8Oco4aYNleeDlNuL6qXWDF47ch4EhVNPh8Rdhf9tmqbN4sWDk/8g+Z/J8JVw== +"@typescript-eslint/scope-manager@5.27.1": + version "5.27.1" + resolved "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.27.1.tgz#4d1504392d01fe5f76f4a5825991ec78b7b7894d" + integrity sha512-fQEOSa/QroWE6fAEg+bJxtRZJTH8NTskggybogHt4H9Da8zd4cJji76gA5SBlR0MgtwF7rebxTbDKB49YUCpAg== dependencies: - "@typescript-eslint/types" "5.23.0" - "@typescript-eslint/visitor-keys" "5.23.0" - -"@typescript-eslint/scope-manager@5.24.0": - version "5.24.0" - resolved "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.24.0.tgz#ac8c4d65064204b596909c204fe9b7e30c3f51b2" - integrity sha512-WpMWipcDzGmMzdT7NtTjRXFabx10WleLUGrJpuJLGaxSqpcyq5ACpKSD5VE40h2nz3melQ91aP4Du7lh9FliCA== - dependencies: - "@typescript-eslint/types" "5.24.0" - "@typescript-eslint/visitor-keys" "5.24.0" + "@typescript-eslint/types" "5.27.1" + "@typescript-eslint/visitor-keys" "5.27.1" "@typescript-eslint/scope-manager@5.9.0": version "5.9.0" @@ -6695,12 +7028,12 @@ "@typescript-eslint/types" "5.9.0" "@typescript-eslint/visitor-keys" "5.9.0" -"@typescript-eslint/type-utils@5.24.0": - version "5.24.0" - resolved "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.24.0.tgz#aad47227e89cceec9175b1a67df69ebcd79f9421" - integrity sha512-uGi+sQiM6E5CeCZYBXiaIvIChBXru4LZ1tMoeKbh1Lze+8BO9syUG07594C4lvN2YPT4KVeIupOJkVI+9/DAmQ== +"@typescript-eslint/type-utils@5.27.1": + version "5.27.1" + resolved "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.27.1.tgz#369f695199f74c1876e395ebea202582eb1d4166" + integrity sha512-+UC1vVUWaDHRnC2cQrCJ4QtVjpjjCgjNFpg8b03nERmkHv9JV9X5M19D7UFMd+/G7T/sgFwX2pGmWK38rqyvXw== dependencies: - "@typescript-eslint/utils" "5.24.0" + "@typescript-eslint/utils" "5.27.1" debug "^4.3.4" tsutils "^3.21.0" @@ -6709,15 +7042,10 @@ resolved "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.20.0.tgz#fa39c3c2aa786568302318f1cb51fcf64258c20c" integrity sha512-+d8wprF9GyvPwtoB4CxBAR/s0rpP25XKgnOvMf/gMXYDvlUC3rPFHupdTQ/ow9vn7UDe5rX02ovGYQbv/IUCbg== -"@typescript-eslint/types@5.23.0": - version "5.23.0" - resolved "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.23.0.tgz#8733de0f58ae0ed318dbdd8f09868cdbf9f9ad09" - integrity sha512-NfBsV/h4dir/8mJwdZz7JFibaKC3E/QdeMEDJhiAE3/eMkoniZ7MjbEMCGXw6MZnZDMN3G9S0mH/6WUIj91dmw== - -"@typescript-eslint/types@5.24.0": - version "5.24.0" - resolved "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.24.0.tgz#565ff94a4b89073265e27514dc65225d18aabe6c" - integrity sha512-Tpg1c3shTDgTmZd3qdUyd+16r/pGmVaVEbLs+ufuWP0EruVbUiEOmpBBQxBb9a8iPRxi8Rb2oiwOxuZJzSq11A== +"@typescript-eslint/types@5.27.1": + version "5.27.1" + resolved "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.27.1.tgz#34e3e629501349d38be6ae97841298c03a6ffbf1" + integrity sha512-LgogNVkBhCTZU/m8XgEYIWICD6m4dmEDbKXESCbqOXfKZxRKeqpiJXQIErv66sdopRKZPo5l32ymNqibYEH/xg== "@typescript-eslint/types@5.9.0": version "5.9.0" @@ -6737,26 +7065,13 @@ semver "^7.3.5" tsutils "^3.21.0" -"@typescript-eslint/typescript-estree@5.23.0": - version "5.23.0" - resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.23.0.tgz#dca5f10a0a85226db0796e8ad86addc9aee52065" - integrity sha512-xE9e0lrHhI647SlGMl+m+3E3CKPF1wzvvOEWnuE3CCjjT7UiRnDGJxmAcVKJIlFgK6DY9RB98eLr1OPigPEOGg== +"@typescript-eslint/typescript-estree@5.27.1": + version "5.27.1" + resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.27.1.tgz#7621ee78607331821c16fffc21fc7a452d7bc808" + integrity sha512-DnZvvq3TAJ5ke+hk0LklvxwYsnXpRdqUY5gaVS0D4raKtbznPz71UJGnPTHEFo0GDxqLOLdMkkmVZjSpET1hFw== dependencies: - "@typescript-eslint/types" "5.23.0" - "@typescript-eslint/visitor-keys" "5.23.0" - debug "^4.3.2" - globby "^11.0.4" - is-glob "^4.0.3" - semver "^7.3.5" - tsutils "^3.21.0" - -"@typescript-eslint/typescript-estree@5.24.0": - version "5.24.0" - resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.24.0.tgz#30b9cbc1af475b9e02aca34cfe9ba9e1bb820143" - integrity sha512-zcor6vQkQmZAQfebSPVwUk/FD+CvnsnlfKXYeQDsWXRF+t7SBPmIfNia/wQxCSeu1h1JIjwV2i9f5/DdSp/uDw== - dependencies: - "@typescript-eslint/types" "5.24.0" - "@typescript-eslint/visitor-keys" "5.24.0" + "@typescript-eslint/types" "5.27.1" + "@typescript-eslint/visitor-keys" "5.27.1" debug "^4.3.4" globby "^11.1.0" is-glob "^4.0.3" @@ -6776,15 +7091,15 @@ semver "^7.3.5" tsutils "^3.21.0" -"@typescript-eslint/utils@5.24.0": - version "5.24.0" - resolved "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.24.0.tgz#7a854028040a305ddea129328e45cfb2c6406e75" - integrity sha512-K05sbWoeCBJH8KXu6hetBJ+ukG0k2u2KlgD3bN+v+oBKm8adJqVHpSSLHNzqyuv0Lh4GVSAUgZ5lB4icmPmWLw== +"@typescript-eslint/utils@5.27.1": + version "5.27.1" + resolved "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.27.1.tgz#b4678b68a94bc3b85bf08f243812a6868ac5128f" + integrity sha512-mZ9WEn1ZLDaVrhRaYgzbkXBkTPghPFsup8zDbbsYTxC5OmqrFE7skkKS/sraVsLP3TcT3Ki5CSyEFBRkLH/H/w== dependencies: "@types/json-schema" "^7.0.9" - "@typescript-eslint/scope-manager" "5.24.0" - "@typescript-eslint/types" "5.24.0" - "@typescript-eslint/typescript-estree" "5.24.0" + "@typescript-eslint/scope-manager" "5.27.1" + "@typescript-eslint/types" "5.27.1" + "@typescript-eslint/typescript-estree" "5.27.1" eslint-scope "^5.1.1" eslint-utils "^3.0.0" @@ -6808,20 +7123,12 @@ "@typescript-eslint/types" "5.20.0" eslint-visitor-keys "^3.0.0" -"@typescript-eslint/visitor-keys@5.23.0": - version "5.23.0" - resolved "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.23.0.tgz#057c60a7ca64667a39f991473059377a8067c87b" - integrity sha512-Vd4mFNchU62sJB8pX19ZSPog05B0Y0CE2UxAZPT5k4iqhRYjPnqyY3woMxCd0++t9OTqkgjST+1ydLBi7e2Fvg== +"@typescript-eslint/visitor-keys@5.27.1": + version "5.27.1" + resolved "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.27.1.tgz#05a62666f2a89769dac2e6baa48f74e8472983af" + integrity sha512-xYs6ffo01nhdJgPieyk7HAOpjhTsx7r/oB9LWEhwAXgwn33tkr+W8DI2ChboqhZlC4q3TC6geDYPoiX8ROqyOQ== dependencies: - "@typescript-eslint/types" "5.23.0" - eslint-visitor-keys "^3.0.0" - -"@typescript-eslint/visitor-keys@5.24.0": - version "5.24.0" - resolved "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.24.0.tgz#bb3e9a788ccd50c53e03557e4e203599880c3123" - integrity sha512-qzGwSXMyMnogcAo+/2fU+jhlPPVMXlIH2PeAonIKjJSoDKl1+lJVvG5Z5Oud36yU0TWK2cs1p/FaSN5J2OUFYA== - dependencies: - "@typescript-eslint/types" "5.24.0" + "@typescript-eslint/types" "5.27.1" eslint-visitor-keys "^3.3.0" "@typescript-eslint/visitor-keys@5.9.0": @@ -6964,7 +7271,7 @@ "@webassemblyjs/ast" "1.11.1" "@xtuc/long" "4.2.2" -"@xmldom/xmldom@^0.7.0", "@xmldom/xmldom@^0.7.2", "@xmldom/xmldom@^0.7.5": +"@xmldom/xmldom@^0.7.0", "@xmldom/xmldom@^0.7.5": version "0.7.5" resolved "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.7.5.tgz#09fa51e356d07d0be200642b0e4f91d8e6dd408d" integrity sha512-V3BIhmY36fXZ1OtVcI9W+FxQqxVLsPKcNjWigIaa81dLC9IolJl5Mt4Cvhmr0flUnjSpTdrbMTSbXqYqV5dT6A== @@ -6990,9 +7297,9 @@ integrity sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ== "@yarnpkg/parsers@^3.0.0-rc.4": - version "3.0.0-rc.4" - resolved "https://registry.npmjs.org/@yarnpkg/parsers/-/parsers-3.0.0-rc.4.tgz#d7b19fa22ce7ff2423e5cbf008f7be0a85e9f1e3" - integrity sha512-ScXXCUwGdx+aEIP20U8VEGtuXmxcMPFdJTb9G9a8MRpQPxIkky/GYXEL5Hf4oqJJXGyCv/DN31zfmxyj31SLKw== + version "3.0.0-rc.9" + resolved "https://registry.npmjs.org/@yarnpkg/parsers/-/parsers-3.0.0-rc.9.tgz#2d284e4e0c79b1c4e410465e217fa303d98b7be3" + integrity sha512-JMBE+6OJoNN9AXBzZ72u22/t9M25K8KgUWZIjvk8CU/NsE/m946l8D7SqMhbi3ZaUfYa5QitqCqVWTTFtysGJQ== dependencies: js-yaml "^3.10.0" tslib "^1.13.0" @@ -7073,12 +7380,12 @@ acorn@^7.1.1: resolved "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== -acorn@^8.2.4, acorn@^8.4.1, acorn@^8.5.0, acorn@^8.7.0: +acorn@^8.2.4, acorn@^8.5.0, acorn@^8.7.0: version "8.7.0" resolved "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz#90951fde0f8f09df93549481e5fc141445b791cf" integrity sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ== -acorn@^8.7.1: +acorn@^8.4.1, acorn@^8.7.1: version "8.7.1" resolved "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz#0197122c843d1bf6d0a5e83220a788f278f63c30" integrity sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A== @@ -7114,6 +7421,15 @@ agentkeepalive@^4.1.3, agentkeepalive@^4.1.4, agentkeepalive@^4.2.0: depd "^1.1.2" humanize-ms "^1.2.1" +agentkeepalive@^4.2.1: + version "4.2.1" + resolved "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.2.1.tgz#a7975cbb9f83b367f06c90cc51ff28fe7d499717" + integrity sha512-Zn4cw2NEqd+9fiSVWMscnjyQ1a8Yfoc5oBajLeo5w+YBHgDUcEBY2hS4YpTz6iN5f/2zQiktcuM6tS8x1p9dpA== + dependencies: + debug "^4.1.0" + depd "^1.1.2" + humanize-ms "^1.2.1" + aggregate-error@^3.0.0, aggregate-error@^3.1.0: version "3.1.0" resolved "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" @@ -7185,10 +7501,10 @@ ansi-align@^3.0.0: dependencies: string-width "^3.0.0" -ansi-colors@^4.1.1: - version "4.1.1" - resolved "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" - integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== +ansi-colors@^4.1.1, ansi-colors@^4.1.3: + version "4.1.3" + resolved "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b" + integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw== ansi-escapes@^3.0.0: version "3.2.0" @@ -7284,12 +7600,12 @@ anymatch@^3.0.3, anymatch@~3.1.2: normalize-path "^3.0.0" picomatch "^2.0.4" -apollo-datasource@^3.3.1: - version "3.3.1" - resolved "https://registry.npmjs.org/apollo-datasource/-/apollo-datasource-3.3.1.tgz#a1168dd68371930de3ed4245ad12fa8600efe2cc" - integrity sha512-Z3a8rEUXVPIZ1p8xrFL8bcNhWmhOmovgDArvwIwmJOBnh093ZpRfO+ESJEDAN4KswmyzCLDAwjsW4zQOONdRUw== +apollo-datasource@^3.3.2: + version "3.3.2" + resolved "https://registry.npmjs.org/apollo-datasource/-/apollo-datasource-3.3.2.tgz#5711f8b38d4b7b53fb788cb4dbd4a6a526ea74c8" + integrity sha512-L5TiS8E2Hn/Yz7SSnWIVbZw0ZfEIXZCa5VUiVxD9P53JvSrf4aStvsFDlGWPvpIdCR+aly2CfoB79B9/JjKFqg== dependencies: - apollo-server-caching "^3.3.0" + "@apollo/utils.keyvaluecache" "^1.0.1" apollo-server-env "^4.2.1" apollo-reporting-protobuf@^3.3.1: @@ -7299,35 +7615,28 @@ apollo-reporting-protobuf@^3.3.1: dependencies: "@apollo/protobufjs" "1.2.2" -apollo-server-caching@^3.3.0: - version "3.3.0" - resolved "https://registry.npmjs.org/apollo-server-caching/-/apollo-server-caching-3.3.0.tgz#f501cbeb820a4201d98c2b768c085f22848d9dc5" - integrity sha512-Wgcb0ArjZ5DjQ7ID+tvxUcZ7Yxdbk5l1MxZL8D8gkyjooOkhPNzjRVQ7ubPoXqO54PrOMOTm1ejVhsF+AfIirQ== - dependencies: - lru-cache "^6.0.0" - -apollo-server-core@^3.7.0: - version "3.7.0" - resolved "https://registry.npmjs.org/apollo-server-core/-/apollo-server-core-3.7.0.tgz#7ec060d269d73d6761cd1eb3d419436e09fa6974" - integrity sha512-xUCDjrBzPVbttbh/HenuQdivco/dcXE2oIDYwCU6FU2RBXqxWFmuCl2Xe7VPA/5Frw/4snJDLCyVte9PA5edww== +apollo-server-core@^3.9.0: + version "3.9.0" + resolved "https://registry.npmjs.org/apollo-server-core/-/apollo-server-core-3.9.0.tgz#44b39e378314cfc0596be7003d3f1f1397c88eea" + integrity sha512-WS54C33cTriDaBIcj7ijWv/fgeJICcrQKlP1Cn6pnZp/eumpMraezLeJ3gFWAXprOuR2E3fZe64lNlup0fMu8w== dependencies: + "@apollo/utils.keyvaluecache" "^1.0.1" "@apollo/utils.logger" "^1.0.0" + "@apollo/utils.usagereporting" "^1.0.0" "@apollographql/apollo-tools" "^0.5.3" "@apollographql/graphql-playground-html" "1.6.29" "@graphql-tools/mock" "^8.1.2" "@graphql-tools/schema" "^8.0.0" "@josephg/resolvable" "^1.0.0" - apollo-datasource "^3.3.1" + apollo-datasource "^3.3.2" apollo-reporting-protobuf "^3.3.1" - apollo-server-caching "^3.3.0" apollo-server-env "^4.2.1" apollo-server-errors "^3.3.1" - apollo-server-plugin-base "^3.5.3" - apollo-server-types "^3.5.3" + apollo-server-plugin-base "^3.6.1" + apollo-server-types "^3.6.1" async-retry "^1.2.1" fast-json-stable-stringify "^2.1.0" graphql-tag "^2.11.0" - lodash.sortby "^4.7.0" loglevel "^1.6.8" lru-cache "^6.0.0" sha.js "^2.4.11" @@ -7346,46 +7655,48 @@ apollo-server-errors@^3.3.1: resolved "https://registry.npmjs.org/apollo-server-errors/-/apollo-server-errors-3.3.1.tgz#ba5c00cdaa33d4cbd09779f8cb6f47475d1cd655" integrity sha512-xnZJ5QWs6FixHICXHxUfm+ZWqqxrNuPlQ+kj5m6RtEgIpekOPssH/SD9gf2B4HuWV0QozorrygwZnux8POvyPA== -apollo-server-express@^3.0.0, apollo-server-express@^3.7.0: - version "3.7.0" - resolved "https://registry.npmjs.org/apollo-server-express/-/apollo-server-express-3.7.0.tgz#dc4cacc25f9528d3a18e2557b52990bf000b454c" - integrity sha512-176LSK7YBxwfleurtbfr5SYMheNNJSHrQa2h4QuosLqhfFJkfTpI2iBW56N737U47QfyueCOvkjNZVq86e3n2g== +apollo-server-express@^3.0.0, apollo-server-express@^3.9.0: + version "3.9.0" + resolved "https://registry.npmjs.org/apollo-server-express/-/apollo-server-express-3.9.0.tgz#1ff3b53fe76e4e8be04b8477ea8a3d9586313af1" + integrity sha512-scSeHy9iB7W3OiF3uLQEzad9Jm9tEfDF8ACsJb2P+xX69uqg6zizsrQvj3qRhazCO7FKMcMu9zQFR0hy7zKbUA== dependencies: "@types/accepts" "^1.3.5" "@types/body-parser" "1.19.2" "@types/cors" "2.8.12" "@types/express" "4.17.13" - "@types/express-serve-static-core" "4.17.28" + "@types/express-serve-static-core" "4.17.29" accepts "^1.3.5" - apollo-server-core "^3.7.0" - apollo-server-types "^3.5.3" + apollo-server-core "^3.9.0" + apollo-server-types "^3.6.1" body-parser "^1.19.0" cors "^2.8.5" parseurl "^1.3.3" -apollo-server-plugin-base@^3.5.3: - version "3.5.3" - resolved "https://registry.npmjs.org/apollo-server-plugin-base/-/apollo-server-plugin-base-3.5.3.tgz#620ca8e9337e5d661c3915375bd6720d0aa205ce" - integrity sha512-zojm3qiUGYtM5k1PPrCJnLZSDNqvWvmIDvqBjCu3wI3iNZqNm3MOA86eYGFfaBi/WNu3qYIj6QE3T7w0XjRV1A== +apollo-server-plugin-base@^3.6.1: + version "3.6.1" + resolved "https://registry.npmjs.org/apollo-server-plugin-base/-/apollo-server-plugin-base-3.6.1.tgz#33e9f26433d5a8b8ed5d27e9fa88de9ef0c2c704" + integrity sha512-bFpxzWO0LTTtSAkGVBeaAtnQXJ5ZCi8eaLN/eMSju8RByifmF3Kr6gAqcOZhOH/geQEt3Y6G8n3bR0eHTGxljQ== dependencies: - apollo-server-types "^3.5.3" + apollo-server-types "^3.6.1" -apollo-server-types@^3.5.3: - version "3.5.3" - resolved "https://registry.npmjs.org/apollo-server-types/-/apollo-server-types-3.5.3.tgz#e874bd99135c5c6550fe5f904b3b521b340e6ea3" - integrity sha512-Qf5mMVTDyABEeyjGecwMsk0y0km4KuW8/j/UwBDQkAAW1QRy+w8nqi+wvSoA5hNXiYCdJN4U4nxTxm9+2eiT4w== +apollo-server-types@^3.6.1: + version "3.6.1" + resolved "https://registry.npmjs.org/apollo-server-types/-/apollo-server-types-3.6.1.tgz#704e5309bd947306030df01f982e36d1d4753eaa" + integrity sha512-XOPlBlRdwP00PrG03OffGGWuzyei+J9t1rAnvyHsSdP0JCgQWigHJfvL1N9Bhgi4UTjl9JadKOJh1znLNlqIFQ== dependencies: + "@apollo/utils.keyvaluecache" "^1.0.1" + "@apollo/utils.logger" "^1.0.0" apollo-reporting-protobuf "^3.3.1" - apollo-server-caching "^3.3.0" apollo-server-env "^4.2.1" apollo-server@^3.0.0: - version "3.7.0" - resolved "https://registry.npmjs.org/apollo-server/-/apollo-server-3.7.0.tgz#8755c1e1f08825a61003667551e5915373db9f87" - integrity sha512-sMD9V0cScsuXKtqtpLBpeU3s0eRyG12n3HtHGg9LNBanYkxS4/I1i0JozO0r0e2kO5yyJ1gc6UWim9+NDxUrnA== + version "3.9.0" + resolved "https://registry.npmjs.org/apollo-server/-/apollo-server-3.9.0.tgz#391b60c4c24d37c65855cccc8aa886e684bc1776" + integrity sha512-g80gXDuK8fl2W0fQF/hEyeoO9AU+sO2gBzeJAYUyGLotYc+oL/Y3mTRk5GB8C7cXUXCg5uvWbUj8va0E5UZE7w== dependencies: - apollo-server-core "^3.7.0" - apollo-server-express "^3.7.0" + "@types/express" "4.17.13" + apollo-server-core "^3.9.0" + apollo-server-express "^3.9.0" express "^4.17.1" aproba@^1.0.3: @@ -7547,6 +7858,17 @@ array-includes@^3.1.2, array-includes@^3.1.3, array-includes@^3.1.4: get-intrinsic "^1.1.1" is-string "^1.0.7" +array-includes@^3.1.5: + version "3.1.5" + resolved "https://registry.npmjs.org/array-includes/-/array-includes-3.1.5.tgz#2c320010db8d31031fd2a5f6b3bbd4b1aad31bdb" + integrity sha512-iSDYZMMyTPkiFasVqfuAQnWAYcvO/SeBSCGKePoEthjp4LEMTe4uLc7b025o4jAZpHhihh8xPo99TNWUWWkGDQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.19.5" + get-intrinsic "^1.1.1" + is-string "^1.0.7" + array-union@^1.0.1: version "1.0.2" resolved "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" @@ -7569,6 +7891,16 @@ array-unique@^0.3.2: resolved "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= +array.prototype.flat@^1.2.3: + version "1.3.0" + resolved "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.0.tgz#0b0c1567bf57b38b56b4c97b8aa72ab45e4adc7b" + integrity sha512-12IUEkHsAhA4DY5s0FPgNXIdc8VRSqD9Zp78a5au9abH/SOBrsp082JOWFNTjkMozh8mqcdiKuaLGhPeYztxSw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.2" + es-shim-unscopables "^1.0.0" + array.prototype.flat@^1.2.5: version "1.2.5" resolved "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz#07e0975d84bbc7c48cd1879d609e682598d33e13" @@ -7578,14 +7910,15 @@ array.prototype.flat@^1.2.5: define-properties "^1.1.3" es-abstract "^1.19.0" -array.prototype.flatmap@^1.2.5: - version "1.2.5" - resolved "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.2.5.tgz#908dc82d8a406930fdf38598d51e7411d18d4446" - integrity sha512-08u6rVyi1Lj7oqWbS9nUxliETrtIROT4XGTA4D/LWGten6E3ocm7cy9SIrmNHOL5XVbVuckUp3X6Xyg8/zpvHA== +array.prototype.flatmap@^1.3.0: + version "1.3.0" + resolved "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.0.tgz#a7e8ed4225f4788a70cd910abcf0791e76a5534f" + integrity sha512-PZC9/8TKAIxcWKdyeb77EzULHPrIX/tIZebLJUQOMR1OwYosT8yggdfWScfTBCDj5utONvOuPQQumYsU2ULbkg== dependencies: - call-bind "^1.0.0" + call-bind "^1.0.2" define-properties "^1.1.3" - es-abstract "^1.19.0" + es-abstract "^1.19.2" + es-shim-unscopables "^1.0.0" arraybuffer.slice@~0.0.7: version "0.0.7" @@ -7595,7 +7928,7 @@ arraybuffer.slice@~0.0.7: arrify@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" - integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0= + integrity sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA== arrify@^2.0.0, arrify@^2.0.1: version "2.0.1" @@ -7724,9 +8057,9 @@ aws-sdk-mock@^5.2.1: traverse "^0.6.6" aws-sdk@^2.1122.0, aws-sdk@^2.840.0, aws-sdk@^2.948.0: - version "2.1137.0" - resolved "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1137.0.tgz#505e51b3c9ed31d54bbd620cb70d005e6086f23b" - integrity sha512-iOm81glo7Y3bj6bO6pUEhFlclfANe0ExBJOZ8TxFx2mE8KsaPkofAy8dSzwbn357yOyVxur9tkZgXM2f05ynlA== + version "2.1158.0" + resolved "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1158.0.tgz#924fb6dda1acecb1dc1c8901095cf64d6acdade4" + integrity sha512-uHYzZMGE+b50sWXaLhga4aD1SpB3+DEZclAkg9aYz2pDZlSDTOMh3uJ/ufsMBs7VcDKGS7mQRibCmCbwRGTIlg== dependencies: buffer "4.9.2" events "1.1.1" @@ -7735,7 +8068,7 @@ aws-sdk@^2.1122.0, aws-sdk@^2.840.0, aws-sdk@^2.948.0: querystring "0.2.0" sax "1.2.1" url "0.10.3" - uuid "3.3.2" + uuid "8.0.0" xml2js "0.4.19" aws-sign2@~0.7.0: @@ -7765,7 +8098,14 @@ axios-cached-dns-resolve@0.5.2: pino "^5.12.2" pino-pretty "^2.6.0" -axios@^0.21.1, axios@^0.21.4: +axios@0.26.1: + version "0.26.1" + resolved "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz#1ede41c51fcf51bbbd6fd43669caaa4f0495aaa9" + integrity sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA== + dependencies: + follow-redirects "^1.14.8" + +axios@^0.21.1: version "0.21.4" resolved "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575" integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg== @@ -7856,6 +8196,22 @@ babel-plugin-polyfill-regenerator@^0.3.0: dependencies: "@babel/helper-define-polyfill-provider" "^0.3.1" +"babel-plugin-styled-components@>= 1.12.0": + version "2.0.7" + resolved "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-2.0.7.tgz#c81ef34b713f9da2b7d3f5550df0d1e19e798086" + integrity sha512-i7YhvPgVqRKfoQ66toiZ06jPNA3p6ierpfUuEWxNF+fV27Uv5gxBkf8KZLHUCc1nFA9j6+80pYoIpqCeyW3/bA== + dependencies: + "@babel/helper-annotate-as-pure" "^7.16.0" + "@babel/helper-module-imports" "^7.16.0" + babel-plugin-syntax-jsx "^6.18.0" + lodash "^4.17.11" + picomatch "^2.3.0" + +babel-plugin-syntax-jsx@^6.18.0: + version "6.18.0" + resolved "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz#0af32a9a6e13ca7a3fd5069e62d7b0f58d0d8946" + integrity sha512-qrPaCSo9c8RHNRHIotaufGbuOBN8rtdC4QrrFFc43vyWCCz7Kl7GL1PGaXtMGQZUXrkCjNEgxDfmAuAabr/rlw== + babel-plugin-syntax-trailing-function-commas@^7.0.0-beta.0: version "7.0.0-beta.0" resolved "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-7.0.0-beta.0.tgz#aa213c1435e2bffeb6fca842287ef534ad05d5cf" @@ -8069,6 +8425,11 @@ big.js@^5.2.2: resolved "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== +big.js@^6.1.1: + version "6.2.0" + resolved "https://registry.npmjs.org/big.js/-/big.js-6.2.0.tgz#39c60822aecb0f34a1d79a90fe9908a0ddf45e1d" + integrity sha512-paIKvJiAaOYdLt6MfnvxkDo64lTOV257XYJyX3oJnJQocIclUn+48k6ZerH/c5FxWE6DGJu1TKDYis7tqHg9kg== + bignumber.js@^9.0.0: version "9.0.1" resolved "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.1.tgz#8d7ba124c882bfd8e43260c67475518d0689e4e5" @@ -8297,6 +8658,11 @@ brorand@^1.0.1, brorand@^1.1.0: resolved "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= +browser-headers@^0.4.1: + version "0.4.1" + resolved "https://registry.npmjs.org/browser-headers/-/browser-headers-0.4.1.tgz#4308a7ad3b240f4203dbb45acedb38dc2d65dd02" + integrity sha512-CA9hsySZVo9371qEHjHZtYxV2cFtVj5Wj/ZHi8ooEsrtm4vOnl9Y9HmyYWk9q+05d7K3rdoAE0j3MVEFVvtQtg== + browser-process-hrtime@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" @@ -8464,6 +8830,13 @@ builtins@^1.0.3: resolved "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz#cb94faeb61c8696451db36534e1422f94f0aee88" integrity sha1-y5T662HIaWRR2zZTThQi+U8K7og= +builtins@^5.0.0: + version "5.0.1" + resolved "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz#87f6db9ab0458be728564fa81d876d8d74552fa9" + integrity sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ== + dependencies: + semver "^7.0.0" + byline@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/byline/-/byline-5.0.0.tgz#741c5216468eadc457b03410118ad77de8c1ddb1" @@ -8508,6 +8881,30 @@ cacache@^15.0.3, cacache@^15.0.5, cacache@^15.2.0, cacache@^15.3.0: tar "^6.0.2" unique-filename "^1.1.1" +cacache@^16.0.0, cacache@^16.0.6, cacache@^16.1.0: + version "16.1.1" + resolved "https://registry.npmjs.org/cacache/-/cacache-16.1.1.tgz#4e79fb91d3efffe0630d5ad32db55cc1b870669c" + integrity sha512-VDKN+LHyCQXaaYZ7rA/qtkURU+/yYhviUdvqEv2LT6QPZU8jpyzEkEVAcKlKLt5dJ5BRp11ym8lo3NKLluEPLg== + dependencies: + "@npmcli/fs" "^2.1.0" + "@npmcli/move-file" "^2.0.0" + chownr "^2.0.0" + fs-minipass "^2.1.0" + glob "^8.0.1" + infer-owner "^1.0.4" + lru-cache "^7.7.1" + minipass "^3.1.6" + minipass-collect "^1.0.2" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.4" + mkdirp "^1.0.4" + p-map "^4.0.0" + promise-inflight "^1.0.1" + rimraf "^3.0.2" + ssri "^9.0.0" + tar "^6.1.11" + unique-filename "^1.1.1" + cache-base@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" @@ -8624,6 +9021,11 @@ camelcase@^6.2.0, camelcase@^6.3.0: resolved "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== +camelize@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz#164a5483e630fa4321e5af07020e531831b2609b" + integrity sha512-W2lPwkBkMZwFlPCXhIlYgxu+7gC/NUlCtdK652DAJ1JdgV0sTrvuPFshNPrFa1TY2JOkLhgdeEBplB4ezEa+xg== + caniuse-api@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0" @@ -8691,6 +9093,14 @@ chalk@4.1.1: ansi-styles "^4.1.0" supports-color "^7.1.0" +chalk@4.1.2, chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.1, chalk@^4.1.2: + version "4.1.2" + resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + chalk@^1.0.0, chalk@^1.1.3: version "1.1.3" resolved "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" @@ -8710,14 +9120,6 @@ chalk@^3.0.0: ansi-styles "^4.1.0" supports-color "^7.1.0" -chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.1, chalk@^4.1.2: - version "4.1.2" - resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" - integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - change-case-all@1.0.14: version "1.0.14" resolved "https://registry.npmjs.org/change-case-all/-/change-case-all-1.0.14.tgz#bac04da08ad143278d0ac3dda7eccd39280bfba1" @@ -8824,7 +9226,7 @@ chokidar@^3.3.1, chokidar@^3.4.2, chokidar@^3.5.2, chokidar@^3.5.3: optionalDependencies: fsevents "~2.3.2" -chownr@^1.1.1, chownr@^1.1.4: +chownr@^1.1.1: version "1.1.4" resolved "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== @@ -8847,9 +9249,9 @@ ci-info@^2.0.0: integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== ci-info@^3.1.0, ci-info@^3.2.0: - version "3.3.0" - resolved "https://registry.npmjs.org/ci-info/-/ci-info-3.3.0.tgz#b4ed1fb6818dea4803a55c623041f9165d2066b2" - integrity sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw== + version "3.3.2" + resolved "https://registry.npmjs.org/ci-info/-/ci-info-3.3.2.tgz#6d2967ffa407466481c6c90b6e16b3098f080128" + integrity sha512-xmDt/QIAdeZ9+nfdPsaBCpMvHNLFiLdjj59qjqn+6iPe6YmHGQ35sBnQ8uslRBXFmXkiZQOJRjvQeoGppoTjjg== cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: version "1.0.4" @@ -9033,7 +9435,7 @@ clone@2.x, clone@^2.1.1, clone@^2.1.2: clone@^1.0.2: version "1.0.4" resolved "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" - integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= + integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg== cloneable-readable@^1.0.0: version "1.1.3" @@ -9066,6 +9468,13 @@ co@^4.6.0: resolved "https://registry.npmjs.org/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= +code-block-writer@^11.0.0: + version "11.0.0" + resolved "https://registry.npmjs.org/code-block-writer/-/code-block-writer-11.0.0.tgz#5956fb186617f6740e2c3257757fea79315dd7d4" + integrity sha512-GEqWvEWWsOvER+g9keO4ohFoD3ymwyCnqY3hoTr7GZipYFwEhMHJw+TtV0rfgRhNImM6QWZGO2XYjlJVyYT62w== + dependencies: + tslib "2.3.1" + code-error-fragment@0.0.230: version "0.0.230" resolved "https://registry.npmjs.org/code-error-fragment/-/code-error-fragment-0.0.230.tgz#d736d75c832445342eca1d1fedbf17d9618b14d7" @@ -9076,12 +9485,12 @@ code-point-at@^1.0.0: resolved "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= -codemirror-graphql@^1.3.0: - version "1.3.0" - resolved "https://registry.npmjs.org/codemirror-graphql/-/codemirror-graphql-1.3.0.tgz#6ca19eb2735dbfd5ac9db59b8fc4c8fc1e34fe43" - integrity sha512-Inqecp/PpUsNFz6+V6jpgQD1m7jjGg3yby60baw2t5yb2stBH8Z/6cHm/IYp9eN0Aq2EWqomd0GkGmiISPi4jQ== +codemirror-graphql@^1.3.2: + version "1.3.2" + resolved "https://registry.npmjs.org/codemirror-graphql/-/codemirror-graphql-1.3.2.tgz#e9d1d18b4a160f0016a28465805284636ee42d2a" + integrity sha512-glwFsEVlH5TvxjSKGymZ1sNy37f3Mes58CB4fXOd0zy9+JzDL08Wti1b5ycy4vFZYghMDK1/Or/zRSjMAGtC2w== dependencies: - graphql-language-service "^5.0.4" + graphql-language-service "^5.0.6" codemirror@^5.65.3: version "5.65.3" @@ -9133,7 +9542,7 @@ color-convert@^2.0.1: color-name@1.1.3: version "1.1.3" resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" - integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== color-name@^1.0.0, color-name@^1.1.4, color-name@~1.1.4: version "1.1.4" @@ -9148,7 +9557,7 @@ color-string@^1.5.2, color-string@^1.9.0: color-name "^1.0.0" simple-swizzle "^0.2.2" -color-support@^1.1.2: +color-support@^1.1.2, color-support@^1.1.3: version "1.1.3" resolved "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== @@ -9174,11 +9583,16 @@ colord@^2.9.1: resolved "https://registry.npmjs.org/colord/-/colord-2.9.1.tgz#c961ea0efeb57c9f0f4834458f26cb9cc4a3f90e" integrity sha512-4LBMSt09vR0uLnPVkOUBnmxgoaeN4ewRbx801wY/bXcltXfpR/G46OdWn96XpYmCWuYvO46aBZP4NgX8HpNAcw== -colorette@2.0.16, colorette@^2.0.10, colorette@^2.0.16: +colorette@2.0.16, colorette@^2.0.10: version "2.0.16" resolved "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz#713b9af84fdb000139f04546bd4a93f62a5085da" integrity sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g== +colorette@^2.0.16, colorette@^2.0.17: + version "2.0.19" + resolved "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz#cdf044f47ad41a0f4b56b3a0d5b4e6e1a2d5a798" + integrity sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ== + colors@1.0.3: version "1.0.3" resolved "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b" @@ -9232,7 +9646,7 @@ command-exists@^1.2.9: resolved "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz#c50725af3808c8ab0260fd60b01fbfa25b954f69" integrity sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w== -commander@*, commander@^8.3.0: +commander@*, commander@8.3.0, commander@^8.3.0: version "8.3.0" resolved "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== @@ -9262,10 +9676,10 @@ commander@^7.2.0: resolved "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== -commander@^9.1.0: - version "9.2.0" - resolved "https://registry.npmjs.org/commander/-/commander-9.2.0.tgz#6e21014b2ed90d8b7c9647230d8b7a94a4a419a9" - integrity sha512-e2i4wANQiSXgnrBlIatyHtP1odfUp0BbV5Y5nEGbxtIrStkEOAAzCUirvLBNXHLr7kwLvJl6V+4V3XV9x7Wd9w== +commander@^9.1.0, commander@^9.3.0: + version "9.3.0" + resolved "https://registry.npmjs.org/commander/-/commander-9.3.0.tgz#f619114a5a2d2054e0d9ff1b31d5ccf89255e26b" + integrity sha512-hv95iU5uXPbK83mjrJKuZyFM/LBAoCV/XhVGkS5Je6tl7sxr6A0ITMw5WoRV46/UaJ46Nllm3Xt7IaJhXTIkzw== common-ancestor-path@^1.0.1: version "1.0.1" @@ -9290,6 +9704,11 @@ compare-func@^2.0.0: array-ify "^1.0.0" dot-prop "^5.1.0" +compare-versions@4.1.3: + version "4.1.3" + resolved "https://registry.npmjs.org/compare-versions/-/compare-versions-4.1.3.tgz#8f7b8966aef7dc4282b45dfa6be98434fc18a1a4" + integrity sha512-WQfnbDcrYnGr55UwbxKiQKASnTtNnaAWVi8jZyy8NTpVAXWACSne8lMD1iaIo9AiU6mnuLvSVshCzewVuWxHUg== + component-bind@1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz#00c608ab7dcd93897c0009651b1d3a8e1e73bbd1" @@ -9389,15 +9808,29 @@ concat-with-sourcemaps@^1.1.0: dependencies: source-map "^0.6.1" -concurrently@^7.0.0: - version "7.2.0" - resolved "https://registry.npmjs.org/concurrently/-/concurrently-7.2.0.tgz#4d9b4d1e527b8a8cb101bc2aee317e09496fad43" - integrity sha512-4KIVY5HopDRhN3ndAgfFOLsMk1PZUPgghlgTMZ5Pb5aTrqYg86RcZaIZC2Cz+qpZ9DsX36WHGjvWnXPqdnblhw== +concurrently@6.5.1: + version "6.5.1" + resolved "https://registry.npmjs.org/concurrently/-/concurrently-6.5.1.tgz#4518c67f7ac680cf5c34d5adf399a2a2047edc8c" + integrity sha512-FlSwNpGjWQfRwPLXvJ/OgysbBxPkWpiVjy1042b0U7on7S7qwwMIILRj7WTN1mTgqa582bG6NFuScOoh6Zgdag== dependencies: chalk "^4.1.0" date-fns "^2.16.1" lodash "^4.17.21" rxjs "^6.6.3" + spawn-command "^0.0.2-1" + supports-color "^8.1.0" + tree-kill "^1.2.2" + yargs "^16.2.0" + +concurrently@^7.0.0: + version "7.2.2" + resolved "https://registry.npmjs.org/concurrently/-/concurrently-7.2.2.tgz#4ad4a4dfd3945f668d727379de2a29502e6a531c" + integrity sha512-DcQkI0ruil5BA/g7Xy3EWySGrFJovF5RYAYxwGvv9Jf9q9B1v3jPFP2tl6axExNf1qgF30kjoNYrangZ0ey4Aw== + dependencies: + chalk "^4.1.0" + date-fns "^2.16.1" + lodash "^4.17.21" + rxjs "^7.0.0" shell-quote "^1.7.3" spawn-command "^0.0.2-1" supports-color "^8.1.0" @@ -9412,7 +9845,7 @@ config-chain@^1.1.12: ini "^1.3.4" proto-list "~1.2.1" -configstore@^5.0.0, configstore@^5.0.1: +configstore@^5.0.1: version "5.0.1" resolved "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz#d365021b5df4b98cdd187d6a3b0e3f6a7cc5ed96" integrity sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA== @@ -9429,6 +9862,11 @@ connect-history-api-fallback@^1.6.0: resolved "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz#8b32089359308d111115d81cad3fceab888f97bc" integrity sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg== +consola@^2.15.0: + version "2.15.3" + resolved "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz#2e11f98d6a4be71ff72e0bdf07bd23e12cb61550" + integrity sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw== + console-browserify@^1.1.0: version "1.2.0" resolved "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336" @@ -9439,6 +9877,13 @@ console-control-strings@^1.0.0, console-control-strings@^1.1.0, console-control- resolved "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= +console.table@0.10.0: + version "0.10.0" + resolved "https://registry.npmjs.org/console.table/-/console.table-0.10.0.tgz#0917025588875befd70cf2eff4bef2c6e2d75d04" + integrity sha1-CRcCVYiHW+/XDPLv9L7yxuLXXQQ= + dependencies: + easy-table "1.1.0" + constant-case@^3.0.4: version "3.0.4" resolved "https://registry.npmjs.org/constant-case/-/constant-case-3.0.4.tgz#3b84a9aeaf4cf31ec45e6bf5de91bdfb0589faf1" @@ -9578,7 +10023,7 @@ cookie@0.4.1: resolved "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz#afd713fe26ebd21ba95ceb61f9a8116e50a537d1" integrity sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA== -cookie@0.4.2, cookie@^0.4.1, cookie@^0.4.2, cookie@~0.4.1: +cookie@0.4.2, cookie@^0.4.2, cookie@~0.4.1: version "0.4.2" resolved "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== @@ -9624,9 +10069,9 @@ core-js@^2.4.0, core-js@^2.5.0, core-js@^2.6.10: integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ== core-js@^3.4.1, core-js@^3.6.5: - version "3.22.5" - resolved "https://registry.npmjs.org/core-js/-/core-js-3.22.5.tgz#a5f5a58e663d5c0ebb4e680cd7be37536fb2a9cf" - integrity sha512-VP/xYuvJ0MJWRAobcmQ8F2H6Bsn+s7zqAAjFaHGBMc5AQm7zaelhD1LGduFn2EehEcQcU+br6t+fwbpQ5d1ZWA== + version "3.23.2" + resolved "https://registry.npmjs.org/core-js/-/core-js-3.23.2.tgz#e07a60ca8b14dd129cabdc3d2551baf5a01c76f0" + integrity sha512-ELJOWxNrJfOH/WK4VJ3Qd+fOqZuOuDNDJz0xG6Bt4mGg2eO/UT9CljCrbqDGovjLKUrGajEEBcoTOc0w+yBYeQ== core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" @@ -9734,17 +10179,17 @@ crelt@^1.0.5: resolved "https://registry.npmjs.org/crelt/-/crelt-1.0.5.tgz#57c0d52af8c859e354bace1883eb2e1eb182bb94" integrity sha512-+BO9wPPi+DWTDcNYhr/W90myha8ptzftZT+LwcmUbbok0rcP/fequmFYCw8NMoH7pkAZQzU78b3kYrlua5a9eA== -cron@^1.8.2: - version "1.8.2" - resolved "https://registry.npmjs.org/cron/-/cron-1.8.2.tgz#4ac5e3c55ba8c163d84f3407bde94632da8370ce" - integrity sha512-Gk2c4y6xKEO8FSAUTklqtfSr7oTq0CiPQeLBG5Fl0qoXpZyMcj1SG59YL+hqq04bu6/IuEA7lMkYDAplQNKkyg== +cron@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/cron/-/cron-2.0.0.tgz#15c6bf37c1cebf6da1d7a688b9ba1c68338bfe6b" + integrity sha512-RPeRunBCFr/WEo7WLp8Jnm45F/ziGJiHVvVQEBSDTSGu6uHW49b2FOP2O14DcXlGJRLhwE7TIoDzHHK4KmlL6g== dependencies: - moment-timezone "^0.5.x" + luxon "^1.23.x" cronstrue@^2.2.0: - version "2.5.0" - resolved "https://registry.npmjs.org/cronstrue/-/cronstrue-2.5.0.tgz#1d69bd53520ce536789fb666d9fd562065b491c6" - integrity sha512-2uhcYEmXEH52Prn1biZ1HSaQwGwUy4fxFiq3U3vKwLYngL14j8f4pZeQt9f1J6tWDaLFeLRgcCHtO45r78ECyw== + version "2.10.0" + resolved "https://registry.npmjs.org/cronstrue/-/cronstrue-2.10.0.tgz#9b57e9acc18eb44ebe9be5dc993753fd7d6d8e56" + integrity sha512-WCCaKuuzjZJl/xTaJiK2KB2lhHqAz+cTAHgSiZQc/pNnF2XUSZX0FBfxAG0qa9CogToNoQw7pEBJExc77QnFBQ== cross-env@^7.0.0: version "7.0.3" @@ -9763,7 +10208,7 @@ cross-fetch@3.1.5, cross-fetch@^3.0.4, cross-fetch@^3.0.6, cross-fetch@^3.1.3, c cross-spawn@^5.1.0: version "5.1.0" resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" - integrity sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk= + integrity sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A== dependencies: lru-cache "^4.0.1" shebang-command "^1.2.0" @@ -9851,6 +10296,11 @@ css-color-converter@^2.0.0: color-name "^1.1.4" css-unit-converter "^1.1.2" +css-color-keywords@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz#fea2616dc676b2962686b3af8dbdbe180b244e05" + integrity sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg== + css-declaration-sorter@^6.0.3: version "6.1.3" resolved "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.1.3.tgz#e9852e4cf940ba79f509d9425b137d1f94438dc2" @@ -9891,6 +10341,15 @@ css-select@^4.1.3: domutils "^2.6.0" nth-check "^2.0.0" +css-to-react-native@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.0.0.tgz#62dbe678072a824a689bcfee011fc96e02a7d756" + integrity sha512-Ro1yETZA813eoyUp2GDBhG2j+YggidUmzO1/v9eYBKR2EHVEniE2MI/NqpTQ954BMpTPZFsGNPm46qFB9dpaPQ== + dependencies: + camelize "^1.0.0" + css-color-keywords "^1.0.0" + postcss-value-parser "^4.0.2" + css-tree@^1.1.2, css-tree@^1.1.3: version "1.1.3" resolved "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz#eb4870fb6fd7707327ec95c2ff2ab09b5e8db91d" @@ -10025,30 +10484,30 @@ csstype@^3.0.2, csstype@^3.0.6: resolved "https://registry.npmjs.org/csstype/-/csstype-3.0.7.tgz#2a5fb75e1015e84dd15692f71e89a1450290950b" integrity sha512-KxnUB0ZMlnUWCsx2Z8MUsr6qV6ja1w9ArPErJaJaF8a5SOWoHLIszeCTKGRGRgtLgYrs1E8CHkNSP1VZTTPc9g== -csv-generate@^3.2.4: - version "3.2.4" - resolved "https://registry.npmjs.org/csv-generate/-/csv-generate-3.2.4.tgz#440dab9177339ee0676c9e5c16f50e2b3463c019" - integrity sha512-qNM9eqlxd53TWJeGtY1IQPj90b563Zx49eZs8e0uMyEvPgvNVmX1uZDtdzAcflB3PniuH9creAzcFOdyJ9YGvA== +csv-generate@^3.4.3: + version "3.4.3" + resolved "https://registry.npmjs.org/csv-generate/-/csv-generate-3.4.3.tgz#bc42d943b45aea52afa896874291da4b9108ffff" + integrity sha512-w/T+rqR0vwvHqWs/1ZyMDWtHHSJaN06klRqJXBEpDJaM/+dZkso0OKh1VcuuYvK3XM53KysVNq8Ko/epCK8wOw== -csv-parse@^4.8.8: - version "4.12.0" - resolved "https://registry.npmjs.org/csv-parse/-/csv-parse-4.12.0.tgz#fd42d6291bbaadd51d3009f6cadbb3e53b4ce026" - integrity sha512-wPQl3H79vWLPI8cgKFcQXl0NBgYYEqVnT1i6/So7OjMpsI540oD7p93r3w6fDSyPvwkTepG05F69/7AViX2lXg== +csv-parse@^4.16.3: + version "4.16.3" + resolved "https://registry.npmjs.org/csv-parse/-/csv-parse-4.16.3.tgz#7ca624d517212ebc520a36873c3478fa66efbaf7" + integrity sha512-cO1I/zmz4w2dcKHVvpCr7JVRu8/FymG5OEpmvsZYlccYolPBLoVGKUHgNoc4ZGkFeFlWGEDmMyBM+TTqRdW/wg== -csv-stringify@^5.3.6: - version "5.5.1" - resolved "https://registry.npmjs.org/csv-stringify/-/csv-stringify-5.5.1.tgz#f42cdd379b0f7f142933a11f674b1a91ebd0fcd0" - integrity sha512-HM0/86Ks8OwFbaYLd495tqTs1NhscZL52dC4ieKYumy8+nawQYC0xZ63w1NqLf0M148T2YLYqowoImc1giPn0g== +csv-stringify@^5.6.5: + version "5.6.5" + resolved "https://registry.npmjs.org/csv-stringify/-/csv-stringify-5.6.5.tgz#c6d74badda4b49a79bf4e72f91cce1e33b94de00" + integrity sha512-PjiQ659aQ+fUTQqSrd1XEDnOr52jh30RBurfzkscaE2tPaFsDH5wOAHJiw8XAHphRknCwMUE9KRayc4K/NbO8A== -csv@^5.3.1: - version "5.3.2" - resolved "https://registry.npmjs.org/csv/-/csv-5.3.2.tgz#50b344e25dfbb8c62684a1bcec18c22468b2161e" - integrity sha512-odDyucr9OgJTdGM2wrMbJXbOkJx3nnUX3Pt8SFOwlAMOpsUQlz1dywvLMXJWX/4Ib0rjfOsaawuuwfI5ucqBGQ== +csv@^5.5.0: + version "5.5.3" + resolved "https://registry.npmjs.org/csv/-/csv-5.5.3.tgz#cd26c1e45eae00ce6a9b7b27dcb94955ec95207d" + integrity sha512-QTaY0XjjhTQOdguARF0lGKm5/mEq9PD9/VhZZegHDIBq2tQwgNpHc3dneD4mGo2iJs+fTKv5Bp0fZ+BRuY3Z0g== dependencies: - csv-generate "^3.2.4" - csv-parse "^4.8.8" - csv-stringify "^5.3.6" - stream-transform "^2.0.1" + csv-generate "^3.4.3" + csv-parse "^4.16.3" + csv-stringify "^5.6.5" + stream-transform "^2.1.3" cypress-plugin-snapshots@^1.4.4: version "1.4.4" @@ -10071,10 +10530,10 @@ cypress-plugin-snapshots@^1.4.4: source-map-support "^0.5.10" unidiff "1.0.2" -cypress@^9.5.0: - version "9.6.1" - resolved "https://registry.npmjs.org/cypress/-/cypress-9.6.1.tgz#a7d6b5a53325b3dc4960181f5800a5ade0f085eb" - integrity sha512-ECzmV7pJSkk+NuAhEw6C3D+RIRATkSb2VAHXDY6qGZbca/F9mv5pPsj2LO6Ty6oIFVBTrwCyL9agl28MtJMe2g== +cypress@^10.0.0: + version "10.1.0" + resolved "https://registry.npmjs.org/cypress/-/cypress-10.1.0.tgz#6514a26c721822a02bc194e9a7f72c3142aea174" + integrity sha512-aQ4JVZVib4Xd9FZW8IRZfKelUvqF4y5A+oUbNvn8TlsBmEfIg3m5Xd6Mt6PVU/jHiVJ9Psl905B3ZPnrDcmyuQ== dependencies: "@cypress/request" "^2.88.10" "@cypress/xvfb" "^1.2.4" @@ -10151,11 +10610,6 @@ d3-color@1: resolved "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz#395b2833dfac71507f12ac2f7af23bf819de24e2" integrity sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA== -"d3-dispatch@1 - 2": - version "2.0.0" - resolved "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-2.0.0.tgz#8a18e16f76dd3fcaef42163c97b926aa9b55e7cf" - integrity sha512-S/m2VsXI7gAti2pBoLClFFTMOO1HTtT0j99AuXLoGFKO6deHDdnv6ZGTxSTTUTgO1zVcv82fCOtDjYK4EECmWA== - "d3-dispatch@1 - 3": version "3.0.1" resolved "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz#5fc75284e9c2375c36c839411a0cf550cbfc4d5e" @@ -10174,14 +10628,14 @@ d3-color@1: resolved "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz#9658ac38a2140d59d346160f1f6c30fda0bd12f4" integrity sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w== -d3-force@^2.0.1: - version "2.1.1" - resolved "https://registry.npmjs.org/d3-force/-/d3-force-2.1.1.tgz#f20ccbf1e6c9e80add1926f09b51f686a8bc0937" - integrity sha512-nAuHEzBqMvpFVMf9OX75d00OxvOXdxY+xECIXjW6Gv8BRrXu6gAWbv/9XKrvfJ5i5DCokDW7RYE50LRoK092ew== +d3-force@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz#3e2ba1a61e70888fe3d9194e30d6d14eece155c4" + integrity sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg== dependencies: - d3-dispatch "1 - 2" - d3-quadtree "1 - 2" - d3-timer "1 - 2" + d3-dispatch "1 - 3" + d3-quadtree "1 - 3" + d3-timer "1 - 3" d3-format@1: version "1.4.5" @@ -10229,10 +10683,10 @@ d3-path@1: resolved "https://registry.npmjs.org/d3-path/-/d3-path-3.0.1.tgz#f09dec0aaffd770b7995f1a399152bf93052321e" integrity sha512-gq6gZom9AFZby0YLduxT1qmrp4xpBA1YZr19OI717WIdKE2OM5ETq5qrHLb301IgxhLwcuxvGZVLeeWc/k1I6w== -"d3-quadtree@1 - 2": - version "2.0.0" - resolved "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-2.0.0.tgz#edbad045cef88701f6fee3aee8e93fb332d30f9d" - integrity sha512-b0Ed2t1UUalJpc3qXzKi+cPGxeXRr4KU9YSlocN74aTzp6R/Ud43t79yLLqxHRWZfsvWXmbDWPpoENK1K539xw== +"d3-quadtree@1 - 3": + version "3.0.1" + resolved "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz#6dca3e8be2b393c9a9d514dabbd80a92deef1a4f" + integrity sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw== d3-scale@^2.1.0: version "2.2.2" @@ -10309,11 +10763,6 @@ d3-time@1: dependencies: d3-array "2" -"d3-timer@1 - 2": - version "2.0.0" - resolved "https://registry.npmjs.org/d3-timer/-/d3-timer-2.0.0.tgz#055edb1d170cfe31ab2da8968deee940b56623e6" - integrity sha512-TO4VLh0/420Y/9dO3+f9abDEFYeCUr2WZRlxJvbp4HPTQcSylXNiL6yZa9FIUvV1yRiFufl1bszTCLDqv9PWNA== - "d3-timer@1 - 3": version "3.0.1" resolved "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz#6284d2a2708285b1abb7e201eda4380af35e63b0" @@ -10465,7 +10914,7 @@ debuglog@^1.0.1: decamelize-keys@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz#d171a87933252807eb3cb61dc1c1445d078df2d9" - integrity sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk= + integrity sha512-ocLWuYzRPoS9bfiSdDd3cxvrzovVMZnRDVEzAs+hWIVXGDbHxWMECij2OBuyB/An0FFW/nLuq6Kv1i/YC5Qfzg== dependencies: decamelize "^1.1.0" map-obj "^1.0.0" @@ -10473,7 +10922,7 @@ decamelize-keys@^1.1.0: decamelize@^1.1.0, decamelize@^1.1.1, decamelize@^1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" - integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= + integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== decimal.js-light@^2.4.1: version "2.5.0" @@ -10548,7 +10997,7 @@ default-gateway@^6.0.3: defaults@^1.0.3: version "1.0.3" resolved "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" - integrity sha1-xlYFHpgX2f8I7YgUd/P+QBnz730= + integrity sha512-s82itHOnYrN0Ib8r+z7laQz3sdE+4FP3d9Q7VLO7U+KRT+CR0GsWuyHxzdAY82I7cXv0G/twrqomTJLOssO5HA== dependencies: clone "^1.0.2" @@ -10567,12 +11016,13 @@ define-lazy-prop@^2.0.0: resolved "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== -define-properties@^1.1.3: - version "1.1.3" - resolved "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" - integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== +define-properties@^1.1.3, define-properties@^1.1.4: + version "1.1.4" + resolved "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz#0b14d7bd7fbeb2f3572c3a7eda80ea5d57fb05b1" + integrity sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA== dependencies: - object-keys "^1.0.12" + has-property-descriptors "^1.0.0" + object-keys "^1.1.1" define-property@^0.2.5: version "0.2.5" @@ -10597,9 +11047,9 @@ define-property@^2.0.2: isobject "^3.0.1" del@^6.0.0: - version "6.1.0" - resolved "https://registry.npmjs.org/del/-/del-6.1.0.tgz#aa79a5b0a2a9ecc985c0a075e8ad9a5b23bf949c" - integrity sha512-OpcRktOt7G7HBfyxP0srBH4Djg4824EQORX8E1qvIhIzthNNArxxhrB/Mm7dRMiLi1nvFyUpDhzD2cTtbBhV8A== + version "6.1.1" + resolved "https://registry.npmjs.org/del/-/del-6.1.1.tgz#3b70314f1ec0aa325c6b14eb36b95786671edb7a" + integrity sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg== dependencies: globby "^11.0.1" graceful-fs "^4.2.4" @@ -10674,9 +11124,9 @@ detect-indent@^5.0.0: integrity sha1-OHHMCmoALow+Wzz38zYmRnXwa50= detect-indent@^6.0.0: - version "6.0.0" - resolved "https://registry.npmjs.org/detect-indent/-/detect-indent-6.0.0.tgz#0abd0f549f69fc6659a254fe96786186b6f528fd" - integrity sha512-oSyFlqaTHCItVRGK5RmrmjB+CmaMOW7IaNA/kdxqhoa6d17j/5ce9O9eWXmV/KEdRwqpQA+Vqe8a8Bsybu4YnA== + version "6.1.0" + resolved "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz#592485ebbbf6b3b1ab2be175c8393d04ca0d57e6" + integrity sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA== detect-libc@^1.0.3: version "1.0.3" @@ -10711,10 +11161,10 @@ detect-port-alt@^1.1.6: address "^1.0.1" debug "^2.6.0" -devtools-protocol@0.0.982423: - version "0.0.982423" - resolved "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.982423.tgz#39ac3791d4c5b90ebb416d4384663b7b0cc44154" - integrity sha512-FnVW2nDbjGNw1uD/JRC+9U5768W7e1TfUwqbDTcSsAu1jXFjITSX8w3rkW5FEpHRMPPGpvNSmO1pOpqByiWscA== +devtools-protocol@0.0.1001819: + version "0.0.1001819" + resolved "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1001819.tgz#0a98f44cefdb02cc684f3d5e6bd898a1690231d9" + integrity sha512-G6OsIFnv/rDyxSqBa2lDLR6thp9oJioLsb2Gl+LbQlyoA9/OBAkrTU9jiCcQ8Pnh7z4d6slDiLaogR5hzgJLmQ== dezalgo@1.0.3, dezalgo@^1.0.0: version "1.0.3" @@ -10817,9 +11267,9 @@ docker-modem@^3.0.0: ssh2 "^1.4.0" dockerode@^3.3.1: - version "3.3.1" - resolved "https://registry.npmjs.org/dockerode/-/dockerode-3.3.1.tgz#74f66e239e092e7910e2beae6322d35c44b08cdc" - integrity sha512-AS2mr8Lp122aa5n6d99HkuTNdRV1wkkhHwBdcnY6V0+28D3DSYwhxAk85/mM9XwD3RMliTxyr63iuvn5ZblFYQ== + version "3.3.2" + resolved "https://registry.npmjs.org/dockerode/-/dockerode-3.3.2.tgz#f3545700d2f7f86141b58843a755eeb21e38d5cd" + integrity sha512-oXN+1XVH2TeyE0Jj9Ci6Fim8ZIDxyqeJrkx9qhEOaRiA+nhLihKfd3M2L+Aqrj5C2ObPw8RVN2zPWvvk0x2dwg== dependencies: docker-modem "^3.0.0" tar-fs "~2.0.1" @@ -10954,15 +11404,10 @@ drange@^1.0.2: resolved "https://registry.npmjs.org/drange/-/drange-1.1.1.tgz#b2aecec2aab82fcef11dbbd7b9e32b83f8f6c0b8" integrity sha512-pYxfDYpued//QpnLIm4Avk7rsNtAtQkUES2cwAYSvD/wd2pKD71gN2Ebj3e7klzXwjocvE8c5vx/1fxwpqmSxA== -dset@^3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/dset/-/dset-3.1.0.tgz#23feb6df93816ea452566308b1374d6e869b0d7b" - integrity sha512-7xTQ5DzyE59Nn+7ZgXDXjKAGSGmXZHqttMVVz1r4QNfmGpyj+cm2YtI3II0c/+4zS4a9yq2mBhgdeq2QnpcYlw== - -dset@^3.1.1: - version "3.1.1" - resolved "https://registry.npmjs.org/dset/-/dset-3.1.1.tgz#07de5af7a8d03eab337ad1a8ba77fe17bba61a8c" - integrity sha512-hYf+jZNNqJBD2GiMYb+5mqOIX4R4RRHXU3qWMWYN+rqcR2/YpRL2bUHr8C8fU+5DNvqYjJ8YvMGSLuVPWU1cNg== +dset@^3.1.0, dset@^3.1.1: + version "3.1.2" + resolved "https://registry.npmjs.org/dset/-/dset-3.1.2.tgz#89c436ca6450398396dc6538ea00abc0c54cd45a" + integrity sha512-g/M9sqy3oHe477Ar4voQxWtaPIFw1jTdKZuomOjhCcBx9nHUNn0pu6NopuFFrTh/TRZIKEj+76vLWFu9BNKk+Q== duplexer2@~0.1.4: version "0.1.4" @@ -11001,6 +11446,13 @@ eastasianwidth@^0.2.0: resolved "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== +easy-table@1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/easy-table/-/easy-table-1.1.0.tgz#86f9ab4c102f0371b7297b92a651d5824bc8cb73" + integrity sha1-hvmrTBAvA3G3KXuSplHVgkvIy3M= + optionalDependencies: + wcwidth ">=1.0.1" + ecc-jsbn@~0.1.1: version "0.1.2" resolved "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" @@ -11101,7 +11553,7 @@ encodeurl@~1.0.2: resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= -encoding@^0.1.12: +encoding@^0.1.12, encoding@^0.1.13: version "0.1.13" resolved "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A== @@ -11224,7 +11676,7 @@ error@^10.4.0: resolved "https://registry.npmjs.org/error/-/error-10.4.0.tgz#6fcf0fd64bceb1e750f8ed9a3dd880f00e46a487" integrity sha512-YxIFEJuhgcICugOUvRx5th0UM+ActZ9sjY0QJmeVwsQdvosZ7kYzc9QqS0Da3R5iUmgU5meGIxh0xBeZpMVeLw== -es-abstract@^1.17.0-next.1, es-abstract@^1.18.0-next.1, es-abstract@^1.18.0-next.2, es-abstract@^1.19.0, es-abstract@^1.19.1: +es-abstract@^1.18.0-next.1, es-abstract@^1.18.0-next.2, es-abstract@^1.19.1: version "1.19.1" resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz#d4885796876916959de78edaa0df456627115ec3" integrity sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w== @@ -11250,11 +11702,47 @@ es-abstract@^1.17.0-next.1, es-abstract@^1.18.0-next.1, es-abstract@^1.18.0-next string.prototype.trimstart "^1.0.4" unbox-primitive "^1.0.1" +es-abstract@^1.19.0, es-abstract@^1.19.2, es-abstract@^1.19.5: + version "1.20.1" + resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.1.tgz#027292cd6ef44bd12b1913b828116f54787d1814" + integrity sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA== + dependencies: + call-bind "^1.0.2" + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + function.prototype.name "^1.1.5" + get-intrinsic "^1.1.1" + get-symbol-description "^1.0.0" + has "^1.0.3" + has-property-descriptors "^1.0.0" + has-symbols "^1.0.3" + internal-slot "^1.0.3" + is-callable "^1.2.4" + is-negative-zero "^2.0.2" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.2" + is-string "^1.0.7" + is-weakref "^1.0.2" + object-inspect "^1.12.0" + object-keys "^1.1.1" + object.assign "^4.1.2" + regexp.prototype.flags "^1.4.3" + string.prototype.trimend "^1.0.5" + string.prototype.trimstart "^1.0.5" + unbox-primitive "^1.0.2" + es-module-lexer@^0.9.0, es-module-lexer@^0.9.3: version "0.9.3" resolved "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz#6f13db00cc38417137daf74366f535c8eb438f19" integrity sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ== +es-shim-unscopables@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz#702e632193201e3edf8713635d083d378e510241" + integrity sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w== + dependencies: + has "^1.0.3" + es-to-primitive@^1.2.1: version "1.2.1" resolved "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" @@ -11269,75 +11757,75 @@ es6-error@^4.1.1: resolved "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d" integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg== -esbuild-android-64@0.14.39: - version "0.14.39" - resolved "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.39.tgz#09f12a372eed9743fd77ff6d889ac14f7b340c21" - integrity sha512-EJOu04p9WgZk0UoKTqLId9VnIsotmI/Z98EXrKURGb3LPNunkeffqQIkjS2cAvidh+OK5uVrXaIP229zK6GvhQ== +esbuild-android-64@0.14.46: + version "0.14.46" + resolved "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.46.tgz#98d019853ca7b526d0d645bb4618fda425b74d35" + integrity sha512-ZyJqwAcjNbZprs0ZAxnUAOhEhdE5kTKwz+CZuLmZYNLAPyRgBtaC8pT2PCuPifNvV8Cl3yLlrQPaOCjovoyb5g== -esbuild-android-arm64@0.14.39: - version "0.14.39" - resolved "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.39.tgz#f608d00ea03fe26f3b1ab92a30f99220390f3071" - integrity sha512-+twajJqO7n3MrCz9e+2lVOnFplRsaGRwsq1KL/uOy7xK7QdRSprRQcObGDeDZUZsacD5gUkk6OiHiYp6RzU3CA== +esbuild-android-arm64@0.14.46: + version "0.14.46" + resolved "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.46.tgz#19835d5265b57120c14fba56a19fc317ca4bbda0" + integrity sha512-BKcnUksvCijO9ONv6b4SikZE/OZftwJvX91XROODZGQmuwGVg97jmLDVu3lxuHdFlMNNzxh8taJ2mbCWZzH/Iw== -esbuild-darwin-64@0.14.39: - version "0.14.39" - resolved "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.39.tgz#31528daa75b4c9317721ede344195163fae3e041" - integrity sha512-ImT6eUw3kcGcHoUxEcdBpi6LfTRWaV6+qf32iYYAfwOeV+XaQ/Xp5XQIBiijLeo+LpGci9M0FVec09nUw41a5g== +esbuild-darwin-64@0.14.46: + version "0.14.46" + resolved "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.46.tgz#2819465ab92f6df407e6462d9e56f6563a043a44" + integrity sha512-/ss2kO92sUJ9/1nHnMb3+oab8w6dyqKrMtPMvSYJ9KZIYGAZxz/WYxfFprY7Xk+ZxWnnlASSyZlG+If1nVmFYg== -esbuild-darwin-arm64@0.14.39: - version "0.14.39" - resolved "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.39.tgz#247f770d86d90a215fa194f24f90e30a0bd97245" - integrity sha512-/fcQ5UhE05OiT+bW5v7/up1bDsnvaRZPJxXwzXsMRrr7rZqPa85vayrD723oWMT64dhrgWeA3FIneF8yER0XTw== +esbuild-darwin-arm64@0.14.46: + version "0.14.46" + resolved "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.46.tgz#2d523fa628930bba38a4f75cede75d6341bc3b5b" + integrity sha512-WX0JOaEFf6t+rIjXO6THsf/0fhQAt2Zb0/PSYlvXnuQQAmOmFAfPsuRNocp5ME0NGaUqZd4FxqqmLEVK3RzPAg== -esbuild-freebsd-64@0.14.39: - version "0.14.39" - resolved "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.39.tgz#479414d294905055eb396ebe455ed42213284ee0" - integrity sha512-oMNH8lJI4wtgN5oxuFP7BQ22vgB/e3Tl5Woehcd6i2r6F3TszpCnNl8wo2d/KvyQ4zvLvCWAlRciumhQg88+kQ== +esbuild-freebsd-64@0.14.46: + version "0.14.46" + resolved "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.46.tgz#dbfbbb1cb943149aaa83b9e767ded6f14ad42ba5" + integrity sha512-o+ozPFuHRCAGCVWU2bLurOUgVkT0jcPEu082VBUY2Q/yLf+B+/3nXzh4Fjp5O21tOvJRTn7hUVydG9j5+vYE6A== -esbuild-freebsd-arm64@0.14.39: - version "0.14.39" - resolved "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.39.tgz#cedeb10357c88533615921ae767a67dc870a474c" - integrity sha512-1GHK7kwk57ukY2yI4ILWKJXaxfr+8HcM/r/JKCGCPziIVlL+Wi7RbJ2OzMcTKZ1HpvEqCTBT/J6cO4ZEwW4Ypg== +esbuild-freebsd-arm64@0.14.46: + version "0.14.46" + resolved "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.46.tgz#fb066d6e7de2bd96138dd1c2fba5e8ce5233ed5a" + integrity sha512-9zicZ0X43WDKz3sjNfcqYO38xbfJpSWYXB+FxvYYkmBwGA52K0SAu4oKuTTLi8od8X2IIo1x5C5TUNvKDSVJww== -esbuild-linux-32@0.14.39: - version "0.14.39" - resolved "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.39.tgz#d9f008c4322d771f3958f59c1eee5a05cdf92485" - integrity sha512-g97Sbb6g4zfRLIxHgW2pc393DjnkTRMeq3N1rmjDUABxpx8SjocK4jLen+/mq55G46eE2TA0MkJ4R3SpKMu7dg== +esbuild-linux-32@0.14.46: + version "0.14.46" + resolved "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.46.tgz#ea2464b10fe188ee7c9be81a2757e2d69ffdb8c1" + integrity sha512-ZnTpZMVb0VGvL99R5eh4OrJwbUyvpM6M88VAMuHP4LvFjuvZrhgefjKqEGuWZZW7JRnAjKqjXLjWdhdSjwMFnQ== -esbuild-linux-64@0.14.39: - version "0.14.39" - resolved "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.39.tgz#ba58d7f66858913aeb1ab5c6bde1bbd824731795" - integrity sha512-4tcgFDYWdI+UbNMGlua9u1Zhu0N5R6u9tl5WOM8aVnNX143JZoBZLpCuUr5lCKhnD0SCO+5gUyMfupGrHtfggQ== +esbuild-linux-64@0.14.46: + version "0.14.46" + resolved "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.46.tgz#f4f2d181af6ea78137311712fca423f8fa954314" + integrity sha512-ECCRRZtX6l4ubeVhHhiVoK/uYAkvzNqfmR4gP4N/9H9RPu+b8YCcN4bQGp7xCuYIV6Xd41WpOMyO+xpcQvjtQQ== -esbuild-linux-arm64@0.14.39: - version "0.14.39" - resolved "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.39.tgz#708785a30072702b5b1c16b65cf9c25c51202529" - integrity sha512-23pc8MlD2D6Px1mV8GMglZlKgwgNKAO8gsgsLLcXWSs9lQsCYkIlMo/2Ycfo5JrDIbLdwgP8D2vpfH2KcBqrDQ== +esbuild-linux-arm64@0.14.46: + version "0.14.46" + resolved "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.46.tgz#113d17c523dfe80c9d4981d05c58f5ada3470e30" + integrity sha512-HX0TXCHyI0NEWG4jg8LlW1PbZQbnz+PUH56yjx996cgM5pC90u32drKs/tyJiyyQmNk9OXOogjKw7LEdp/Qc1w== -esbuild-linux-arm@0.14.39: - version "0.14.39" - resolved "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.39.tgz#4e8b5deaa7ab60d0d28fab131244ef82b40684f4" - integrity sha512-t0Hn1kWVx5UpCzAJkKRfHeYOLyFnXwYynIkK54/h3tbMweGI7dj400D1k0Vvtj2u1P+JTRT9tx3AjtLEMmfVBQ== +esbuild-linux-arm@0.14.46: + version "0.14.46" + resolved "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.46.tgz#1b6ba77b0301572b2fed35fe922b1613b10b5c7e" + integrity sha512-RvTJEi4vj13c5FP9YPp+8Y6x6HK1E7uSqfy3y9UoeaNAzNZWA7fN1U3hQjTL/dy5zTJH5KE64mrt5k5+he+CQA== -esbuild-linux-mips64le@0.14.39: - version "0.14.39" - resolved "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.39.tgz#6f3bf3023f711084e5a1e8190487d2020f39f0f7" - integrity sha512-epwlYgVdbmkuRr5n4es3B+yDI0I2e/nxhKejT9H0OLxFAlMkeQZxSpxATpDc9m8NqRci6Kwyb/SfmD1koG2Zuw== +esbuild-linux-mips64le@0.14.46: + version "0.14.46" + resolved "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.46.tgz#8775e12510eaf988a9bc5e6787cf0c87c3eb40d6" + integrity sha512-jnb2NDwGqJUVmxn1v0f7seNdDm0nRNWHP9Z3MrWAGnBCdnnDlsjqRFDnbKoaQvWONEa+rOOr/giK+VL0hgQExA== -esbuild-linux-ppc64le@0.14.39: - version "0.14.39" - resolved "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.39.tgz#900e718a4ea3f6aedde8424828eeefdd4b48d4b9" - integrity sha512-W/5ezaq+rQiQBThIjLMNjsuhPHg+ApVAdTz2LvcuesZFMsJoQAW2hutoyg47XxpWi7aEjJGrkS26qCJKhRn3QQ== +esbuild-linux-ppc64le@0.14.46: + version "0.14.46" + resolved "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.46.tgz#da003df59859b02e160827e26bc1e107bec23d07" + integrity sha512-uu3JTQUrwwauKY9z8yq5MnDyOlT3f2DNOzBcYz4dB78HqwEqilCsifoBGd0WcbED5n57dc59X+LZMTZ8Ose44w== -esbuild-linux-riscv64@0.14.39: - version "0.14.39" - resolved "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.39.tgz#dcbff622fa37047a75d2ff7a1d8d2949d80277e4" - integrity sha512-IS48xeokcCTKeQIOke2O0t9t14HPvwnZcy+5baG13Z1wxs9ZrC5ig5ypEQQh4QMKxURD5TpCLHw2W42CLuVZaA== +esbuild-linux-riscv64@0.14.46: + version "0.14.46" + resolved "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.46.tgz#b6c4faf9f8482e2a898ec2becac4a6789ae3ff22" + integrity sha512-OB29r1EG44ZY34JnXCRERxo7k4pRKoQdaoRg2HIeCavatsXZwW4LCakpLnMQ72vXT1HtpBUABEjHkKkn5JyrUg== -esbuild-linux-s390x@0.14.39: - version "0.14.39" - resolved "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.39.tgz#3f725a7945b419406c99d93744b28552561dcdfd" - integrity sha512-zEfunpqR8sMomqXhNTFEKDs+ik7HC01m3M60MsEjZOqaywHu5e5682fMsqOlZbesEAAaO9aAtRBsU7CHnSZWyA== +esbuild-linux-s390x@0.14.46: + version "0.14.46" + resolved "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.46.tgz#d30d0f7ee8466c4ec99ac2c689b70514320406b2" + integrity sha512-XQ/U9TueMSGYyPTKyZsJVraiuvxhwCDIMn/QwFXCRCJ6H/Cy/Rq33u9qhpeSziinHKfzJROGx5A8mQY6aYamdQ== esbuild-loader@^2.18.0: version "2.19.0" @@ -11351,61 +11839,61 @@ esbuild-loader@^2.18.0: tapable "^2.2.0" webpack-sources "^2.2.0" -esbuild-netbsd-64@0.14.39: - version "0.14.39" - resolved "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.39.tgz#e10e40b6a765798b90d4eb85901cc85c8b7ff85e" - integrity sha512-Uo2suJBSIlrZCe4E0k75VDIFJWfZy+bOV6ih3T4MVMRJh1lHJ2UyGoaX4bOxomYN3t+IakHPyEoln1+qJ1qYaA== +esbuild-netbsd-64@0.14.46: + version "0.14.46" + resolved "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.46.tgz#fcf2b739b63731b5b264dc584240c0b61dbbf035" + integrity sha512-i15BwqHaAIFp1vBJkitAbHtwXcLk9TdHs/Ia1xGIAutQYXSJNPLM3Z4B4hyfHNEFl2yBqBIYpglMohv2ClNdOQ== -esbuild-openbsd-64@0.14.39: - version "0.14.39" - resolved "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.39.tgz#935ec143f75ce10bd9cdb1c87fee00287eb0edbc" - integrity sha512-secQU+EpgUPpYjJe3OecoeGKVvRMLeKUxSMGHnK+aK5uQM3n1FPXNJzyz1LHFOo0WOyw+uoCxBYdM4O10oaCAA== +esbuild-openbsd-64@0.14.46: + version "0.14.46" + resolved "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.46.tgz#fc7880751b78b325216e66470b9a3df7b1c21cbf" + integrity sha512-XwOIFCE140Y/PvjrwjFfa/QLWBuvhR1mPCOa35mKx02jt++wPNgf0qhn6HfdVC3vQe7R46RwTp4q2cp99fepEg== -esbuild-sunos-64@0.14.39: - version "0.14.39" - resolved "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.39.tgz#0e7aa82b022a2e6d55b0646738b2582c2d72c3c0" - integrity sha512-qHq0t5gePEDm2nqZLb+35p/qkaXVS7oIe32R0ECh2HOdiXXkj/1uQI9IRogGqKkK+QjDG+DhwiUw7QoHur/Rwg== +esbuild-sunos-64@0.14.46: + version "0.14.46" + resolved "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.46.tgz#ab73b91e79ae53dddb035da9bb69ba7dd44d36ee" + integrity sha512-+kV3JnmfdxBVpHyFvuGXWtu6tXxXApOLPkSrVkMJf6+ns/3PLtPndpzwCzHjD+qYUEk8ln4MA+ufQ2qmjW5mZg== -esbuild-windows-32@0.14.39: - version "0.14.39" - resolved "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.39.tgz#3f1538241f31b538545f4b5841b248cac260fa35" - integrity sha512-XPjwp2OgtEX0JnOlTgT6E5txbRp6Uw54Isorm3CwOtloJazeIWXuiwK0ONJBVb/CGbiCpS7iP2UahGgd2p1x+Q== +esbuild-windows-32@0.14.46: + version "0.14.46" + resolved "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.46.tgz#88ad388c896325d273e92dbf28f146d1d34d4351" + integrity sha512-gzGC1Q11B/Bo5A2EX4N22oigWmhL7Z0eDyc8kbSoJjqSrGQuRE7B0uMpluO+q0O/gZ1S3zdw+M4PCWlqOIeXLA== -esbuild-windows-64@0.14.39: - version "0.14.39" - resolved "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.39.tgz#b100c59f96d3c2da2e796e42fee4900d755d3e03" - integrity sha512-E2wm+5FwCcLpKsBHRw28bSYQw0Ikxb7zIMxw3OPAkiaQhLVr3dnVO8DofmbWhhf6b97bWzg37iSZ45ZDpLw7Ow== +esbuild-windows-64@0.14.46: + version "0.14.46" + resolved "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.46.tgz#bfb21b68d54d0db86190f8073cbf66a86bd0de14" + integrity sha512-Do2daaskfOjmCB7o3ygz6fD3K6SPjZLERiZLktzHz2oUCwsebKu/gmop0+j/XdrVIXC32wFzHzDS+9CTu9OShw== -esbuild-windows-arm64@0.14.39: - version "0.14.39" - resolved "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.39.tgz#00268517e665b33c89778d61f144e4256b39f631" - integrity sha512-sBZQz5D+Gd0EQ09tZRnz/PpVdLwvp/ufMtJ1iDFYddDaPpZXKqPyaxfYBLs3ueiaksQ26GGa7sci0OqFzNs7KA== +esbuild-windows-arm64@0.14.46: + version "0.14.46" + resolved "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.46.tgz#b8aa66a0c347342b9ae780b537d5574e9691c123" + integrity sha512-VEzMy6bM60/HT/URTDElyhfi2Pk0quCCrEhRlI4MRno/AIqYUGw0rZwkPl6PeoqVI6BgoBHGY576GWTiPmshCA== esbuild@^0.14.1, esbuild@^0.14.10, esbuild@^0.14.39: - version "0.14.39" - resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.14.39.tgz#c926b2259fe6f6d3a94f528fb42e103c5a6d909a" - integrity sha512-2kKujuzvRWYtwvNjYDY444LQIA3TyJhJIX3Yo4+qkFlDDtGlSicWgeHVJqMUP/2sSfH10PGwfsj+O2ro1m10xQ== + version "0.14.46" + resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.14.46.tgz#d548cfc13fecfd4bc074ce38e0122d1dd9b067db" + integrity sha512-vdm5G1JdZBktva8dwQci/s44VbeBUg8g907xoZx77mqFZ4gU5GlMULNsdGeID+qXCXocsfYSGtE0LvqH3eiNQg== optionalDependencies: - esbuild-android-64 "0.14.39" - esbuild-android-arm64 "0.14.39" - esbuild-darwin-64 "0.14.39" - esbuild-darwin-arm64 "0.14.39" - esbuild-freebsd-64 "0.14.39" - esbuild-freebsd-arm64 "0.14.39" - esbuild-linux-32 "0.14.39" - esbuild-linux-64 "0.14.39" - esbuild-linux-arm "0.14.39" - esbuild-linux-arm64 "0.14.39" - esbuild-linux-mips64le "0.14.39" - esbuild-linux-ppc64le "0.14.39" - esbuild-linux-riscv64 "0.14.39" - esbuild-linux-s390x "0.14.39" - esbuild-netbsd-64 "0.14.39" - esbuild-openbsd-64 "0.14.39" - esbuild-sunos-64 "0.14.39" - esbuild-windows-32 "0.14.39" - esbuild-windows-64 "0.14.39" - esbuild-windows-arm64 "0.14.39" + esbuild-android-64 "0.14.46" + esbuild-android-arm64 "0.14.46" + esbuild-darwin-64 "0.14.46" + esbuild-darwin-arm64 "0.14.46" + esbuild-freebsd-64 "0.14.46" + esbuild-freebsd-arm64 "0.14.46" + esbuild-linux-32 "0.14.46" + esbuild-linux-64 "0.14.46" + esbuild-linux-arm "0.14.46" + esbuild-linux-arm64 "0.14.46" + esbuild-linux-mips64le "0.14.46" + esbuild-linux-ppc64le "0.14.46" + esbuild-linux-riscv64 "0.14.46" + esbuild-linux-s390x "0.14.46" + esbuild-netbsd-64 "0.14.46" + esbuild-openbsd-64 "0.14.46" + esbuild-sunos-64 "0.14.46" + esbuild-windows-32 "0.14.46" + esbuild-windows-64 "0.14.46" + esbuild-windows-arm64 "0.14.46" escalade@^3.1.1: version "3.1.1" @@ -11425,7 +11913,7 @@ escape-html@^1.0.3, escape-html@~1.0.3: escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" - integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== escape-string-regexp@^2.0.0: version "2.0.0" @@ -11522,9 +12010,9 @@ eslint-plugin-import@^2.25.4: tsconfig-paths "^3.14.1" eslint-plugin-jest@^26.1.2: - version "26.2.2" - resolved "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-26.2.2.tgz#74e000544259f1ef0462a609a3fc9e5da3768f6c" - integrity sha512-etSFZ8VIFX470aA6kTqDPhIq7YWe0tjBcboFNV3WeiC18PJ/AVonGhuTwlmuz2fBkH8FJHA7JQ4k7GsQIj1Gew== + version "26.5.3" + resolved "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-26.5.3.tgz#a3ceeaf4a757878342b8b00eca92379b246e5505" + integrity sha512-sICclUqJQnR1bFRZGLN2jnSVsYOsmPYYnroGCIMVSvTS3y8XR3yjzy1EcTQmk6typ5pRgyIWzbjqxK6cZHEZuQ== dependencies: "@typescript-eslint/utils" "^5.10.0" @@ -11569,29 +12057,29 @@ eslint-plugin-notice@^0.9.10: metric-lcs "^0.1.2" eslint-plugin-react-hooks@^4.3.0: - version "4.5.0" - resolved "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.5.0.tgz#5f762dfedf8b2cf431c689f533c9d3fa5dcf25ad" - integrity sha512-8k1gRt7D7h03kd+SAAlzXkQwWK22BnK6GKZG+FJA6BAGy22CFvl8kCIXKpVux0cCxMWDQUPqSok0LKaZ0aOcCw== + version "4.6.0" + resolved "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz#4c3e697ad95b77e93f8646aaa1630c1ba607edd3" + integrity sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g== eslint-plugin-react@^7.28.0: - version "7.29.4" - resolved "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.29.4.tgz#4717de5227f55f3801a5fd51a16a4fa22b5914d2" - integrity sha512-CVCXajliVh509PcZYRFyu/BoUEz452+jtQJq2b3Bae4v3xBUWPLCmtmBM+ZinG4MzwmxJgJ2M5rMqhqLVn7MtQ== + version "7.30.0" + resolved "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.30.0.tgz#8e7b1b2934b8426ac067a0febade1b13bd7064e3" + integrity sha512-RgwH7hjW48BleKsYyHK5vUAvxtE9SMPDKmcPRQgtRCYaZA0XQPt5FSkrU3nhz5ifzMZcA8opwmRJ2cmOO8tr5A== dependencies: - array-includes "^3.1.4" - array.prototype.flatmap "^1.2.5" + array-includes "^3.1.5" + array.prototype.flatmap "^1.3.0" doctrine "^2.1.0" estraverse "^5.3.0" jsx-ast-utils "^2.4.1 || ^3.0.0" minimatch "^3.1.2" object.entries "^1.1.5" object.fromentries "^2.0.5" - object.hasown "^1.1.0" + object.hasown "^1.1.1" object.values "^1.1.5" prop-types "^15.8.1" resolve "^2.0.0-next.3" semver "^6.3.0" - string.prototype.matchall "^4.0.6" + string.prototype.matchall "^4.0.7" eslint-scope@5.1.1, eslint-scope@^5.1.1: version "5.1.1" @@ -11638,11 +12126,11 @@ eslint-webpack-plugin@^3.1.1: schema-utils "^3.1.1" eslint@^8.6.0: - version "8.15.0" - resolved "https://registry.npmjs.org/eslint/-/eslint-8.15.0.tgz#fea1d55a7062da48d82600d2e0974c55612a11e9" - integrity sha512-GG5USZ1jhCu8HJkzGgeK8/+RGnHaNYZGrGDzUtigK3BsGESW/rs2az23XqE0WVwDxy1VRvvjSSGu5nB0Bu+6SA== + version "8.18.0" + resolved "https://registry.npmjs.org/eslint/-/eslint-8.18.0.tgz#78d565d16c993d0b73968c523c0446b13da784fd" + integrity sha512-As1EfFMVk7Xc6/CvhssHUjsAQSkpfXvUGMFC3ce8JDe6WvqCgRrLOBQbVpsBFr1X1V+RACOadnzVvcUS5ni2bA== dependencies: - "@eslint/eslintrc" "^1.2.3" + "@eslint/eslintrc" "^1.3.0" "@humanwhocodes/config-array" "^0.9.2" ajv "^6.10.0" chalk "^4.0.0" @@ -11660,7 +12148,7 @@ eslint@^8.6.0: file-entry-cache "^6.0.1" functional-red-black-tree "^1.0.1" glob-parent "^6.0.1" - globals "^13.6.0" + globals "^13.15.0" ignore "^5.2.0" import-fresh "^3.0.0" imurmurhash "^0.1.4" @@ -11798,61 +12286,62 @@ evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: safe-buffer "^5.1.1" "example-app@link:packages/app": - version "0.2.71" + version "0.2.72" dependencies: - "@backstage/app-defaults" "^1.0.2" - "@backstage/catalog-model" "^1.0.2" - "@backstage/cli" "^0.17.1" + "@backstage/app-defaults" "^1.0.3" + "@backstage/catalog-model" "^1.0.3" + "@backstage/cli" "^0.17.2" "@backstage/config" "^1.0.1" - "@backstage/core-app-api" "^1.0.2" - "@backstage/core-components" "^0.9.4" - "@backstage/core-plugin-api" "^1.0.2" - "@backstage/integration-react" "^1.1.0" - "@backstage/plugin-airbrake" "^0.3.5" - "@backstage/plugin-apache-airflow" "^0.1.13" - "@backstage/plugin-api-docs" "^0.8.5" - "@backstage/plugin-azure-devops" "^0.1.21" - "@backstage/plugin-badges" "^0.2.29" - "@backstage/plugin-catalog" "^1.2.0" - "@backstage/plugin-catalog-common" "^1.0.2" - "@backstage/plugin-catalog-graph" "^0.2.17" - "@backstage/plugin-catalog-import" "^0.8.8" - "@backstage/plugin-catalog-react" "^1.1.0" - "@backstage/plugin-circleci" "^0.3.5" - "@backstage/plugin-cloudbuild" "^0.3.5" - "@backstage/plugin-code-coverage" "^0.1.32" - "@backstage/plugin-cost-insights" "^0.11.27" - "@backstage/plugin-explore" "^0.3.36" - "@backstage/plugin-gcalendar" "^0.3.1" - "@backstage/plugin-gcp-projects" "^0.3.24" - "@backstage/plugin-github-actions" "^0.5.5" - "@backstage/plugin-gocd" "^0.1.11" - "@backstage/plugin-graphiql" "^0.2.37" - "@backstage/plugin-home" "^0.4.21" - "@backstage/plugin-jenkins" "^0.7.4" - "@backstage/plugin-kafka" "^0.3.5" - "@backstage/plugin-kubernetes" "^0.6.5" - "@backstage/plugin-lighthouse" "^0.3.5" - "@backstage/plugin-newrelic" "^0.3.23" - "@backstage/plugin-newrelic-dashboard" "^0.1.13" - "@backstage/plugin-org" "^0.5.5" - "@backstage/plugin-pagerduty" "0.3.32" - "@backstage/plugin-permission-react" "^0.4.1" - "@backstage/plugin-rollbar" "^0.4.5" - "@backstage/plugin-scaffolder" "^1.2.0" - "@backstage/plugin-search" "^0.8.1" - "@backstage/plugin-search-common" "^0.3.4" - "@backstage/plugin-search-react" "^0.2.0" - "@backstage/plugin-sentry" "^0.3.43" - "@backstage/plugin-shortcuts" "^0.2.6" - "@backstage/plugin-stack-overflow" "^0.1.1" - "@backstage/plugin-tech-insights" "^0.2.1" - "@backstage/plugin-tech-radar" "^0.5.12" - "@backstage/plugin-techdocs" "^1.1.1" - "@backstage/plugin-techdocs-module-addons-contrib" "^1.0.0" - "@backstage/plugin-techdocs-react" "^1.0.0" - "@backstage/plugin-todo" "^0.2.7" - "@backstage/plugin-user-settings" "^0.4.4" + "@backstage/core-app-api" "^1.0.3" + "@backstage/core-components" "^0.9.5" + "@backstage/core-plugin-api" "^1.0.3" + "@backstage/integration-react" "^1.1.1" + "@backstage/plugin-airbrake" "^0.3.6" + "@backstage/plugin-apache-airflow" "^0.1.14" + "@backstage/plugin-api-docs" "^0.8.6" + "@backstage/plugin-azure-devops" "^0.1.22" + "@backstage/plugin-badges" "^0.2.30" + "@backstage/plugin-catalog" "^1.3.0" + "@backstage/plugin-catalog-common" "^1.0.3" + "@backstage/plugin-catalog-graph" "^0.2.18" + "@backstage/plugin-catalog-import" "^0.8.9" + "@backstage/plugin-catalog-react" "^1.1.1" + "@backstage/plugin-circleci" "^0.3.6" + "@backstage/plugin-cloudbuild" "^0.3.6" + "@backstage/plugin-code-coverage" "^0.1.33" + "@backstage/plugin-cost-insights" "^0.11.28" + "@backstage/plugin-dynatrace" "^0.1.0" + "@backstage/plugin-explore" "^0.3.37" + "@backstage/plugin-gcalendar" "^0.3.2" + "@backstage/plugin-gcp-projects" "^0.3.25" + "@backstage/plugin-github-actions" "^0.5.6" + "@backstage/plugin-gocd" "^0.1.12" + "@backstage/plugin-graphiql" "^0.2.38" + "@backstage/plugin-home" "^0.4.22" + "@backstage/plugin-jenkins" "^0.7.5" + "@backstage/plugin-kafka" "^0.3.6" + "@backstage/plugin-kubernetes" "^0.6.6" + "@backstage/plugin-lighthouse" "^0.3.6" + "@backstage/plugin-newrelic" "^0.3.24" + "@backstage/plugin-newrelic-dashboard" "^0.1.14" + "@backstage/plugin-org" "^0.5.6" + "@backstage/plugin-pagerduty" "0.4.0" + "@backstage/plugin-permission-react" "^0.4.2" + "@backstage/plugin-rollbar" "^0.4.6" + "@backstage/plugin-scaffolder" "^1.3.0" + "@backstage/plugin-search" "^0.9.0" + "@backstage/plugin-search-common" "^0.3.5" + "@backstage/plugin-search-react" "^0.2.1" + "@backstage/plugin-sentry" "^0.3.44" + "@backstage/plugin-shortcuts" "^0.2.7" + "@backstage/plugin-stack-overflow" "^0.1.2" + "@backstage/plugin-tech-insights" "^0.2.2" + "@backstage/plugin-tech-radar" "^0.5.13" + "@backstage/plugin-techdocs" "^1.2.0" + "@backstage/plugin-techdocs-module-addons-contrib" "^1.0.1" + "@backstage/plugin-techdocs-react" "^1.0.1" + "@backstage/plugin-todo" "^0.2.8" + "@backstage/plugin-user-settings" "^0.4.5" "@backstage/theme" "^0.2.15" "@material-ui/core" "^4.12.2" "@material-ui/icons" "^4.9.1" @@ -11902,7 +12391,7 @@ execa@5.0.0: signal-exit "^3.0.3" strip-final-newline "^2.0.0" -execa@5.1.1, execa@^5.0.0, execa@^5.1.1: +execa@5.1.1, execa@^5.0.0: version "5.1.1" resolved "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== @@ -11930,6 +12419,21 @@ execa@^1.0.0: signal-exit "^3.0.0" strip-eof "^1.0.0" +execa@^6.1.0: + version "6.1.0" + resolved "https://registry.npmjs.org/execa/-/execa-6.1.0.tgz#cea16dee211ff011246556388effa0818394fb20" + integrity sha512-QVWlX2e50heYJcCPG0iWtf8r0xjEYfz/OYLGDYH+IyjWezzPNxz63qNFOu0l4YftGWuizFVZHHs8PrLU5p2IDA== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.1" + human-signals "^3.0.1" + is-stream "^3.0.0" + merge-stream "^2.0.0" + npm-run-path "^5.1.0" + onetime "^6.0.0" + signal-exit "^3.0.7" + strip-final-newline "^3.0.0" + executable@^4.1.1: version "4.1.1" resolved "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz#41532bff361d3e57af4d763b70582db18f5d133c" @@ -12018,7 +12522,7 @@ express-xml-bodyparser@^0.3.0: dependencies: xml2js "^0.4.11" -express@^4.17.1, express@^4.17.3: +express@^4.17.1, express@^4.17.3, express@^4.18.1: version "4.18.1" resolved "https://registry.npmjs.org/express/-/express-4.18.1.tgz#7797de8b9c72c857b9cd0e14a5eea80666267caf" integrity sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q== @@ -12149,7 +12653,7 @@ fast-equals@^2.0.0: resolved "https://registry.npmjs.org/fast-equals/-/fast-equals-2.0.4.tgz#3add9410585e2d7364c2deeb6a707beadb24b927" integrity sha512-caj/ZmjHljPrZtbzJ3kfH5ia/k4mTJe/qSiXAGzxZWRZgsgDV0cvNaQULqUX8t0/JVlzzEdYOwCN5DmzTxoD4w== -fast-glob@^3.2.9: +fast-glob@^3.2.11, fast-glob@^3.2.9: version "3.2.11" resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9" integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew== @@ -12185,7 +12689,7 @@ fast-redact@^2.0.0: resolved "https://registry.npmjs.org/fast-redact/-/fast-redact-2.1.0.tgz#dfe3c1ca69367fb226f110aa4ec10ec85462ffdf" integrity sha512-0LkHpTLyadJavq9sRzzyqIoMZemWli77K2/MGOkafrR64B9ItrvZ9aT+jluvNDsv0YEHjSNhlMBtbokuoqii4A== -fast-safe-stringify@^2.0.6, fast-safe-stringify@^2.0.7, fast-safe-stringify@^2.1.1: +fast-safe-stringify@2.1.1, fast-safe-stringify@^2.0.6, fast-safe-stringify@^2.0.7, fast-safe-stringify@^2.1.1: version "2.1.1" resolved "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz#c406a83b6e70d9e35ce3b30a81141df30aeba884" integrity sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA== @@ -12484,6 +12988,11 @@ follow-redirects@^1.0.0, follow-redirects@^1.14.0: resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.8.tgz#016996fb9a11a100566398b1c6839337d7bfa8fc" integrity sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA== +follow-redirects@^1.14.8: + version "1.15.0" + resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.0.tgz#06441868281c86d0dda4ad8bdaead2d02dca89d4" + integrity sha512-aExlJShTV4qOUOL7yF1U5tvLCB0xQuudbf6toyYA0E/acBNw71mvjFTnLaRp50aQaYocMR0a/RMMBIHeZnGyjQ== + for-in@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" @@ -12647,6 +13156,15 @@ fs-constants@^1.0.0: resolved "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== +fs-extra@10.0.1: + version "10.0.1" + resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.1.tgz#27de43b4320e833f6867cc044bfce29fdf0ef3b8" + integrity sha512-NbdoVMZso2Lsrn/QwLXOy6rm0ufY2zEOKCDzJR/0kBsb0E6qed0P3iYK+Ath3BfvXEeu4JhEtXLgILx5psUfag== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + fs-extra@10.1.0, fs-extra@^10.0.0, fs-extra@^10.0.1: version "10.1.0" resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf" @@ -12684,13 +13202,6 @@ fs-extra@^9.0.0, fs-extra@^9.1.0: jsonfile "^6.0.1" universalify "^2.0.0" -fs-minipass@^1.2.7: - version "1.2.7" - resolved "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" - integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA== - dependencies: - minipass "^2.6.0" - fs-minipass@^2.0.0, fs-minipass@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" @@ -12728,11 +13239,26 @@ function-bind@^1.1.1: resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +function.prototype.name@^1.1.5: + version "1.1.5" + resolved "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz#cce0505fe1ffb80503e6f9e46cc64e46a12a9621" + integrity sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.0" + functions-have-names "^1.2.2" + functional-red-black-tree@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= +functions-have-names@^1.2.2: + version "1.2.3" + resolved "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" + integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== + gauge@^3.0.0: version "3.0.2" resolved "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz#03bf4441c044383908bcfa0656ad91803259b395" @@ -12763,6 +13289,20 @@ gauge@^4.0.0: strip-ansi "^6.0.1" wide-align "^1.1.2" +gauge@^4.0.3: + version "4.0.4" + resolved "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz#52ff0652f2bbf607a989793d53b751bef2328dce" + integrity sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg== + dependencies: + aproba "^1.0.3 || ^2.0.0" + color-support "^1.1.3" + console-control-strings "^1.1.0" + has-unicode "^2.0.1" + signal-exit "^3.0.7" + string-width "^4.2.3" + strip-ansi "^6.0.1" + wide-align "^1.1.5" + gauge@~2.7.3: version "2.7.4" resolved "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" @@ -12788,6 +13328,17 @@ gaxios@^4.0.0: is-stream "^2.0.0" node-fetch "^2.3.0" +gaxios@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/gaxios/-/gaxios-5.0.0.tgz#df11e5d0a45831dd39eb5fbbba0d6a6b09815e70" + integrity sha512-VD/yc5ln6XU8Ch1hyYY6kRMBE0Yc2np3fPyeJeYHhrPs1i8rgnsApPMWyrugkl7LLoSqpOJVBWlQIa87OAvt8Q== + dependencies: + abort-controller "^3.0.0" + extend "^3.0.2" + https-proxy-agent "^5.0.0" + is-stream "^2.0.0" + node-fetch "^2.6.7" + gcp-metadata@^4.2.0: version "4.2.1" resolved "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-4.2.1.tgz#31849fbcf9025ef34c2297c32a89a1e7e9f2cd62" @@ -12796,6 +13347,22 @@ gcp-metadata@^4.2.0: gaxios "^4.0.0" json-bigint "^1.0.0" +gcp-metadata@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-5.0.0.tgz#a00f999f60a4461401e7c515f8a3267cfb401ee7" + integrity sha512-gfwuX3yA3nNsHSWUL4KG90UulNiq922Ukj3wLTrcnX33BB7PwB1o0ubR8KVvXu9nJH+P5w1j2SQSNNqto+H0DA== + dependencies: + gaxios "^5.0.0" + json-bigint "^1.0.0" + +gendocu-public-apis@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/gendocu-public-apis/-/gendocu-public-apis-1.0.0.tgz#ba9212207c6b4c9ba131b6cafb82deb07f068010" + integrity sha512-2XbY+joWdhBK2Z7b5NaP8+3NkOYZjHkqux83lpgdShp1QN9eu10s/MojCaJ7WiCTB7ZKIkfqUtWgWipDxDPjpw== + dependencies: + "@improbable-eng/grpc-web" "^0.14.0" + google-protobuf "^3.15.8" + generate-function@^2.3.1: version "2.3.1" resolved "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz#f069617690c10c868e73b8465746764f97c3479f" @@ -12826,13 +13393,13 @@ get-caller-file@^2.0.1, get-caller-file@^2.0.5: integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" - integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q== + version "1.1.2" + resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.2.tgz#336975123e05ad0b7ba41f152ee4aadbea6cf598" + integrity sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA== dependencies: function-bind "^1.1.1" has "^1.0.3" - has-symbols "^1.0.1" + has-symbols "^1.0.3" get-monorepo-packages@^1.1.0: version "1.2.0" @@ -12881,7 +13448,7 @@ get-stream@^5.0.0, get-stream@^5.1.0: dependencies: pump "^3.0.0" -get-stream@^6.0.0: +get-stream@^6.0.0, get-stream@^6.0.1: version "6.0.1" resolved "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== @@ -13075,10 +13642,10 @@ globals@^11.1.0, globals@^11.12.0: resolved "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== -globals@^13.6.0, globals@^13.9.0: - version "13.9.0" - resolved "https://registry.npmjs.org/globals/-/globals-13.9.0.tgz#4bf2bf635b334a173fb1daf7c5e6b218ecdc06cb" - integrity sha512-74/FduwI/JaIrr1H8e71UbDE+5x7pIPs1C2rrwC52SszOo043CsWOZEMW7o2Y58xwm9b+0RBKDxY5n2sUpEFxA== +globals@^13.15.0: + version "13.15.0" + resolved "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz#38113218c907d2f7e98658af246cef8b77e90bac" + integrity sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog== dependencies: type-fest "^0.20.2" @@ -13113,7 +13680,7 @@ globby@^7.1.1: pify "^3.0.0" slash "^1.0.0" -google-auth-library@^7.14.1, google-auth-library@^7.6.1: +google-auth-library@^7.14.0: version "7.14.1" resolved "https://registry.npmjs.org/google-auth-library/-/google-auth-library-7.14.1.tgz#e3483034162f24cc71b95c8a55a210008826213c" integrity sha512-5Rk7iLNDFhFeBYc3s8l1CqzbEBcdhwR193RlD4vSNFajIcINKI8W8P0JLmBpwymHqqWbX34pJDQu39cSy/6RsA== @@ -13128,25 +13695,59 @@ google-auth-library@^7.14.1, google-auth-library@^7.6.1: jws "^4.0.0" lru-cache "^6.0.0" -google-gax@^2.24.1: - version "2.28.1" - resolved "https://registry.npmjs.org/google-gax/-/google-gax-2.28.1.tgz#99bc234b5769d901d70959d40bd1651729eb4a34" - integrity sha512-2Xjd3FrjlVd6Cmw2B2Aicpc/q92SwTpIOvxPUlnRg9w+Do8nu7UR+eQrgoKlo2FIUcUuDTvppvcx8toND0pK9g== +google-auth-library@^8.0.0, google-auth-library@^8.0.1, google-auth-library@^8.0.2: + version "8.0.2" + resolved "https://registry.npmjs.org/google-auth-library/-/google-auth-library-8.0.2.tgz#5fa0f2d3795c3e4019d2bb315ade4454cc9c30b5" + integrity sha512-HoG+nWFAThLovKpvcbYzxgn+nBJPTfAwtq0GxPN821nOO+21+8oP7MoEHfd1sbDulUFFGfcjJr2CnJ4YssHcyg== dependencies: - "@grpc/grpc-js" "~1.4.0" - "@grpc/proto-loader" "^0.6.1" + arrify "^2.0.0" + base64-js "^1.3.0" + ecdsa-sig-formatter "^1.0.11" + fast-text-encoding "^1.0.0" + gaxios "^5.0.0" + gcp-metadata "^5.0.0" + gtoken "^5.3.2" + jws "^4.0.0" + lru-cache "^6.0.0" + +google-gax@^2.24.1: + version "2.30.5" + resolved "https://registry.npmjs.org/google-gax/-/google-gax-2.30.5.tgz#e836f984f3228900a8336f608c83d75f9cb73eff" + integrity sha512-Jey13YrAN2hfpozHzbtrwEfEHdStJh1GwaQ2+Akh1k0Tv/EuNVSuBtHZoKSBm5wBMvNsxTsEIZ/152NrYyZgxQ== + dependencies: + "@grpc/grpc-js" "~1.6.0" + "@grpc/proto-loader" "^0.6.12" "@types/long" "^4.0.0" abort-controller "^3.0.0" duplexify "^4.0.0" fast-text-encoding "^1.0.3" - google-auth-library "^7.6.1" + google-auth-library "^7.14.0" is-stream-ended "^0.1.4" node-fetch "^2.6.1" - object-hash "^2.1.1" - proto3-json-serializer "^0.1.5" - protobufjs "6.11.2" + object-hash "^3.0.0" + proto3-json-serializer "^0.1.8" + protobufjs "6.11.3" retry-request "^4.0.0" +google-gax@^3.0.1: + version "3.0.3" + resolved "https://registry.npmjs.org/google-gax/-/google-gax-3.0.3.tgz#c8f75dc0ec044fc8ecf97675d85b77911539cca5" + integrity sha512-oS1x9DWOmC2xX6pl2W92KtyR50x1zh1aEh0dxUCpu7GCv62aQ/MMkax2kJb/T6OKj/Yn8MYT8bIGyU+trk/p2Q== + dependencies: + "@grpc/grpc-js" "~1.6.0" + "@grpc/proto-loader" "^0.6.12" + "@types/long" "^4.0.0" + abort-controller "^3.0.0" + duplexify "^4.0.0" + fast-text-encoding "^1.0.3" + google-auth-library "^8.0.2" + is-stream-ended "^0.1.4" + node-fetch "^2.6.1" + object-hash "^3.0.0" + proto3-json-serializer "^1.0.0" + protobufjs "6.11.3" + retry-request "^5.0.0" + google-p12-pem@^3.0.3: version "3.1.3" resolved "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-3.1.3.tgz#5497998798ee86c2fc1f4bb1f92b7729baf37537" @@ -13154,6 +13755,18 @@ google-p12-pem@^3.0.3: dependencies: node-forge "^1.0.0" +google-p12-pem@^3.1.3: + version "3.1.4" + resolved "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-3.1.4.tgz#123f7b40da204de4ed1fbf2fd5be12c047fc8b3b" + integrity sha512-HHuHmkLgwjdmVRngf5+gSmpkyaRI6QmOg77J8tkNBHhNEI62sGHyw4/+UkgyZEI7h84NbWprXDJ+sa3xOYFvTg== + dependencies: + node-forge "^1.3.1" + +google-protobuf@^3.15.8, google-protobuf@^3.19.1: + version "3.20.1" + resolved "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.20.1.tgz#1b255c2b59bcda7c399df46c65206aa3c7a0ce8b" + integrity sha512-XMf1+O32FjYIV3CYu6Tuh5PNbfNEU5Xu22X+Xkdb/DUexFlCzhvv7d5Iirm4AOwn8lv4al1YvIhzGrg2j9Zfzw== + got@11.8.3, got@^11.8.0: version "11.8.3" resolved "https://registry.npmjs.org/got/-/got-11.8.3.tgz#f496c8fdda5d729a90b4905d2b07dbd148170770" @@ -13188,12 +13801,12 @@ got@^9.6.0: to-readable-stream "^1.0.0" url-parse-lax "^3.0.0" -graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.5, graceful-fs@^4.2.2, graceful-fs@^4.2.3, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: +graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.2.2, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: version "4.2.9" resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz#041b05df45755e587a24942279b9d113146e1c96" integrity sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ== -graceful-fs@^4.1.6, graceful-fs@^4.2.0: +graceful-fs@^4.1.2, graceful-fs@^4.1.5, graceful-fs@^4.1.6, graceful-fs@^4.2.0: version "4.2.10" resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== @@ -13204,19 +13817,15 @@ grapheme-splitter@^1.0.4: integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== graphiql@^1.5.12, graphiql@^1.8.8: - version "1.8.10" - resolved "https://registry.npmjs.org/graphiql/-/graphiql-1.8.10.tgz#a45e80a896b802f9591a89aa9af90fdafe44d5d8" - integrity sha512-ZYGsBzJz3OWh30eRr5D07ETHLqUEgfOddKdMLHgivLRmWVG6paRNOPzgwFyXy49JwL6gTzVmZoHabml7yySCnA== + version "1.9.9" + resolved "https://registry.npmjs.org/graphiql/-/graphiql-1.9.9.tgz#dfc1760e7552140247e591e3c66d1ccc389ea71f" + integrity sha512-eJ5kGpXFXw69QG/1SJrQs/mzGpDIv4XeGqohGSKQCOh7NtsAsQIxs/VMVR8Lvv9tx6YiIFUvAHp997NlyOwHbQ== dependencies: - "@graphiql/toolkit" "^0.4.5" - codemirror "^5.65.3" - codemirror-graphql "^1.3.0" - copy-to-clipboard "^3.2.0" + "@graphiql/react" "^0.4.2" + "@graphiql/toolkit" "^0.6.0" entities "^2.0.0" - escape-html "^1.0.3" - graphql-language-service "^5.0.4" + graphql-language-service "^5.0.6" markdown-it "^12.2.0" - set-value "^4.1.0" graphlib@^2.1.8: version "2.1.8" @@ -13247,10 +13856,10 @@ graphql-executor@0.0.23: resolved "https://registry.npmjs.org/graphql-executor/-/graphql-executor-0.0.23.tgz#205c1764b39ee0fcf611553865770f37b45851a2" integrity sha512-3Ivlyfjaw3BWmGtUSnMpP/a4dcXCp0mJtj0PiPG14OKUizaMKlSEX+LX2Qed0LrxwniIwvU6B4w/koVjEPyWJg== -graphql-language-service@^5.0.4: - version "5.0.4" - resolved "https://registry.npmjs.org/graphql-language-service/-/graphql-language-service-5.0.4.tgz#068dd4ff5dc6fcc6c8560ccc8f05311d5fd63fcd" - integrity sha512-lX+ahYBwvTHJe1N7JqA08moNwbr0RWaFILxVnbciaaeb469TTIhQi87ZgVJ/y9Szre4d0r3vjIt2EstwafzcDA== +graphql-language-service@^5.0.6: + version "5.0.6" + resolved "https://registry.npmjs.org/graphql-language-service/-/graphql-language-service-5.0.6.tgz#7fd1e6479e5c3074b070c760fa961d9ad1ed7c72" + integrity sha512-FjE23aTy45Lr5metxCv3ZgSKEZOzN7ERR+OFC1isV5mHxI0Ob8XxayLTYjQKrs8b3kOpvgTYmSmu6AyXOzYslg== dependencies: nullthrows "^1.0.0" vscode-languageserver-types "^3.15.1" @@ -13287,14 +13896,9 @@ graphql-type-json@^0.3.2: integrity sha512-J+vjof74oMlCWXSvt0DOf2APEdZOCdubEvGDUAlqH//VBYcOYsGgRW7Xzorr44LvkjiuvecWc8fChxuZZbChtg== graphql-ws@^5.4.1: - version "5.5.5" - resolved "https://registry.npmjs.org/graphql-ws/-/graphql-ws-5.5.5.tgz#f375486d3f196e2a2527b503644693ae3a8670a9" - integrity sha512-hvyIS71vs4Tu/yUYHPvGXsTgo0t3arU820+lT5VjZS2go0ewp2LqyCgxEN56CzOG7Iys52eRhHBiD1gGRdiQtw== - -graphql@^15.5.1: - version "15.8.0" - resolved "https://registry.npmjs.org/graphql/-/graphql-15.8.0.tgz#33410e96b012fa3bdb1091cc99a94769db212b38" - integrity sha512-5gghUc24tP9HRznNpV2+FIoq3xKkj5dTQqf4v0CpdPbFVwFkWoxOM+o+2OC9ZSvjEMTjfmG9QT+gcvggTwW1zw== + version "5.8.2" + resolved "https://registry.npmjs.org/graphql-ws/-/graphql-ws-5.8.2.tgz#800184b1addb20b3010dc06cb70877703a5fff20" + integrity sha512-hYo8kTGzxePFJtMGC7Y4cbypwifMphIJJ7n4TDcVUAfviRwQBnmZAbfZlC+XFwWDUaR7raEDQPxWctpccmE0JQ== graphql@^16.0.0, graphql@^16.3.0: version "16.5.0" @@ -13306,6 +13910,26 @@ grouped-queue@^2.0.0: resolved "https://registry.npmjs.org/grouped-queue/-/grouped-queue-2.0.0.tgz#a2c6713f2171e45db2c300a3a9d7c119d694dac8" integrity sha512-/PiFUa7WIsl48dUeCvhIHnwNmAAzlI/eHoJl0vu3nsFA366JleY7Ff8EVTplZu5kO0MIdZjKTTnzItL61ahbnw== +grpc-docs@^1.0.6, grpc-docs@^1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/grpc-docs/-/grpc-docs-1.1.2.tgz#021d065f8a386bdc07d16f80a8ab604cddefba06" + integrity sha512-IUQJpDNgCgq5aL1OWkcvK2Yeky0tscEOI0rDLb4fNxXAOgamrsEI/14aNX+DRzGq3gs/NTF9xU7YqN2kj3OiFA== + dependencies: + "@improbable-eng/grpc-web" "^0.15.0" + "@types/minimatch" "^3.0.5" + gendocu-public-apis "^1.0.0" + google-protobuf "^3.19.1" + grpc-docs "^1.0.6" + js-yaml "^4.1.0" + minimatch "^3.0.4" + react-copy-to-clipboard "^5.0.4" + react-dom "^16.13.1" + react-is "^16.8.0" + react-syntax-highlighter "^15.4.5" + rollup "^0.60" + rollup-plugin-smart-asset "^2.1.2" + styled-components "^5.3.3" + gtoken@^5.0.4: version "5.1.0" resolved "https://registry.npmjs.org/gtoken/-/gtoken-5.1.0.tgz#4ba8d2fc9a8459098f76e7e8fd7beaa39fda9fe4" @@ -13316,6 +13940,15 @@ gtoken@^5.0.4: jws "^4.0.0" mime "^2.2.0" +gtoken@^5.3.2: + version "5.3.2" + resolved "https://registry.npmjs.org/gtoken/-/gtoken-5.3.2.tgz#deb7dc876abe002178e0515e383382ea9446d58f" + integrity sha512-gkvEKREW7dXWF8NV8pVrKfW7WqReAmjjkMBh6lNCCGOM4ucS0r0YyXXl0r/9Yj8wcW/32ISkfc8h5mPTDbtifQ== + dependencies: + gaxios "^4.0.0" + google-p12-pem "^3.1.3" + jws "^4.0.0" + gzip-size@^6.0.0: version "6.0.0" resolved "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz#065367fd50c239c0671cbcbad5be3e2eeb10e462" @@ -13370,10 +14003,10 @@ has-ansi@^2.0.0: dependencies: ansi-regex "^2.0.0" -has-bigints@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113" - integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA== +has-bigints@^1.0.1, has-bigints@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" + integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== has-binary2@~1.0.2: version "1.0.3" @@ -13390,17 +14023,24 @@ has-cors@1.1.0: has-flag@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" - integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== has-flag@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== -has-symbols@^1.0.1, has-symbols@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" - integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== +has-property-descriptors@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861" + integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ== + dependencies: + get-intrinsic "^1.1.1" + +has-symbols@^1.0.1, has-symbols@^1.0.2, has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== has-tostringtag@^1.0.0: version "1.0.0" @@ -13470,11 +14110,6 @@ hash-it@^5.0.0: resolved "https://registry.npmjs.org/hash-it/-/hash-it-5.0.2.tgz#8cc981944964a5124f74f1065af0c0a5d182d556" integrity sha512-csU3E/a9QEmEgPPxoShVuMcFWM329IGioEPRvYVBv3r5BFrU8pCfnk3jGEVvriAcwqd+nl6KsNhPPjg8MUzkhQ== -hash-stream-validation@^0.2.2: - version "0.2.4" - resolved "https://registry.npmjs.org/hash-stream-validation/-/hash-stream-validation-0.2.4.tgz#ee68b41bf822f7f44db1142ec28ba9ee7ccb7512" - integrity sha512-Gjzu0Xn7IagXVkSu9cSFuK1fqzwtLwFhNhVL8IFJijRNMgUttFbBSIAzKuSIrsFMO1+g1RlsoN49zPIbwPDMGQ== - hash.js@^1.0.0, hash.js@^1.0.3: version "1.1.7" resolved "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" @@ -13522,15 +14157,10 @@ headers-polyfill@^3.0.4: resolved "https://registry.npmjs.org/headers-polyfill/-/headers-polyfill-3.0.7.tgz#725c4f591e6748f46b036197eae102c92b959ff4" integrity sha512-JoLCAdCEab58+2/yEmSnOlficyHFpIl0XJqwu3l+Unkm1gXpFUYsThz6Yha3D6tNhocWkCPfyW0YVIGWFqTi7w== -headers-utils@^3.0.2: - version "3.0.2" - resolved "https://registry.npmjs.org/headers-utils/-/headers-utils-3.0.2.tgz#dfc65feae4b0e34357308aefbcafa99c895e59ef" - integrity sha512-xAxZkM1dRyGV2Ou5bzMxBPNLoRCjcX+ya7KSWybQD2KwLphxsapUVK6x/02o7f4VU6GPSXch9vNY2+gkU8tYWQ== - helmet@^5.0.2: - version "5.0.2" - resolved "https://registry.npmjs.org/helmet/-/helmet-5.0.2.tgz#3264ec6bab96c82deaf65e3403c369424cb2366c" - integrity sha512-QWlwUZZ8BtlvwYVTSDTBChGf8EOcQ2LkGMnQJxSzD1mUu8CCjXJZq/BXP8eWw4kikRnzlhtYo3lCk0ucmYA3Vg== + version "5.1.0" + resolved "https://registry.npmjs.org/helmet/-/helmet-5.1.0.tgz#e98a5d4bf89ab8119c856018a3bcc82addadcd47" + integrity sha512-klsunXs8rgNSZoaUrNeuCiWUxyc+wzucnEnFejUg3/A+CaF589k9qepLZZ1Jehnzig7YbD4hEuscGXuBY3fq+g== hexoid@1.0.0: version "1.0.0" @@ -13566,7 +14196,7 @@ hogan.js@^3.0.2: mkdirp "0.3.0" nopt "1.0.10" -hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2: +hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2: version "3.3.2" resolved "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== @@ -13597,6 +14227,13 @@ hosted-git-info@^4.0.0, hosted-git-info@^4.0.1: dependencies: lru-cache "^6.0.0" +hosted-git-info@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-5.0.0.tgz#df7a06678b4ebd722139786303db80fdf302ea56" + integrity sha512-rRnjWu0Bxj+nIfUOkz0695C0H6tRrN5iYIzYejb0tDEefe2AekHu/U5Kn9pEie5vsJqpNQU02az7TGSH3qpz4Q== + dependencies: + lru-cache "^7.5.1" + hpack.js@^2.1.6: version "2.1.6" resolved "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2" @@ -13802,10 +14439,15 @@ human-signals@^2.1.0: resolved "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== +human-signals@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/human-signals/-/human-signals-3.0.1.tgz#c740920859dafa50e5a3222da9d3bf4bb0e5eef5" + integrity sha512-rQLskxnM/5OCldHo+wNXbpVgDn5A17CUoKX+7Sokwaknlq7CdSnphy0W39GU8dw59XiCXmFXDg4fRuckQRKewQ== + humanize-duration@^3.25.1, humanize-duration@^3.26.0, humanize-duration@^3.27.0, humanize-duration@^3.27.1: - version "3.27.1" - resolved "https://registry.npmjs.org/humanize-duration/-/humanize-duration-3.27.1.tgz#2cd4ea4b03bd92184aee6d90d77a8f3d7628df69" - integrity sha512-jCVkMl+EaM80rrMrAPl96SGG4NRac53UyI1o/yAzebDntEY6K6/Fj2HOjdPg8omTqIe5Y0wPBai2q5xXrIbarA== + version "3.27.2" + resolved "https://registry.npmjs.org/humanize-duration/-/humanize-duration-3.27.2.tgz#4b4e565bec098d22c9a54344e16156d1c649f160" + integrity sha512-A15OmA3FLFRnehvF4ZMocsxTZYvHq4ze7L+AgR1DeHw0xC9vMd4euInY83uqGU9/XXKNnVIEeKc1R8G8nKqtzg== humanize-ms@^1.2.1: version "1.2.1" @@ -13912,9 +14554,9 @@ immer@9.0.7: integrity sha512-KGllzpbamZDvOIxnmJ0jI840g7Oikx58lBPWV0hUh7dtAyZpFqqrBZdKka5GlTwMTZ1Tjc/bKKW4VSFAt6BqMA== immer@^9.0.1, immer@^9.0.7: - version "9.0.14" - resolved "https://registry.npmjs.org/immer/-/immer-9.0.14.tgz#e05b83b63999d26382bb71676c9d827831248a48" - integrity sha512-ubBeqQutOSLIFCUBN03jGeOS6a3DoYlSYwYJTa+gSKEZKU5redJIqkIdZ3JVv/4RZpfcXdAWH5zCNLWPRv2WDw== + version "9.0.15" + resolved "https://registry.npmjs.org/immer/-/immer-9.0.15.tgz#0b9169e5b1d22137aba7d43f8a81a495dd1b62dc" + integrity sha512-2eB/sswms9AEUSkOm4SbV5Y7Vmt/bKRwByd52jfLkW4OLYeaTP3EEiJ9agqU0O/tq6Dk62Zfj+TJSqfm1rLVGQ== immutable@^3.x.x: version "3.8.2" @@ -14067,6 +14709,26 @@ inline-style-prefixer@^6.0.0: dependencies: css-in-js-utils "^2.0.0" +inquirer@8.2.2: + version "8.2.2" + resolved "https://registry.npmjs.org/inquirer/-/inquirer-8.2.2.tgz#1310517a87a0814d25336c78a20b44c3d9b7629d" + integrity sha512-pG7I/si6K/0X7p1qU+rfWnpTE1UIkTONN1wxtzh0d+dHXtT/JG6qBgLxoyHVsQa8cFABxAPh0pD6uUUHiAoaow== + dependencies: + ansi-escapes "^4.2.1" + chalk "^4.1.1" + cli-cursor "^3.1.0" + cli-width "^3.0.0" + external-editor "^3.0.3" + figures "^3.0.0" + lodash "^4.17.21" + mute-stream "0.0.8" + ora "^5.4.1" + run-async "^2.4.0" + rxjs "^7.5.5" + string-width "^4.1.0" + strip-ansi "^6.0.0" + through "^2.3.6" + inquirer@^7.3.3: version "7.3.3" resolved "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz#04d176b2af04afc157a83fd7c100e98ee0aad003" @@ -14086,7 +14748,7 @@ inquirer@^7.3.3: strip-ansi "^6.0.0" through "^2.3.6" -inquirer@^8.0.0, inquirer@^8.1.1, inquirer@^8.2.0: +inquirer@^8.0.0, inquirer@^8.2.0: version "8.2.4" resolved "https://registry.npmjs.org/inquirer/-/inquirer-8.2.4.tgz#ddbfe86ca2f67649a67daa6f1051c128f684f0b4" integrity sha512-nn4F01dxU8VeKfq192IjLsxu0/OmMZ4Lg3xKAns148rCaXP6ntAoEkVYZThWjwON8AlzdZZi6oqnhNbxUG9hVg== @@ -14242,7 +14904,7 @@ is-arguments@^1.0.4: is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" - integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= + integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== is-arrayish@^0.3.1: version "0.3.2" @@ -14250,9 +14912,11 @@ is-arrayish@^0.3.1: integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== is-bigint@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.1.tgz#6923051dfcbc764278540b9ce0e6b3213aa5ebc2" - integrity sha512-J0ELF4yHFxHy0cmSxZuheDOz2luOdVvqjwmEcj8H/L1JHeuEDSDbeRP+Dk9kFVk5RTFzbucJ2Kb9F7ixY2QaCg== + version "1.0.4" + resolved "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" + integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== + dependencies: + has-bigints "^1.0.1" is-binary-path@~2.1.0: version "2.1.0" @@ -14262,11 +14926,12 @@ is-binary-path@~2.1.0: binary-extensions "^2.0.0" is-boolean-object@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.0.tgz#e2aaad3a3a8fca34c28f6eee135b156ed2587ff0" - integrity sha512-a7Uprx8UtD+HWdyYwnD1+ExtTgqQtD2k/1yJgtXP6wnMm8byhkoTZRl+95LLThpzNZJ5aEvi46cdH+ayMFRwmA== + version "1.1.2" + resolved "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" + integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== dependencies: - call-bind "^1.0.0" + call-bind "^1.0.2" + has-tostringtag "^1.0.0" is-buffer@^1.1.5: version "1.1.6" @@ -14304,14 +14969,14 @@ is-ci@^3.0.0, is-ci@^3.0.1: dependencies: ci-info "^3.2.0" -is-core-module@^2.1.0, is-core-module@^2.2.0, is-core-module@^2.8.0: +is-core-module@^2.1.0, is-core-module@^2.2.0: version "2.8.1" resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211" integrity sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA== dependencies: has "^1.0.3" -is-core-module@^2.8.1: +is-core-module@^2.8.0, is-core-module@^2.8.1: version "2.9.0" resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz#e1c34429cd51c6dd9e09e0799e396e27b19a9c69" integrity sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A== @@ -14333,9 +14998,11 @@ is-data-descriptor@^1.0.0: kind-of "^6.0.0" is-date-object@^1.0.1: - version "1.0.2" - resolved "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e" - integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g== + version "1.0.5" + resolved "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" + integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== + dependencies: + has-tostringtag "^1.0.0" is-decimal@^1.0.0: version "1.0.4" @@ -14484,10 +15151,10 @@ is-module@^1.0.0: resolved "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" integrity sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE= -is-negative-zero@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24" - integrity sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w== +is-negative-zero@^2.0.1, is-negative-zero@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" + integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== is-node-process@^1.0.1: version "1.0.1" @@ -14500,9 +15167,11 @@ is-npm@^5.0.0: integrity sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA== is-number-object@^1.0.4: - version "1.0.4" - resolved "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.4.tgz#36ac95e741cf18b283fc1ddf5e83da798e3ec197" - integrity sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw== + version "1.0.7" + resolved "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz#59d50ada4c45251784e9904f5246c742f07a42fc" + integrity sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ== + dependencies: + has-tostringtag "^1.0.0" is-number@^3.0.0: version "3.0.0" @@ -14646,10 +15315,12 @@ is-scoped@^2.1.0: dependencies: scoped-regex "^2.0.0" -is-shared-array-buffer@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz#97b0c85fbdacb59c9c446fe653b82cf2b5b7cfe6" - integrity sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA== +is-shared-array-buffer@^1.0.1, is-shared-array-buffer@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79" + integrity sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA== + dependencies: + call-bind "^1.0.2" is-ssh@^1.3.0: version "1.3.1" @@ -14673,6 +15344,11 @@ is-stream@^2.0.0: resolved "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== +is-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz#e6bfd7aa6bef69f4f472ce9bb681e3e57b4319ac" + integrity sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA== + is-string@^1.0.5, is-string@^1.0.7: version "1.0.7" resolved "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" @@ -14681,18 +15357,18 @@ is-string@^1.0.5, is-string@^1.0.7: has-tostringtag "^1.0.0" is-subdir@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/is-subdir/-/is-subdir-1.1.1.tgz#423e66902f9c5f159b9cc4826c820df083059538" - integrity sha512-VYpq0S7gPBVkkmfwkvGnx1EL9UVIo87NQyNcgMiNUdQCws3CJm5wj2nB+XPL7zigvjxhuZgp3bl2yBcKkSIj1w== + version "1.2.0" + resolved "https://registry.npmjs.org/is-subdir/-/is-subdir-1.2.0.tgz#b791cd28fab5202e91a08280d51d9d7254fd20d4" + integrity sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw== dependencies: better-path-resolve "1.0.0" is-symbol@^1.0.2, is-symbol@^1.0.3: - version "1.0.3" - resolved "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" - integrity sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ== + version "1.0.4" + resolved "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" + integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== dependencies: - has-symbols "^1.0.1" + has-symbols "^1.0.2" is-text-path@^1.0.1: version "1.0.1" @@ -14741,12 +15417,12 @@ is-utf8@^0.2.0, is-utf8@^0.2.1: resolved "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= -is-weakref@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.1.tgz#842dba4ec17fa9ac9850df2d6efbc1737274f2a2" - integrity sha512-b2jKc2pQZjaeFYWEf7ScFj+Be1I+PXmlu572Q8coTXZ+LD/QQZ7ShPMst8h16riVgyXTQwUsFEl74mDvc/3MHQ== +is-weakref@^1.0.1, is-weakref@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" + integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== dependencies: - call-bind "^1.0.0" + call-bind "^1.0.2" is-window@^1.0.2: version "1.0.2" @@ -14798,7 +15474,7 @@ isbinaryfile@^5.0.0: isexe@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== isobject@^2.0.0: version "2.1.0" @@ -14842,9 +15518,9 @@ isomorphic-form-data@^2.0.0: form-data "^2.3.2" isomorphic-git@^1.8.0: - version "1.17.2" - resolved "https://registry.npmjs.org/isomorphic-git/-/isomorphic-git-1.17.2.tgz#d49eda3122dfa3b860f0c68d651297c6403b715f" - integrity sha512-KtYyJye7Ad1XSgObsw7dwn3OaEcGUy04BHv6D+qBz2BWY7T7q7HXBzoSOiVvVMbOo+K2EZ2MUT+g2QkpJCPQLQ== + version "1.17.3" + resolved "https://registry.npmjs.org/isomorphic-git/-/isomorphic-git-1.17.3.tgz#d4a4e7a2defc7e9c80d4d0bfd43c1e7d2bfb25ee" + integrity sha512-jEQtmg1lJ8ZiJLjJCCJDDIdXaeoHwqHFY7QCLgNw7GzZ6MktXLzKXnQsFRfIcm7sNYGt+w1/6FQTwO9zoHq/Fw== dependencies: async-lock "^1.1.0" clean-git-ref "^2.0.1" @@ -14915,6 +15591,11 @@ istanbul-reports@^3.1.3: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" +iterare@1.2.1: + version "1.2.1" + resolved "https://registry.npmjs.org/iterare/-/iterare-1.2.1.tgz#139c400ff7363690e33abffa33cbba8920f00042" + integrity sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q== + jake@^10.8.5: version "10.8.5" resolved "https://registry.npmjs.org/jake/-/jake-10.8.5.tgz#f2183d2c59382cb274226034543b9c03b8164c46" @@ -15728,7 +16409,7 @@ jsonc-parser@^3.0.0: jsonfile@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" - integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= + integrity sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg== optionalDependencies: graceful-fs "^4.1.6" @@ -15979,9 +16660,9 @@ keyv@^3.0.0: json-buffer "3.0.0" keyv@^4.0.0, keyv@^4.0.3: - version "4.2.9" - resolved "https://registry.npmjs.org/keyv/-/keyv-4.2.9.tgz#b8f25d4968b583ed7f07fceadab646d4baadad6b" - integrity sha512-vqRBrN4xQHud7UMAGzGGFbt96MtGB9pb0OOg8Dhtq5RtiswCb1pCFq878iqC4hdeOP6eDPnCoFxA+2TXx427Ow== + version "4.3.1" + resolved "https://registry.npmjs.org/keyv/-/keyv-4.3.1.tgz#7970672f137d987945821b1a07b524ce5a4edd27" + integrity sha512-nwP7AQOxFzELXsNq3zCx/oh81zu4DHWwCE6W9RaeHb7OHO0JpmKS8n801ovVQC7PTsZDWtPA5j1QY+/WWtARYg== dependencies: compress-brotli "^1.3.8" json-buffer "3.0.1" @@ -16015,7 +16696,7 @@ kleur@^3.0.3: resolved "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== -kleur@^4.0.3: +kleur@^4.0.3, kleur@^4.1.4: version "4.1.4" resolved "https://registry.npmjs.org/kleur/-/kleur-4.1.4.tgz#8c202987d7e577766d039a8cd461934c01cda04d" integrity sha512-8QADVssbrFjivHWQU7KkMgptGTl6WAcSdlbBPY4uNF+mWr6DGcKrvY2w4FQJoXch7+fKMjj0dRrL75vk3k23OA== @@ -16126,27 +16807,27 @@ leasot@^12.0.0: strip-ansi "^6.0.0" text-table "^0.2.0" -lerna@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/lerna/-/lerna-4.0.0.tgz#b139d685d50ea0ca1be87713a7c2f44a5b678e9e" - integrity sha512-DD/i1znurfOmNJb0OBw66NmNqiM8kF6uIrzrJ0wGE3VNdzeOhz9ziWLYiRaZDGGwgbcjOo6eIfcx9O5Qynz+kg== +lerna@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/lerna/-/lerna-5.0.0.tgz#077e35d41fcead5ea223af1862dc25475e1aaf2a" + integrity sha512-dUYmJ7H9k/xHtwKpQWLTNUa1jnFUiW4o4K2LFkRchlIijoIUT4yK/RprIxNvYCrLrEaOdZryvY5UZvSHI2tBxA== dependencies: - "@lerna/add" "4.0.0" - "@lerna/bootstrap" "4.0.0" - "@lerna/changed" "4.0.0" - "@lerna/clean" "4.0.0" - "@lerna/cli" "4.0.0" - "@lerna/create" "4.0.0" - "@lerna/diff" "4.0.0" - "@lerna/exec" "4.0.0" - "@lerna/import" "4.0.0" - "@lerna/info" "4.0.0" - "@lerna/init" "4.0.0" - "@lerna/link" "4.0.0" - "@lerna/list" "4.0.0" - "@lerna/publish" "4.0.0" - "@lerna/run" "4.0.0" - "@lerna/version" "4.0.0" + "@lerna/add" "5.0.0" + "@lerna/bootstrap" "5.0.0" + "@lerna/changed" "5.0.0" + "@lerna/clean" "5.0.0" + "@lerna/cli" "5.0.0" + "@lerna/create" "5.0.0" + "@lerna/diff" "5.0.0" + "@lerna/exec" "5.0.0" + "@lerna/import" "5.0.0" + "@lerna/info" "5.0.0" + "@lerna/init" "5.0.0" + "@lerna/link" "5.0.0" + "@lerna/list" "5.0.0" + "@lerna/publish" "5.0.0" + "@lerna/run" "5.0.0" + "@lerna/version" "5.0.0" import-local "^3.0.2" npmlog "^4.1.2" @@ -16202,15 +16883,20 @@ libnpmpublish@^4.0.0: semver "^7.1.3" ssri "^8.0.0" -lilconfig@2.0.4, lilconfig@^2.0.3: +lilconfig@2.0.5: + version "2.0.5" + resolved "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.5.tgz#19e57fd06ccc3848fd1891655b5a447092225b25" + integrity sha512-xaYmXZtTHPAw5m+xLN8ab9C+3a8YmV3asNSPOATITbtwrfbwaLJj8h66H1WMIpALCkqsIzK3h7oQ+PdX+LQ9Eg== + +lilconfig@^2.0.3: version "2.0.4" resolved "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.4.tgz#f4507d043d7058b380b6a8f5cb7bcd4b34cee082" integrity sha512-bfTIN7lEsiooCocSISTWXkiWJkRqtL9wYtYy+8EK3Y41qh3mpwPU0ycTOgjdY9ErwXCc8QyrQp82bdL0Xkm9yA== lines-and-columns@^1.1.6: - version "1.1.6" - resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" - integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= + version "1.2.4" + resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" + integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== linkify-it@^3.0.1: version "3.0.2" @@ -16219,25 +16905,24 @@ linkify-it@^3.0.1: dependencies: uc.micro "^1.0.1" -lint-staged@^12.2.0: - version "12.4.1" - resolved "https://registry.npmjs.org/lint-staged/-/lint-staged-12.4.1.tgz#63fa27bfc8a33515f6902f63f6670864f1fb233c" - integrity sha512-PTXgzpflrQ+pODQTG116QNB+Q6uUTDg5B5HqGvNhoQSGt8Qy+MA/6zSnR8n38+sxP5TapzeQGTvoKni0KRS8Vg== +lint-staged@^13.0.0: + version "13.0.2" + resolved "https://registry.npmjs.org/lint-staged/-/lint-staged-13.0.2.tgz#35a1c57130e9ad5b1dea784972a40777ba433dd5" + integrity sha512-qQLfLTh9z34eMzfEHENC+QBskZfxjomrf+snF3xJ4BzilORbD989NLqQ00ughsF/A+PT41e87+WsMFabf9++pQ== dependencies: cli-truncate "^3.1.0" - colorette "^2.0.16" - commander "^8.3.0" - debug "^4.3.3" - execa "^5.1.1" - lilconfig "2.0.4" - listr2 "^4.0.1" - micromatch "^4.0.4" + colorette "^2.0.17" + commander "^9.3.0" + debug "^4.3.4" + execa "^6.1.0" + lilconfig "2.0.5" + listr2 "^4.0.5" + micromatch "^4.0.5" normalize-path "^3.0.0" - object-inspect "^1.12.0" - pidtree "^0.5.0" + object-inspect "^1.12.2" + pidtree "^0.6.0" string-argv "^0.3.1" - supports-color "^9.2.1" - yaml "^1.10.2" + yaml "^2.1.1" listenercount@~1.0.1: version "1.0.1" @@ -16287,7 +16972,7 @@ listr2@^3.8.3: through "^2.3.8" wrap-ansi "^7.0.0" -listr2@^4.0.1: +listr2@^4.0.5: version "4.0.5" resolved "https://registry.npmjs.org/listr2/-/listr2-4.0.5.tgz#9dcc50221583e8b4c71c43f9c7dfd0ef546b75d5" integrity sha512-juGHV1doQdpNT3GSTs9IUN43QJb7KHdF9uqg7Vufs/tG9VTzpFphqF4pm/ICdAABGQxsyNn9CiYA3StkI6jpwA== @@ -16592,7 +17277,7 @@ lodash.sortby@^4.7.0: lodash.startcase@^4.4.0: version "4.4.0" resolved "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz#9436e34ed26093ed7ffae1936144350915d9add8" - integrity sha1-lDbjTtJgk+1/+uGTYUQ1CRXZrdg= + integrity sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg== lodash.template@^4.5.0: version "4.5.0" @@ -16624,7 +17309,7 @@ lodash.uniq@^4.5.0: resolved "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= -lodash@^4.17.10, lodash@^4.17.13, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.7.0, lodash@~4.17.0, lodash@~4.17.15, lodash@~4.17.4: +lodash@4.17.21, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.7.0, lodash@~4.17.0, lodash@~4.17.15, lodash@~4.17.4: version "4.17.21" resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -16750,6 +17435,11 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" +lru-cache@^7.10.1, lru-cache@^7.4.4, lru-cache@^7.5.1, lru-cache@^7.7.1: + version "7.10.1" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-7.10.1.tgz#db577f42a94c168f676b638d15da8fb073448cab" + integrity sha512-BQuhQxPuRl79J5zSXRP+uNzPOyZw2oFI9JLRQ80XswSvg21KMKNtQza9eF42rfI/3Z40RvzBdXgziEkudzjo8A== + lru-cache@^7.3.1: version "7.3.1" resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-7.3.1.tgz#7702e80694ec2bf19865567a469f2b081fcf53f5" @@ -16760,7 +17450,12 @@ lunr@^2.3.9: resolved "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz#18b123142832337dd6e964df1a5a7707b25d35e1" integrity sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow== -luxon@^2.0.2, luxon@^2.3.0: +luxon@^1.23.x: + version "1.28.0" + resolved "https://registry.npmjs.org/luxon/-/luxon-1.28.0.tgz#e7f96daad3938c06a62de0fb027115d251251fbf" + integrity sha512-TfTiyvZhwBYM/7QdAVDh+7dBTBA29v4ik0Ce9zda3Mnf8on1S5KJI8P2jKFZ8+5C0jhmr0KwJEO/Wdpm0VeWJQ== + +luxon@^2.0.2, luxon@^2.3.0, luxon@^2.3.1: version "2.4.0" resolved "https://registry.npmjs.org/luxon/-/luxon-2.4.0.tgz#9435806545bb32d4234dab766ab8a3d54847a765" integrity sha512-w+NAwWOUL5hO0SgwOHsMBAmZ15SoknmQXhSO0hIbJCAmPKSsGeK8MlmhYh2w6Iib38IxN2M+/ooXWLbeis7GuA== @@ -16831,6 +17526,28 @@ make-fetch-happen@^10.0.1: socks-proxy-agent "^6.1.1" ssri "^8.0.1" +make-fetch-happen@^10.0.6: + version "10.1.7" + resolved "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.1.7.tgz#b1402cb3c9fad92b380ff3a863cdae5414a42f76" + integrity sha512-J/2xa2+7zlIUKqfyXDCXFpH3ypxO4k3rgkZHPSZkyUYcBT/hM80M3oyKLM/9dVriZFiGeGGS2Ei+0v2zfhqj3Q== + dependencies: + agentkeepalive "^4.2.1" + cacache "^16.1.0" + http-cache-semantics "^4.1.0" + http-proxy-agent "^5.0.0" + https-proxy-agent "^5.0.0" + is-lambda "^1.0.1" + lru-cache "^7.7.1" + minipass "^3.1.6" + minipass-collect "^1.0.2" + minipass-fetch "^2.0.3" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.4" + negotiator "^0.6.3" + promise-retry "^2.0.1" + socks-proxy-agent "^7.0.0" + ssri "^9.0.0" + make-fetch-happen@^8.0.9: version "8.0.14" resolved "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-8.0.14.tgz#aaba73ae0ab5586ad8eaa68bd83332669393e222" @@ -16889,7 +17606,7 @@ map-cache@^0.2.0, map-cache@^0.2.2: map-obj@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" - integrity sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0= + integrity sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg== map-obj@^4.0.0, map-obj@^4.1.0: version "4.3.0" @@ -16925,9 +17642,9 @@ markdown-table@^3.0.0: integrity sha512-CBbaYXKSGnE1uLRpKA1SWgIRb2PQrpkllNWpZtZe6VojOJ4ysqiq7/2glYcmKsOYN09QgH/HEBX5hIshAeiK6A== marked@^4.0.14: - version "4.0.16" - resolved "https://registry.npmjs.org/marked/-/marked-4.0.16.tgz#9ec18fc1a723032eb28666100344d9428cf7a264" - integrity sha512-wahonIQ5Jnyatt2fn8KqF/nIqZM8mh3oRu2+l5EANGMhu6RFjiSG52QNE2eWzFMI94HqYSgN184NurgNG6CztA== + version "4.0.17" + resolved "https://registry.npmjs.org/marked/-/marked-4.0.17.tgz#1186193d85bb7882159cdcfc57d1dfccaffb3fe9" + integrity sha512-Wfk0ATOK5iPxM4ptrORkFemqroz0ZDxp5MWfYA7H/F+wO17NRWV5Ypxi6p3g2Xmw2bKeiYOl6oVnLHKxBA0VhA== match-sorter@^6.0.2: version "6.3.1" @@ -17516,7 +18233,7 @@ micromatch@^3.1.10: snapdragon "^0.8.1" to-regex "^3.0.2" -micromatch@^4.0.2, micromatch@^4.0.4: +micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.5: version "4.0.5" resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== @@ -17566,7 +18283,7 @@ mime@1.6.0, mime@^1.3.4: resolved "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== -mime@^2.2.0, mime@^2.5.0: +mime@^2.2.0, mime@^2.5.0, mime@^2.5.2: version "2.6.0" resolved "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== @@ -17586,6 +18303,11 @@ mimic-fn@^2.1.0: resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== +mimic-fn@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz#60a90550d5cb0b239cca65d893b1a53b29871ecc" + integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw== + mimic-response@^1.0.0, mimic-response@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" @@ -17609,14 +18331,14 @@ min-document@^2.19.0: dom-walk "^0.1.0" min-indent@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/min-indent/-/min-indent-1.0.0.tgz#cfc45c37e9ec0d8f0a0ec3dd4ef7f7c3abe39256" - integrity sha1-z8RcN+nsDY8KDsPdTvf3w6vjklY= + version "1.0.1" + resolved "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" + integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== mini-css-extract-plugin@^2.4.2: - version "2.6.0" - resolved "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.6.0.tgz#578aebc7fc14d32c0ad304c2c34f08af44673f5e" - integrity sha512-ndG8nxCEnAemsg4FSgS+yNyHKgkTB4nPKqCOgh65j3/30qqC5RaSQQXMm++Y6sb6E1zRSxPkztj9fqxhS1Eo6w== + version "2.6.1" + resolved "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.6.1.tgz#9a1251d15f2035c342d99a468ab9da7a0451b71e" + integrity sha512-wd+SD57/K6DiV7jIR34P+s3uckTRuQvx0tKPcvjFlrEylk6P4mQ2KSWk1hblj1Kxaqok7LogKOieygXqBczNlg== dependencies: schema-utils "^4.0.0" @@ -17637,10 +18359,10 @@ minimatch@3.0.4: dependencies: brace-expansion "^1.1.7" -minimatch@5.0.1, minimatch@^5.0.0, minimatch@^5.0.1: - version "5.0.1" - resolved "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b" - integrity sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g== +minimatch@5.1.0, minimatch@^5.0.0, minimatch@^5.0.1, minimatch@^5.1.0: + version "5.1.0" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz#1717b464f4971b144f6aabe8f2d0b8e4511e09c7" + integrity sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg== dependencies: brace-expansion "^2.0.1" @@ -17697,6 +18419,17 @@ minipass-fetch@^1.3.0, minipass-fetch@^1.3.2, minipass-fetch@^1.4.1: optionalDependencies: encoding "^0.1.12" +minipass-fetch@^2.0.3: + version "2.1.0" + resolved "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-2.1.0.tgz#ca1754a5f857a3be99a9271277246ac0b44c3ff8" + integrity sha512-H9U4UVBGXEyyWJnqYDCLp1PwD8XIkJ4akNHp1aGVI+2Ym7wQMlxDKi4IB4JbmyU+pl9pEs/cVrK6cOuvmbK4Sg== + dependencies: + minipass "^3.1.6" + minipass-sized "^1.0.3" + minizlib "^2.1.2" + optionalDependencies: + encoding "^0.1.13" + minipass-flush@^1.0.5: version "1.0.5" resolved "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz#82e7135d7e89a50ffe64610a787953c4c4cbb373" @@ -17726,14 +18459,6 @@ minipass-sized@^1.0.3: dependencies: minipass "^3.0.0" -minipass@^2.6.0, minipass@^2.9.0: - version "2.9.0" - resolved "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" - integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg== - dependencies: - safe-buffer "^5.1.2" - yallist "^3.0.0" - minipass@^3.0.0, minipass@^3.1.0, minipass@^3.1.1, minipass@^3.1.3, minipass@^3.1.6: version "3.1.6" resolved "https://registry.npmjs.org/minipass/-/minipass-3.1.6.tgz#3b8150aa688a711a1521af5e8779c1d3bb4f45ee" @@ -17741,13 +18466,6 @@ minipass@^3.0.0, minipass@^3.1.0, minipass@^3.1.1, minipass@^3.1.3, minipass@^3. dependencies: yallist "^4.0.0" -minizlib@^1.3.3: - version "1.3.3" - resolved "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" - integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q== - dependencies: - minipass "^2.9.0" - minizlib@^2.0.0, minizlib@^2.1.1, minizlib@^2.1.2: version "2.1.2" resolved "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" @@ -17793,7 +18511,7 @@ mkdirp@0.3.0: resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz#1bbf5ab1ba827af23575143490426455f481fe1e" integrity sha1-G79asbqCevI1dRQ0kEJkVfSB/h4= -"mkdirp@>=0.5 0", mkdirp@^0.5.1, mkdirp@^0.5.5: +"mkdirp@>=0.5 0", mkdirp@^0.5.1: version "0.5.5" resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== @@ -17815,17 +18533,10 @@ modify-values@^1.0.0: resolved "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022" integrity sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw== -moment-timezone@^0.5.x: - version "0.5.34" - resolved "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.34.tgz#a75938f7476b88f155d3504a9343f7519d9a405c" - integrity sha512-3zAEHh2hKUs3EXLESx/wsgw6IQdusOT8Bxm3D9UrHPQR7zlMmzwybC8zHEM1tQ4LJwP7fcxrWr8tuBg05fFCbg== - dependencies: - moment ">= 2.9.0" - -"moment@>= 2.9.0", moment@>=2.14.0, moment@^2.27.0, moment@^2.29.1: - version "2.29.2" - resolved "https://registry.npmjs.org/moment/-/moment-2.29.2.tgz#00910c60b20843bcba52d37d58c628b47b1f20e4" - integrity sha512-UgzG4rvxYpN15jgCmVJwac49h9ly9NurikMWGPdVxm8GZD6XjkKPxDTjQQ43gtGgnV3X0cAyWDdP2Wexoquifg== +moment@^2.27.0, moment@^2.29.1: + version "2.29.3" + resolved "https://registry.npmjs.org/moment/-/moment-2.29.3.tgz#edd47411c322413999f7a5940d526de183c031f3" + integrity sha512-c6YRvhEo//6T2Jz/vVtYzqBzwvPT95JBQ+smCytzf7c50oMZRsR/a4w88aD34I+/QVSfnoAnSBFPJHItlOMJVw== morgan@^1.10.0: version "1.10.0" @@ -17863,58 +18574,6 @@ ms@2.1.3, ms@^2.0.0, ms@^2.1.1, ms@^2.1.3: resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== -msw@^0.35.0: - version "0.35.0" - resolved "https://registry.npmjs.org/msw/-/msw-0.35.0.tgz#18a4ceb6c822ef226a30421d434413bc45030d38" - integrity sha512-V7A6PqaS31F1k//fPS0OnO7vllfaqBUFsMEu3IpYixyWpiUInfyglodnbXhhtDyytkQikpkPZv8TZi/CvZzv/w== - dependencies: - "@mswjs/cookies" "^0.1.6" - "@mswjs/interceptors" "^0.12.6" - "@open-draft/until" "^1.0.3" - "@types/cookie" "^0.4.1" - "@types/inquirer" "^7.3.3" - "@types/js-levenshtein" "^1.1.0" - chalk "^4.1.1" - chokidar "^3.4.2" - cookie "^0.4.1" - graphql "^15.5.1" - headers-utils "^3.0.2" - inquirer "^8.1.1" - is-node-process "^1.0.1" - js-levenshtein "^1.1.6" - node-fetch "^2.6.1" - node-match-path "^0.6.3" - statuses "^2.0.0" - strict-event-emitter "^0.2.0" - type-fest "^1.2.2" - yargs "^17.0.1" - -msw@^0.36.3: - version "0.36.8" - resolved "https://registry.npmjs.org/msw/-/msw-0.36.8.tgz#33ff8bfb0299626a95f43d0e4c3dc2c73c17f1ba" - integrity sha512-K7lOQoYqhGhTSChsmHMQbf/SDCsxh/m0uhN6Ipt206lGoe81fpTmaGD0KLh4jUxCONMOUnwCSj0jtX2CM4pEdw== - dependencies: - "@mswjs/cookies" "^0.1.7" - "@mswjs/interceptors" "^0.12.7" - "@open-draft/until" "^1.0.3" - "@types/cookie" "^0.4.1" - "@types/inquirer" "^8.1.3" - "@types/js-levenshtein" "^1.1.0" - chalk "4.1.1" - chokidar "^3.4.2" - cookie "^0.4.1" - graphql "^15.5.1" - headers-utils "^3.0.2" - inquirer "^8.2.0" - is-node-process "^1.0.1" - js-levenshtein "^1.1.6" - node-fetch "^2.6.7" - path-to-regexp "^6.2.0" - statuses "^2.0.0" - strict-event-emitter "^0.2.0" - type-fest "^1.2.2" - yargs "^17.3.0" - msw@^0.39.2: version "0.39.2" resolved "https://registry.npmjs.org/msw/-/msw-0.39.2.tgz#832e9274db62c43cb79854d5a69dce031c700de8" @@ -17940,6 +18599,32 @@ msw@^0.39.2: type-fest "^1.2.2" yargs "^17.3.1" +msw@^0.42.0: + version "0.42.1" + resolved "https://registry.npmjs.org/msw/-/msw-0.42.1.tgz#2496d3e191754b68686e2530de459a2e102f85c4" + integrity sha512-LZZuz7VddL45gCBgfBWHyXj6a4W7OTJY0mZPoipJ3P/xwbuJwrtwB3IJrWlqBM8aink/eTKlRxwzmtIAwCj5yQ== + dependencies: + "@mswjs/cookies" "^0.2.0" + "@mswjs/interceptors" "^0.16.3" + "@open-draft/until" "^1.0.3" + "@types/cookie" "^0.4.1" + "@types/js-levenshtein" "^1.1.1" + chalk "4.1.1" + chokidar "^3.4.2" + cookie "^0.4.2" + graphql "^16.3.0" + headers-polyfill "^3.0.4" + inquirer "^8.2.0" + is-node-process "^1.0.1" + js-levenshtein "^1.1.6" + node-fetch "^2.6.7" + outvariant "^1.3.0" + path-to-regexp "^6.2.0" + statuses "^2.0.0" + strict-event-emitter "^0.2.0" + type-fest "^1.2.2" + yargs "^17.3.1" + multicast-dns@^7.2.4: version "7.2.4" resolved "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.4.tgz#cf0b115c31e922aeb20b64e6556cbeb34cf0dd19" @@ -18030,7 +18715,7 @@ nanoid@^3.1.23: resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz#6347a18cac88af88f58af0b3594b723d5e99bb35" integrity sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw== -nanoid@^3.3.3: +nanoid@^3.3.4: version "3.3.4" resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab" integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw== @@ -18062,6 +18747,17 @@ natural-compare@^1.4.0: resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= +ndjson@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/ndjson/-/ndjson-2.0.0.tgz#320ac86f6fe53f5681897349b86ac6f43bfa3a19" + integrity sha512-nGl7LRGrzugTtaFcJMhLbpzJM6XdivmbkdlaGcrk/LXg2KL/YBC6z1g70xh0/al+oFuVFP8N8kiWRucmeEH/qQ== + dependencies: + json-stringify-safe "^5.0.1" + minimist "^1.2.5" + readable-stream "^3.6.0" + split2 "^3.0.0" + through2 "^4.0.0" + negotiator@0.6.3, negotiator@^0.6.2, negotiator@^0.6.3: version "0.6.3" resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" @@ -18134,45 +18830,12 @@ node-fetch@2.6.7, node-fetch@^2.3.0, node-fetch@^2.6.0, node-fetch@^2.6.1, node- dependencies: whatwg-url "^5.0.0" -node-forge@^1, node-forge@^1.0.0: +node-forge@^1, node-forge@^1.0.0, node-forge@^1.3.1: version "1.3.1" resolved "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== -node-gyp@^5.0.2: - version "5.1.0" - resolved "https://registry.npmjs.org/node-gyp/-/node-gyp-5.1.0.tgz#8e31260a7af4a2e2f994b0673d4e0b3866156332" - integrity sha512-OUTryc5bt/P8zVgNUmC6xdXiDJxLMAW8cF5tLQOT9E5sOQj+UeQxnnPy74K3CLCa/SOjjBlbuzDLR8ANwA+wmw== - dependencies: - env-paths "^2.2.0" - glob "^7.1.4" - graceful-fs "^4.2.2" - mkdirp "^0.5.1" - nopt "^4.0.1" - npmlog "^4.1.2" - request "^2.88.0" - rimraf "^2.6.3" - semver "^5.7.1" - tar "^4.4.12" - which "^1.3.1" - -node-gyp@^7.1.0: - version "7.1.2" - resolved "https://registry.npmjs.org/node-gyp/-/node-gyp-7.1.2.tgz#21a810aebb187120251c3bcec979af1587b188ae" - integrity sha512-CbpcIo7C3eMu3dL1c3d0xw449fHIGALIJsRP4DDPHpyiW8vcriNY7ubh9TE4zEKfSxscY7PjeFnshE7h75ynjQ== - dependencies: - env-paths "^2.2.0" - glob "^7.1.4" - graceful-fs "^4.2.3" - nopt "^5.0.0" - npmlog "^4.1.2" - request "^2.88.2" - rimraf "^3.0.2" - semver "^7.3.2" - tar "^6.0.2" - which "^2.0.2" - -node-gyp@^8.2.0: +node-gyp@^8.2.0, node-gyp@^8.4.1: version "8.4.1" resolved "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz#3d49308fc31f768180957d6b5746845fbd429937" integrity sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w== @@ -18222,11 +18885,6 @@ node-libs-browser@^2.2.1: util "^0.11.0" vm-browserify "^1.0.1" -node-match-path@^0.6.3: - version "0.6.3" - resolved "https://registry.npmjs.org/node-match-path/-/node-match-path-0.6.3.tgz#55dd8443d547f066937a0752dce462ea7dc27551" - integrity sha512-fB1reOHKLRZCJMAka28hIxCwQLxGmd7WewOCBDYKpyA1KXi68A7vaGgdZAPhY2E6SXoYt3KqYCCvXLJ+O0Fu/Q== - node-modules-regexp@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz#8d9dbe28964a4ac5712e9131642107c71e90ec40" @@ -18260,14 +18918,6 @@ nopt@1.0.10, nopt@~1.0.10: dependencies: abbrev "1" -nopt@^4.0.1: - version "4.0.3" - resolved "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz#a375cad9d02fd921278d954c2254d5aa57e15e48" - integrity sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg== - dependencies: - abbrev "1" - osenv "^0.1.4" - nopt@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88" @@ -18275,7 +18925,7 @@ nopt@^5.0.0: dependencies: abbrev "1" -normalize-package-data@^2.0.0, normalize-package-data@^2.3.2, normalize-package-data@^2.5.0: +normalize-package-data@^2.3.2, normalize-package-data@^2.5.0: version "2.5.0" resolved "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== @@ -18295,6 +18945,16 @@ normalize-package-data@^3.0.0: semver "^7.3.2" validate-npm-package-license "^3.0.1" +normalize-package-data@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-4.0.0.tgz#1122d5359af21d4cd08718b92b058a658594177c" + integrity sha512-m+GL22VXJKkKbw62ZaBBjv8u6IE3UI4Mh5QakIqs3fWiKe0Xyi6L97hakwZK41/LD4R/2ly71Bayx0NLMwLA/g== + dependencies: + hosted-git-info "^5.0.0" + is-core-module "^2.8.1" + semver "^7.3.5" + validate-npm-package-license "^3.0.4" + normalize-path@^2.1.1: version "2.1.1" resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" @@ -18336,19 +18996,12 @@ npm-install-checks@^4.0.0: dependencies: semver "^7.1.1" -npm-lifecycle@^3.1.5: - version "3.1.5" - resolved "https://registry.npmjs.org/npm-lifecycle/-/npm-lifecycle-3.1.5.tgz#9882d3642b8c82c815782a12e6a1bfeed0026309" - integrity sha512-lDLVkjfZmvmfvpvBzA4vzee9cn+Me4orq0QF8glbswJVEbIcSNWib7qGOffolysc3teCqbbPZZkzbr3GQZTL1g== +npm-install-checks@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-5.0.0.tgz#5ff27d209a4e3542b8ac6b0c1db6063506248234" + integrity sha512-65lUsMI8ztHCxFz5ckCEC44DRvEGdZX5usQFriauxHEwt7upv1FKaQEmAtU0YnOAdwuNWCmk64xYiQABNrEyLA== dependencies: - byline "^5.0.0" - graceful-fs "^4.1.15" - node-gyp "^5.0.2" - resolve-from "^4.0.0" - slide "^1.1.6" - uid-number "0.0.6" - umask "^1.1.0" - which "^1.3.1" + semver "^7.1.1" npm-normalize-package-bin@^1.0.0, npm-normalize-package-bin@^1.0.1: version "1.0.1" @@ -18364,6 +19017,15 @@ npm-package-arg@^8.0.0, npm-package-arg@^8.0.1, npm-package-arg@^8.1.0, npm-pack semver "^7.3.4" validate-npm-package-name "^3.0.0" +npm-package-arg@^9.0.0, npm-package-arg@^9.0.1: + version "9.0.2" + resolved "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-9.0.2.tgz#f3ef7b1b3b02e82564af2d5228b4c36567dcd389" + integrity sha512-v/miORuX8cndiOheW8p2moNuPJ7QhcFh9WGlTorruG8hXSA23vMTEp5hTCmDxic0nD8KHhj/NQgFuySD3GYY3g== + dependencies: + hosted-git-info "^5.0.0" + semver "^7.3.5" + validate-npm-package-name "^4.0.0" + npm-packlist@^2.1.4: version "2.1.4" resolved "https://registry.npmjs.org/npm-packlist/-/npm-packlist-2.1.4.tgz#40e96b2b43787d0546a574542d01e066640d09da" @@ -18384,10 +19046,10 @@ npm-packlist@^3.0.0: npm-bundled "^1.1.1" npm-normalize-package-bin "^1.0.1" -npm-packlist@^5.0.0: - version "5.0.3" - resolved "https://registry.npmjs.org/npm-packlist/-/npm-packlist-5.0.3.tgz#0aec03bc2d6d488ca71396d7afb463563c3274f7" - integrity sha512-KuSbzgejxdsAWbNNyEs8EsyDHsO+nJF6k+9WuWzFbSNh5tFHs4lDApXw7kntKpuehfp8lKRzJkMtz0+WmGvTIw== +npm-packlist@^5.0.0, npm-packlist@^5.1.0: + version "5.1.0" + resolved "https://registry.npmjs.org/npm-packlist/-/npm-packlist-5.1.0.tgz#f3fd52903a021009913a133732022132eb355ce7" + integrity sha512-a04sqF6FbkyOAFA19AA0e94gS7Et5T2/IMj3VOT9nOF2RaRdVPQ1Q17Fb/HaDRFs+gbC7HOmhVZ29adpWgmDZg== dependencies: glob "^8.0.1" ignore-walk "^5.0.1" @@ -18404,6 +19066,16 @@ npm-pick-manifest@^6.0.0, npm-pick-manifest@^6.1.0, npm-pick-manifest@^6.1.1: npm-package-arg "^8.1.2" semver "^7.3.4" +npm-pick-manifest@^7.0.0: + version "7.0.1" + resolved "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-7.0.1.tgz#76dda30a7cd6b99be822217a935c2f5eacdaca4c" + integrity sha512-IA8+tuv8KujbsbLQvselW2XQgmXWS47t3CB0ZrzsRZ82DbDfkcFunOaPm4X7qNuhMfq+FmV7hQT4iFVpHqV7mg== + dependencies: + npm-install-checks "^5.0.0" + npm-normalize-package-bin "^1.0.1" + npm-package-arg "^9.0.0" + semver "^7.3.5" + npm-registry-fetch@^12.0.0, npm-registry-fetch@^12.0.1: version "12.0.2" resolved "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-12.0.2.tgz#ae583bb3c902a60dae43675b5e33b5b1f6159f1e" @@ -18416,6 +19088,19 @@ npm-registry-fetch@^12.0.0, npm-registry-fetch@^12.0.1: minizlib "^2.1.2" npm-package-arg "^8.1.5" +npm-registry-fetch@^13.0.0, npm-registry-fetch@^13.0.1: + version "13.1.1" + resolved "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-13.1.1.tgz#26dc4b26d0a545886e807748032ba2aefaaae96b" + integrity sha512-5p8rwe6wQPLJ8dMqeTnA57Dp9Ox6GH9H60xkyJup07FmVlu3Mk7pf/kIIpl9gaN5bM8NM+UUx3emUWvDNTt39w== + dependencies: + make-fetch-happen "^10.0.6" + minipass "^3.1.6" + minipass-fetch "^2.0.3" + minipass-json-stream "^1.0.1" + minizlib "^2.1.2" + npm-package-arg "^9.0.1" + proc-log "^2.0.0" + npm-registry-fetch@^9.0.0: version "9.0.0" resolved "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-9.0.0.tgz#86f3feb4ce00313bc0b8f1f8f69daae6face1661" @@ -18444,6 +19129,13 @@ npm-run-path@^4.0.0, npm-run-path@^4.0.1: dependencies: path-key "^3.0.0" +npm-run-path@^5.1.0: + version "5.1.0" + resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz#bc62f7f3f6952d9894bd08944ba011a6ee7b7e00" + integrity sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q== + dependencies: + path-key "^4.0.0" + npmlog@^4.0.1, npmlog@^4.1.2: version "4.1.2" resolved "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" @@ -18474,6 +19166,16 @@ npmlog@^6.0.0: gauge "^4.0.0" set-blocking "^2.0.0" +npmlog@^6.0.2: + version "6.0.2" + resolved "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz#c8166017a42f2dea92d6453168dd865186a70830" + integrity sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg== + dependencies: + are-we-there-yet "^3.0.0" + console-control-strings "^1.1.0" + gauge "^4.0.3" + set-blocking "^2.0.0" + nth-check@^2.0.0: version "2.0.1" resolved "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz#2efe162f5c3da06a28959fbd3db75dbeea9f0fc2" @@ -18529,22 +19231,22 @@ object-copy@^0.1.0: define-property "^0.2.5" kind-of "^3.0.3" -object-hash@^2.0.1, object-hash@^2.1.1: - version "2.2.0" - resolved "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz#5ad518581eefc443bd763472b8ff2e9c2c0d54a5" - integrity sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw== - -object-hash@^3.0.0: +object-hash@3.0.0, object-hash@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz#73f97f753e7baffc0e2cc9d6e079079744ac82e9" integrity sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw== -object-inspect@^1.11.0, object-inspect@^1.12.0, object-inspect@^1.9.0: - version "1.12.0" - resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz#6e2c120e868fd1fd18cb4f18c31741d0d6e776f0" - integrity sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g== +object-hash@^2.0.1: + version "2.2.0" + resolved "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz#5ad518581eefc443bd763472b8ff2e9c2c0d54a5" + integrity sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw== -object-keys@^1.0.12, object-keys@^1.1.1: +object-inspect@^1.11.0, object-inspect@^1.12.0, object-inspect@^1.12.2, object-inspect@^1.9.0: + version "1.12.2" + resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea" + integrity sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ== + +object-keys@^1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== @@ -18584,21 +19286,13 @@ object.fromentries@^2.0.5: define-properties "^1.1.3" es-abstract "^1.19.1" -object.getownpropertydescriptors@^2.0.3: - version "2.1.0" - resolved "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz#369bf1f9592d8ab89d712dced5cb81c7c5352649" - integrity sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg== +object.hasown@^1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.1.tgz#ad1eecc60d03f49460600430d97f23882cf592a3" + integrity sha512-LYLe4tivNQzq4JdaWW6WO3HMZZJWzkkH8fnI6EebWl0VZth2wL2Lovm74ep2/gZzlaTdV62JZHEqHQ2yVn8Q/A== dependencies: - define-properties "^1.1.3" - es-abstract "^1.17.0-next.1" - -object.hasown@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.0.tgz#7232ed266f34d197d15cac5880232f7a4790afe5" - integrity sha512-MhjYRfj3GBlhSkDHo6QmvgjRLXQ2zndabdf3nX0yTyZK9rPfxb6uRpAac8HXNLy1GpqWtZ81Qh4v3uOls2sRAg== - dependencies: - define-properties "^1.1.3" - es-abstract "^1.19.1" + define-properties "^1.1.4" + es-abstract "^1.19.5" object.pick@^1.3.0: version "1.3.0" @@ -18634,9 +19328,9 @@ octokit-plugin-create-pull-request@^3.10.0: "@octokit/types" "^6.8.2" octokit@^1.7.1: - version "1.7.1" - resolved "https://registry.npmjs.org/octokit/-/octokit-1.7.1.tgz#d86e51c8e0cec65cb64822ca2c9ff1b052593799" - integrity sha512-1b7eRgU8uWetHOWr8f9ptnVo2EKbrkOfocMeQdpgCt7tl/LK67HptFsy2Xg4fMjsJ/+onoBJW0hy/fO0In3/uA== + version "1.7.2" + resolved "https://registry.npmjs.org/octokit/-/octokit-1.7.2.tgz#c5f11699c39ba5526ff170cbfcb7eaa291ddf11a" + integrity sha512-C+iwOeUMWwbvHxGbLX5rAde5WuEVGe/hNQniU1haZAPMHqUz1+ppffvkP4v/2R3dkSLJnzceUG+ir0klNmEoBA== dependencies: "@octokit/app" "^12.0.4" "@octokit/core" "^3.5.1" @@ -18704,6 +19398,13 @@ onetime@^5.1.0, onetime@^5.1.2: dependencies: mimic-fn "^2.1.0" +onetime@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz#7c24c18ed1fd2e9bca4bd26806a33613c77d34b4" + integrity sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ== + dependencies: + mimic-fn "^4.0.0" + open@^8.0.0, open@^8.0.9, open@^8.4.0: version "8.4.0" resolved "https://registry.npmjs.org/open/-/open-8.4.0.tgz#345321ae18f8138f82565a910fdc6b39e8c244f8" @@ -18788,11 +19489,6 @@ os-browserify@^0.3.0: resolved "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc= -os-homedir@^1.0.0: - version "1.0.2" - resolved "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" - integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= - os-locale@^1.4.0: version "1.4.0" resolved "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9" @@ -18808,18 +19504,10 @@ os-name@^3.1.0: macos-release "^2.2.0" windows-release "^3.1.0" -os-tmpdir@^1.0.0, os-tmpdir@~1.0.2: +os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" - integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= - -osenv@^0.1.4: - version "0.1.5" - resolved "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" - integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g== - dependencies: - os-homedir "^1.0.0" - os-tmpdir "^1.0.0" + integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== ospath@^1.2.2: version "1.2.2" @@ -18831,12 +19519,7 @@ outdent@^0.5.0: resolved "https://registry.npmjs.org/outdent/-/outdent-0.5.0.tgz#9e10982fdc41492bb473ad13840d22f9655be2ff" integrity sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q== -outvariant@^1.2.0: - version "1.2.1" - resolved "https://registry.npmjs.org/outvariant/-/outvariant-1.2.1.tgz#e630f6cdc1dbf398ed857e36f219de4a005ccd35" - integrity sha512-bcILvFkvpMXh66+Ubax/inxbKRyWTUiiFIW2DWkiS79wakrLGn3Ydy+GvukadiyfZjaL6C7YhIem4EZSM282wA== - -outvariant@^1.2.1: +outvariant@^1.2.1, outvariant@^1.3.0: version "1.3.0" resolved "https://registry.npmjs.org/outvariant/-/outvariant-1.3.0.tgz#c39723b1d2cba729c930b74bf962317a81b9b1c9" integrity sha512-yeWM9k6UPfG/nzxdaPlJkB2p08hCg4xP6Lx99F+vP8YF7xyZVfTmJjrrNalkmzudD4WFvNLVudQikqUmF8zhVQ== @@ -18882,13 +19565,20 @@ p-limit@^1.1.0: dependencies: p-try "^1.0.0" -p-limit@^2.0.0, p-limit@^2.2.0: +p-limit@^2.0.0: version "2.2.2" resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz#61279b67721f5287aa1c13a9a7fbbc48c9291b1e" integrity sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ== dependencies: p-try "^2.0.0" +p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + p-limit@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz#914af6544ed32bfa54670b061cafcbd04984b644" @@ -19014,31 +19704,6 @@ packet-reader@1.0.0: resolved "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz#9238e5480dedabacfe1fe3f2771063f164157d74" integrity sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ== -pacote@^11.2.6: - version "11.2.6" - resolved "https://registry.npmjs.org/pacote/-/pacote-11.2.6.tgz#c0426e5d5c8d33aeea3461a75e1390f1ba78f953" - integrity sha512-xCl++Hb3aBC7LaWMimbO4xUqZVsEbKDVc6KKDIIyAeBYrmMwY1yJC2nES/lsGd8sdQLUosgBxQyuVNncZ2Ru0w== - dependencies: - "@npmcli/git" "^2.0.1" - "@npmcli/installed-package-contents" "^1.0.6" - "@npmcli/promise-spawn" "^1.2.0" - "@npmcli/run-script" "^1.8.2" - cacache "^15.0.5" - chownr "^2.0.0" - fs-minipass "^2.1.0" - infer-owner "^1.0.4" - minipass "^3.1.3" - mkdirp "^1.0.3" - npm-package-arg "^8.0.1" - npm-packlist "^2.1.4" - npm-pick-manifest "^6.0.0" - npm-registry-fetch "^9.0.0" - promise-retry "^2.0.1" - read-package-json-fast "^2.0.1" - rimraf "^3.0.2" - ssri "^8.0.1" - tar "^6.1.0" - pacote@^12.0.0, pacote@^12.0.2: version "12.0.3" resolved "https://registry.npmjs.org/pacote/-/pacote-12.0.3.tgz#b6f25868deb810e7e0ddf001be88da2bcaca57c7" @@ -19064,6 +19729,33 @@ pacote@^12.0.0, pacote@^12.0.2: ssri "^8.0.1" tar "^6.1.0" +pacote@^13.0.3, pacote@^13.0.5, pacote@^13.4.1: + version "13.6.0" + resolved "https://registry.npmjs.org/pacote/-/pacote-13.6.0.tgz#79ea3d3ae5a2b29e2994dcf18d75494e8d888032" + integrity sha512-zHmuCwG4+QKnj47LFlW3LmArwKoglx2k5xtADiMCivVWPgNRP5QyLDGOIjGjwOe61lhl1rO63m/VxT16pEHLWg== + dependencies: + "@npmcli/git" "^3.0.0" + "@npmcli/installed-package-contents" "^1.0.7" + "@npmcli/promise-spawn" "^3.0.0" + "@npmcli/run-script" "^3.0.1" + cacache "^16.0.0" + chownr "^2.0.0" + fs-minipass "^2.1.0" + infer-owner "^1.0.4" + minipass "^3.1.6" + mkdirp "^1.0.4" + npm-package-arg "^9.0.0" + npm-packlist "^5.1.0" + npm-pick-manifest "^7.0.0" + npm-registry-fetch "^13.0.1" + proc-log "^2.0.0" + promise-retry "^2.0.1" + read-package-json "^5.0.0" + read-package-json-fast "^2.0.3" + rimraf "^3.0.2" + ssri "^9.0.0" + tar "^6.1.11" + pako@^1.0.10, pako@^1.0.5, pako@~1.0.5: version "1.0.11" resolved "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" @@ -19181,17 +19873,7 @@ parse-json@^4.0.0: error-ex "^1.3.1" json-parse-better-errors "^1.0.1" -parse-json@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/parse-json/-/parse-json-5.0.0.tgz#73e5114c986d143efa3712d4ea24db9a4266f60f" - integrity sha512-OOY5b7PAEFV0E2Fir1KOkxchnZNCdowAJgQ5NuxjpBKTRP3pQhwkrkxqQjeoKJ+fO7bCpmIZaogI4eZGDMEGOw== - dependencies: - "@babel/code-frame" "^7.0.0" - error-ex "^1.3.1" - json-parse-better-errors "^1.0.1" - lines-and-columns "^1.1.6" - -parse-json@^5.2.0: +parse-json@^5.0.0, parse-json@^5.2.0: version "5.2.0" resolved "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== @@ -19358,19 +20040,25 @@ passport-strategy@1.x.x, passport-strategy@^1.0.0: resolved "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz#b5539aa8fc225a3d1ad179476ddf236b440f52e4" integrity sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ= -passport@^0.5.2: - version "0.5.3" - resolved "https://registry.npmjs.org/passport/-/passport-0.5.3.tgz#e69b46c9bb3290660bc2b3299330d78710b198cc" - integrity sha512-gGc+70h4gGdBWNsR3FuV3byLDY6KBTJAIExGFXTpQaYfbbcHCBlRRKx7RBQSpqEqc5Hh2qVzRs7ssvSfOpkUEA== +passport@^0.6.0: + version "0.6.0" + resolved "https://registry.npmjs.org/passport/-/passport-0.6.0.tgz#e869579fab465b5c0b291e841e6cc95c005fac9d" + integrity sha512-0fe+p3ZnrWRW74fe8+SvCyf4a3Pb2/h7gFkQ8yTJpAO50gDzlfjZUZTO1k5Eg9kUct22OxHLqDZoKUWRHOh9ug== dependencies: passport-strategy "1.x.x" pause "0.0.1" + utils-merge "^1.0.1" path-browserify@0.0.1: version "0.0.1" resolved "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a" integrity sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ== +path-browserify@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd" + integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g== + path-case@^3.0.4: version "3.0.4" resolved "https://registry.npmjs.org/path-case/-/path-case-3.0.4.tgz#9168645334eb942658375c56f80b4c0cb5f82c6f" @@ -19379,6 +20067,11 @@ path-case@^3.0.4: dot-case "^3.0.4" tslib "^2.0.3" +path-equal@1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/path-equal/-/path-equal-1.1.2.tgz#260e7c449c4c2022f68cc5fa6e617e892858250d" + integrity sha512-p5kxPPwCdbf5AdXzT1bUBJomhgBlEjRBavYNr1XUpMFIE4Hnf2roueCMXudZK5tnaAu1tTmp3GPzqwJK45IHEA== + path-exists@^2.0.0: version "2.1.0" resolved "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" @@ -19416,6 +20109,11 @@ path-key@^3.0.0, path-key@^3.1.0: resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== +path-key@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz#295588dc3aee64154f877adb9d780b81c554bf18" + integrity sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ== + path-parse@^1.0.6, path-parse@^1.0.7: version "1.0.7" resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" @@ -19443,6 +20141,11 @@ path-to-regexp@2.2.1: resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-2.2.1.tgz#90b617025a16381a879bc82a38d4e8bdeb2bcf45" integrity sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ== +path-to-regexp@3.2.0: + version "3.2.0" + resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-3.2.0.tgz#fa7877ecbc495c601907562222453c43cc204a5f" + integrity sha512-jczvQbCUS7XmS7o+y1aEO9OBVFeZBQ1MDSEqmO7xSoPgOPoowY/SxLpZ6Vh97/8qHZOteiCKb7gkG9gA2ZUxJA== + path-to-regexp@^1.7.0: version "1.8.0" resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a" @@ -19585,15 +20288,15 @@ picocolors@^1.0.0: resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== -picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.2.3, picomatch@^2.3.1: +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.2.3, picomatch@^2.3.0, picomatch@^2.3.1: version "2.3.1" resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== -pidtree@^0.5.0: - version "0.5.0" - resolved "https://registry.npmjs.org/pidtree/-/pidtree-0.5.0.tgz#ad5fbc1de78b8a5f99d6fbdd4f6e4eee21d1aca1" - integrity sha512-9nxspIM7OpZuhBxPg73Zvyq7j1QMPMPsGKTqRc2XOaFQauDvoNz9fM1Wdkjmeo7l9GXOZiRs97sPkuayl39wjA== +pidtree@^0.6.0: + version "0.6.0" + resolved "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz#90ad7b6d42d5841e69e0a2419ef38f8883aa057c" + integrity sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g== pify@^2.0.0, pify@^2.2.0, pify@^2.3.0: version "2.3.0" @@ -20012,11 +20715,11 @@ postcss-value-parser@^4.0.2, postcss-value-parser@^4.1.0, postcss-value-parser@^ integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== postcss@^8.1.0, postcss@^8.4.7: - version "8.4.13" - resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.13.tgz#7c87bc268e79f7f86524235821dfdf9f73e5d575" - integrity sha512-jtL6eTBrza5MPzy8oJLFuUscHDXTV5KcLlqAWHl5q5WYRfnNRGSmOZmOZ1T6Gy7A99mOZfqungmZMpMmCVJ8ZA== + version "8.4.14" + resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz#ee9274d5622b4858c1007a74d76e42e56fd21caf" + integrity sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig== dependencies: - nanoid "^3.3.3" + nanoid "^3.3.4" picocolors "^1.0.0" source-map-js "^1.0.2" @@ -20097,9 +20800,9 @@ prettier@^1.16.4, prettier@^1.19.1: integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew== prettier@^2.2.1: - version "2.6.2" - resolved "https://registry.npmjs.org/prettier/-/prettier-2.6.2.tgz#e26d71a18a74c3d0f0597f55f01fb6c06c206032" - integrity sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew== + version "2.7.1" + resolved "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz#e235806850d057f97bb08368a4f7d899f7760c64" + integrity sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g== pretty-bytes@^5.3.0, pretty-bytes@^5.6.0: version "5.6.0" @@ -20163,6 +20866,11 @@ proc-log@^1.0.0: resolved "https://registry.npmjs.org/proc-log/-/proc-log-1.0.0.tgz#0d927307401f69ed79341e83a0b2c9a13395eb77" integrity sha512-aCk8AO51s+4JyuYGg3Q/a6gnrlDO09NpVWePtjp7xwphcoQ04x5WAfCyugcsbLooWcMJ87CLkD4+604IckEdhg== +proc-log@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/proc-log/-/proc-log-2.0.1.tgz#8f3f69a1f608de27878f91f5c688b225391cb685" + integrity sha512-Kcmo2FhfDTXdcbfDH76N7uBYHINxc/8GW7UAVuVP9I+Va3uHSerrnKV6dLooga/gh7GlgzuCCr/eoldnL1muGw== + process-nextick-args@^2.0.0, process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" @@ -20273,17 +20981,24 @@ proto-list@~1.2.1: resolved "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" integrity sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk= -proto3-json-serializer@^0.1.5: - version "0.1.6" - resolved "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-0.1.6.tgz#67cf3b8d5f4c8bebfc410698ad3b1ed64da39c7b" - integrity sha512-tGbV6m6Kad8NqxMh5hw87euPS0YoZSAOIfvR01zYkQV8Gpx1V/8yU/0gCKCvfCkhAJsjvzzhnnsdQxA1w7PSog== +proto3-json-serializer@^0.1.8: + version "0.1.9" + resolved "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-0.1.9.tgz#705ddb41b009dd3e6fcd8123edd72926abf65a34" + integrity sha512-A60IisqvnuI45qNRygJjrnNjX2TMdQGMY+57tR3nul3ZgO2zXkR9OGR8AXxJhkqx84g0FTnrfi3D5fWMSdANdQ== dependencies: protobufjs "^6.11.2" -protobufjs@6.11.2, protobufjs@^6.10.0, protobufjs@^6.11.2, protobufjs@^6.8.6: - version "6.11.2" - resolved "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.2.tgz#de39fabd4ed32beaa08e9bb1e30d08544c1edf8b" - integrity sha512-4BQJoPooKJl2G9j3XftkIXjoC9C0Av2NOrWmbLWT1vH32GcSUHjM0Arra6UfTsVyfMAuFzaLucXn1sadxJydAw== +proto3-json-serializer@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-1.0.0.tgz#4c87b06f5788cb3c9d94be7e0c6ef2420c020146" + integrity sha512-uEylKn4a7I6ZtLZ0fwCJCdQqr2vMsGtxxwKZIoqy4VwYeK9HKpCiG8WMBdtodV+1UO5YHHvHvb39b5CyRWT+9g== + dependencies: + protobufjs "^6.11.2" + +protobufjs@6.11.3, protobufjs@^6.10.0, protobufjs@^6.11.2, protobufjs@^6.8.6: + version "6.11.3" + resolved "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.3.tgz#637a527205a35caa4f3e2a9a4a13ddffe0e7af74" + integrity sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg== dependencies: "@protobufjs/aspromise" "^1.1.2" "@protobufjs/base64" "^1.1.2" @@ -20332,7 +21047,7 @@ ps-tree@1.2.0: pseudomap@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" - integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= + integrity sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ== psl@^1.1.28, psl@^1.1.33: version "1.8.0" @@ -20356,11 +21071,6 @@ public-encrypt@^4.0.0: randombytes "^2.0.1" safe-buffer "^5.1.2" -puka@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/puka/-/puka-1.0.1.tgz#a2df782b7eb4cf9564e4c93a5da422de0dfacc02" - integrity sha512-ssjRZxBd7BT3dte1RR3VoeT2cT/ODH8x+h0rUF1rMqB0srHYf48stSDWfiYakTp5UBZMxroZhB2+ExLDHm7W3g== - pump@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" @@ -20401,13 +21111,13 @@ pupa@^2.1.1: escape-goat "^2.0.0" puppeteer@^14.0.0: - version "14.1.0" - resolved "https://registry.npmjs.org/puppeteer/-/puppeteer-14.1.0.tgz#319560e20ff451890158d7146c79ab589c6e7031" - integrity sha512-T3eB4f6k9HVttYvyy8drGIKb04M+vxhepqM7qqcVCBTNT3T6M9cUaJT4k7P+a6wSonObJSJUP98JkPDQG+3fJw== + version "14.4.1" + resolved "https://registry.npmjs.org/puppeteer/-/puppeteer-14.4.1.tgz#6c7437a65f7ba98ef8ad7c2b0f1cf808e91617bb" + integrity sha512-+H0Gm84aXUvSLdSiDROtLlOofftClgw2TdceMvvCU9UvMryappoeS3+eOLfKvoy4sm8B8MWnYmPhWxVFudAOFQ== dependencies: cross-fetch "3.1.5" debug "4.3.4" - devtools-protocol "0.0.982423" + devtools-protocol "0.0.1001819" extract-zip "2.0.1" https-proxy-agent "5.0.1" pkg-dir "4.2.0" @@ -20416,7 +21126,7 @@ puppeteer@^14.0.0: rimraf "3.0.2" tar-fs "2.1.1" unbzip2-stream "1.4.3" - ws "8.6.0" + ws "8.7.0" q@^1.5.1: version "1.5.1" @@ -20560,10 +21270,10 @@ raw-body@2.5.1, raw-body@^2.4.1: iconv-lite "0.4.24" unpipe "1.0.0" -rc-progress@3.3.2: - version "3.3.2" - resolved "https://registry.npmjs.org/rc-progress/-/rc-progress-3.3.2.tgz#57362d49bb6b7b09fec84bf18220619294bde675" - integrity sha512-m+1LtVlXIomz8lcghfF/kViT5h/NtdWp87hnFL+FlnrxyZCEuB4N5BKNN6ctZa3a1Mae+4SMO8AG5lScjU06Ig== +rc-progress@3.3.3: + version "3.3.3" + resolved "https://registry.npmjs.org/rc-progress/-/rc-progress-3.3.3.tgz#eb9bffbacab1534f2542f9f6861ce772254362b1" + integrity sha512-MDVNVHzGanYtRy2KKraEaWeZLri2ZHWIRyaE1a9MQ2MuJ09m+Wxj5cfcaoaR6z5iRpHpA59YeUxAlpML8N4PJw== dependencies: "@babel/runtime" "^7.10.1" classnames "^2.2.6" @@ -20609,6 +21319,14 @@ react-copy-to-clipboard@5.0.4: copy-to-clipboard "^3" prop-types "^15.5.8" +react-copy-to-clipboard@^5.0.4: + version "5.1.0" + resolved "https://registry.npmjs.org/react-copy-to-clipboard/-/react-copy-to-clipboard-5.1.0.tgz#09aae5ec4c62750ccb2e6421a58725eabc41255c" + integrity sha512-k61RsNgAayIJNoy9yDsYzDe/yAZAzEbEgcz3DZMhF686LEyukcE1hzurxe85JandPUG+yTfGVFzuEw3xt8WP/A== + dependencies: + copy-to-clipboard "^3.3.1" + prop-types "^15.8.1" + react-debounce-input@=3.2.4: version "3.2.4" resolved "https://registry.npmjs.org/react-debounce-input/-/react-debounce-input-3.2.4.tgz#8204373a6498776536a2fcc7e467d054c3b729d4" @@ -20647,6 +21365,16 @@ react-dev-utils@^12.0.0-next.60: strip-ansi "^6.0.1" text-table "^0.2.0" +react-dom@^16.13.1: + version "16.14.0" + resolved "https://registry.npmjs.org/react-dom/-/react-dom-16.14.0.tgz#7ad838ec29a777fb3c75c3a190f661cf92ab8b89" + integrity sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + prop-types "^15.6.2" + scheduler "^0.19.1" + react-dom@^17.0.2: version "17.0.2" resolved "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23" @@ -20679,9 +21407,9 @@ react-fast-compare@^3.1.1: integrity sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA== react-ga@^3.3.0: - version "3.3.0" - resolved "https://registry.npmjs.org/react-ga/-/react-ga-3.3.0.tgz#c91f407198adcb3b49e2bc5c12b3fe460039b3ca" - integrity sha512-o8RScHj6Lb8cwy3GMrVH6NJvL+y0zpJvKtc0+wmH7Bt23rszJmnqEQxRbyrqUzk9DTJIHoP42bfO5rswC9SWBQ== + version "3.3.1" + resolved "https://registry.npmjs.org/react-ga/-/react-ga-3.3.1.tgz#d8e1f4e05ec55ed6ff944dcb14b99011dfaf9504" + integrity sha512-4Vc0W5EvXAXUN/wWyxvsAKDLLgtJ3oLmhYYssx+YzphJpejtOst6cbIHCIyF50Fdxuf5DDKqRYny24yJ2y7GFQ== react-helmet@6.1.0: version "6.1.0" @@ -20694,9 +21422,9 @@ react-helmet@6.1.0: react-side-effect "^2.1.0" react-hook-form@^7.12.2, react-hook-form@^7.13.0: - version "7.31.1" - resolved "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.31.1.tgz#16c357dd366bc226172e6acbb5a1672873bbfb28" - integrity sha512-QjtjZ8r8KtEBWWpcXLyQordCraTFxILtyQpaz5KLLxN2YzcC+FZ9LLtOnNGuOnzZo9gCoB+viK3ZHV9Mb2htmQ== + version "7.31.3" + resolved "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.31.3.tgz#b61bafb9a7435f91695351a7a9f714d8c4df0121" + integrity sha512-NVZdCWViIWXXXlQ3jxVQH0NuNfwPf8A/0KvuCxrM9qxtP1qYosfR2ZudarziFrVOC7eTUbWbm1T4OyYCwv9oSQ== react-hot-loader@^4.13.0: version "4.13.0" @@ -20775,9 +21503,9 @@ react-markdown@^8.0.0: vfile "^5.0.0" react-query@^3.34.16: - version "3.39.0" - resolved "https://registry.npmjs.org/react-query/-/react-query-3.39.0.tgz#0caca7b0da98e65008bbcd4df0d25618c2100050" - integrity sha512-Od0IkSuS79WJOhzWBx/ys0x13+7wFqgnn64vBqqAAnZ9whocVhl/y1padD5uuZ6EIkXbFbInax0qvY7zGM0thA== + version "3.39.1" + resolved "https://registry.npmjs.org/react-query/-/react-query-3.39.1.tgz#3876c0fdac7a3b5a84e195534e5fa8fbdd628847" + integrity sha512-qYKT1bavdDiQZbngWZyPotlBVzcBjDYEJg5RQLBa++5Ix5jjfbEYJmHSZRZD+USVHUSvl/ey9Hu+QfF1QAK80A== dependencies: "@babel/runtime" "^7.5.5" broadcast-channel "^3.4.1" @@ -20888,10 +21616,10 @@ react-test-renderer@^16.13.1: react-is "^16.8.6" scheduler "^0.19.1" -react-text-truncate@^0.18.0: - version "0.18.0" - resolved "https://registry.npmjs.org/react-text-truncate/-/react-text-truncate-0.18.0.tgz#c65f4be660d24734badb903a4832467eddcf8058" - integrity sha512-0cc07CRPRPm3cTloVbPicVTSosJNauwVcmJb5FPa71u4KtDVgrRPJoxVvLBubl3gLyx1pVUozgREYMHpHM16jg== +react-text-truncate@^0.19.0: + version "0.19.0" + resolved "https://registry.npmjs.org/react-text-truncate/-/react-text-truncate-0.19.0.tgz#60bc5ecf29a03ebc256f31f90a2d8402176aac91" + integrity sha512-QxHpZABfGG0Z3WEYbRTZ+rXdZn50Zvp+sWZXgVAd7FCKAMzv/kcwctTpNmWgXDTpAoHhMjOVwmgRtX3x5yeF4w== dependencies: prop-types "^15.5.7" @@ -20921,9 +21649,9 @@ react-universal-interface@^0.6.2: integrity sha512-dg8yXdcQmvgR13RIlZbTRQOoUrDciFVoSBZILwjE2LFISxZZ8loVJKAkuzswl5js8BHda79bIb2b84ehU8IjXw== react-use@^17.2.4, react-use@^17.3.1, react-use@^17.3.2: - version "17.3.2" - resolved "https://registry.npmjs.org/react-use/-/react-use-17.3.2.tgz#448abf515f47c41c32455024db28167cb6e53be8" - integrity sha512-bj7OD0/1wL03KyWmzFXAFe425zziuTf7q8olwCYBfOeFHY1qfO1FAMjROQLsLZYwG4Rx63xAfb7XAbBrJsZmEw== + version "17.4.0" + resolved "https://registry.npmjs.org/react-use/-/react-use-17.4.0.tgz#cefef258b0a6c534a5c8021c2528ac6e1a4cdc6d" + integrity sha512-TgbNTCA33Wl7xzIJegn1HndB4qTS9u03QUwyNycUnXaweZkE4Kq2SB+Yoxx8qbshkZGYBDvUXbXWRUmQDcZZ/Q== dependencies: "@types/js-cookie" "^2.2.6" "@xobotyi/scrollbar-width" "^1.9.5" @@ -20966,7 +21694,7 @@ read-cmd-shim@^2.0.0: resolved "https://registry.npmjs.org/read-cmd-shim/-/read-cmd-shim-2.0.0.tgz#4a50a71d6f0965364938e9038476f7eede3928d9" integrity sha512-HJpV9bQpkl6KwjxlJcBoqu9Ba0PQg8TqSNIOrulGt54a0uup0HtevreFHzYzkm0lpnleRdNBzXznKrgxglEHQw== -read-package-json-fast@^2.0.1, read-package-json-fast@^2.0.2: +read-package-json-fast@^2.0.1, read-package-json-fast@^2.0.2, read-package-json-fast@^2.0.3: version "2.0.3" resolved "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-2.0.3.tgz#323ca529630da82cb34b36cc0b996693c98c2b83" integrity sha512-W/BKtbL+dUjTuRL2vziuYhp76s5HZ9qQhd/dKfWIZveD0O40453QNyZhC0e63lqZrAQ4jiOapVoeJ7JrszenQQ== @@ -20974,18 +21702,6 @@ read-package-json-fast@^2.0.1, read-package-json-fast@^2.0.2: json-parse-even-better-errors "^2.3.0" npm-normalize-package-bin "^1.0.1" -read-package-json@^2.0.0: - version "2.1.1" - resolved "https://registry.npmjs.org/read-package-json/-/read-package-json-2.1.1.tgz#16aa66c59e7d4dad6288f179dd9295fd59bb98f1" - integrity sha512-dAiqGtVc/q5doFz6096CcnXhpYk0ZN8dEKVkGLU0CsASt8SrgF6SF7OTKAYubfvFhWaqofl+Y8HK19GR8jwW+A== - dependencies: - glob "^7.1.1" - json-parse-better-errors "^1.0.1" - normalize-package-data "^2.0.0" - npm-normalize-package-bin "^1.0.0" - optionalDependencies: - graceful-fs "^4.1.2" - read-package-json@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/read-package-json/-/read-package-json-3.0.0.tgz#2219328e77c9be34f035a4ce58d1fb8e2979adf9" @@ -20996,14 +21712,15 @@ read-package-json@^3.0.0: normalize-package-data "^3.0.0" npm-normalize-package-bin "^1.0.0" -read-package-tree@^5.3.1: - version "5.3.1" - resolved "https://registry.npmjs.org/read-package-tree/-/read-package-tree-5.3.1.tgz#a32cb64c7f31eb8a6f31ef06f9cedf74068fe636" - integrity sha512-mLUDsD5JVtlZxjSlPPx1RETkNjjvQYuweKwNVt1Sn8kP5Jh44pvYuUHCp6xSVDZWbNxVxG5lyZJ921aJH61sTw== +read-package-json@^5.0.0: + version "5.0.1" + resolved "https://registry.npmjs.org/read-package-json/-/read-package-json-5.0.1.tgz#1ed685d95ce258954596b13e2e0e76c7d0ab4c26" + integrity sha512-MALHuNgYWdGW3gKzuNMuYtcSSZbGQm94fAp16xt8VsYTLBjUSc55bLMKe6gzpWue0Tfi6CBgwCSdDAqutGDhMg== dependencies: - read-package-json "^2.0.0" - readdir-scoped-modules "^1.0.0" - util-promisify "^2.1.0" + glob "^8.0.1" + json-parse-even-better-errors "^2.3.1" + normalize-package-data "^4.0.0" + npm-normalize-package-bin "^1.0.1" read-pkg-up@^1.0.1: version "1.0.1" @@ -21111,7 +21828,7 @@ readdir-glob@^1.0.0: dependencies: minimatch "^3.0.4" -readdir-scoped-modules@^1.0.0, readdir-scoped-modules@^1.1.0: +readdir-scoped-modules@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz#8d45407b4f870a0dcaebc0e28670d18e74514309" integrity sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw== @@ -21259,7 +21976,7 @@ redux@^4.0.0, redux@^4.0.4, redux@^4.1.2: dependencies: "@babel/runtime" "^7.9.2" -reflect-metadata@^0.1.13: +reflect-metadata@0.1.13, reflect-metadata@^0.1.13: version "0.1.13" resolved "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08" integrity sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg== @@ -21316,13 +22033,14 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" -regexp.prototype.flags@^1.3.1: - version "1.4.1" - resolved "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.1.tgz#b3f4c0059af9e47eca9f3f660e51d81307e72307" - integrity sha512-pMR7hBVUUGI7PMA37m2ofIdQCsomVnas+Jn5UPGAHQ+/LlwKm/aTLJHdasmHRzlfeZwHiAOaRSo2rbBDm3nNUQ== +regexp.prototype.flags@^1.4.1, regexp.prototype.flags@^1.4.3: + version "1.4.3" + resolved "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz#87cab30f80f66660181a3bb7bf5981a872b367ac" + integrity sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA== dependencies: call-bind "^1.0.2" define-properties "^1.1.3" + functions-have-names "^1.2.2" regexpp@^3.2.0: version "3.2.0" @@ -21377,29 +22095,6 @@ relateurl@^0.2.7: resolved "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" integrity sha1-VNvzd+UUQKypCkzSdGANP/LYiKk= -relay-compiler@12.0.0: - version "12.0.0" - resolved "https://registry.npmjs.org/relay-compiler/-/relay-compiler-12.0.0.tgz#9f292d483fb871976018704138423a96c8a45439" - integrity sha512-SWqeSQZ+AMU/Cr7iZsHi1e78Z7oh00I5SvR092iCJq79aupqJ6Ds+I1Pz/Vzo5uY5PY0jvC4rBJXzlIN5g9boQ== - dependencies: - "@babel/core" "^7.14.0" - "@babel/generator" "^7.14.0" - "@babel/parser" "^7.14.0" - "@babel/runtime" "^7.0.0" - "@babel/traverse" "^7.14.0" - "@babel/types" "^7.0.0" - babel-preset-fbjs "^3.4.0" - chalk "^4.0.0" - fb-watchman "^2.0.0" - fbjs "^3.0.0" - glob "^7.1.1" - immutable "~3.7.6" - invariant "^2.2.4" - nullthrows "^1.1.1" - relay-runtime "12.0.0" - signedsource "^1.0.0" - yargs "^15.3.1" - relay-runtime@12.0.0: version "12.0.0" resolved "https://registry.npmjs.org/relay-runtime/-/relay-runtime-12.0.0.tgz#1e039282bdb5e0c1b9a7dc7f6b9a09d4f4ff8237" @@ -21493,9 +22188,9 @@ replace-ext@^1.0.0: integrity sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw== replace-in-file@^6.0.0: - version "6.3.2" - resolved "https://registry.npmjs.org/replace-in-file/-/replace-in-file-6.3.2.tgz#0f19835137177c89932f45df319f3539a019484f" - integrity sha512-Dbt5pXKvFVPL3WAaEB3ZX+95yP0CeAtIPJDwYzHbPP5EAHn+0UoegH/Wg3HKflU9dYBH8UnBC2NvY3P+9EZtTg== + version "6.3.5" + resolved "https://registry.npmjs.org/replace-in-file/-/replace-in-file-6.3.5.tgz#ff956b0ab5bc96613207d603d197cd209400a654" + integrity sha512-arB9d3ENdKva2fxRnSjwBEXfK1npgyci7ZZuwysgAp7ORjHSyxz6oqIjTEv8R0Ydl4Ll7uOAZXL4vbkhGIizCg== dependencies: chalk "^4.1.2" glob "^7.2.0" @@ -21513,7 +22208,7 @@ request-progress@^3.0.0: dependencies: throttleit "^1.0.0" -request@^2.88.0, request@^2.88.2: +request@^2.88.0: version "2.88.2" resolved "https://registry.npmjs.org/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== @@ -21542,7 +22237,7 @@ request@^2.88.0, request@^2.88.2: require-directory@^2.1.1: version "2.1.1" resolved "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" - integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== require-from-string@^2.0.2: version "2.0.2" @@ -21606,7 +22301,7 @@ resolve.exports@^1.1.0: resolved "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz#5ce842b94b05146c0e03076985d1d0e7e48c90c9" integrity sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ== -resolve@^1.1.6, resolve@^1.10.0, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.19.0, resolve@^1.20.0: +resolve@^1.1.6, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.19.0, resolve@^1.20.0: version "1.21.0" resolved "https://registry.npmjs.org/resolve/-/resolve-1.21.0.tgz#b51adc97f3472e6a5cf4444d34bc9d6b9037591f" integrity sha512-3wCbTpk5WJlyE4mSOtDLhqQmGFi0/TD9VPwmiolnk8U0wRgMEktqCXd3vy5buTO3tljvalNvKrjHEfrd2WpEKA== @@ -21615,7 +22310,7 @@ resolve@^1.1.6, resolve@^1.10.0, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.19 path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" -resolve@^1.22.0: +resolve@^1.10.0, resolve@^1.22.0: version "1.22.0" resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198" integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw== @@ -21687,7 +22382,7 @@ ret@~0.1.10: resolved "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== -retry-request@^4.0.0, retry-request@^4.2.2: +retry-request@^4.0.0: version "4.2.2" resolved "https://registry.npmjs.org/retry-request/-/retry-request-4.2.2.tgz#b7d82210b6d2651ed249ba3497f07ea602f1a903" integrity sha512-xA93uxUD/rogV7BV59agW/JHPGXeREMWiZc9jhcwY4YdZ7QOtC7qbomYg0n4wyk2lJhggjvKvhNX8wln/Aldhg== @@ -21695,6 +22390,14 @@ retry-request@^4.0.0, retry-request@^4.2.2: debug "^4.1.1" extend "^3.0.2" +retry-request@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/retry-request/-/retry-request-5.0.0.tgz#886ff8ec0e77fffbe66a4d5e90fd8f6646b6eae4" + integrity sha512-vBZdBxUordje9253imlmGtppC5gdcwZmNz7JnU2ui+KKFPk25keR+0c020AVV20oesYxIFOI0Kh3HE88/59ieg== + dependencies: + debug "^4.1.1" + extend "^3.0.2" + retry@0.13.1: version "0.13.1" resolved "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658" @@ -21769,9 +22472,9 @@ roarr@^2.15.3: sprintf-js "^1.1.2" rollup-plugin-dts@^4.0.1: - version "4.2.1" - resolved "https://registry.npmjs.org/rollup-plugin-dts/-/rollup-plugin-dts-4.2.1.tgz#c17968a0f7c5ae70a9e0ab37e715f3ef63da01c7" - integrity sha512-eaxQZNUJ5iQcxNGlpJ1CUgG4OSVqWjDZ3nNSWBIoGrpcote2aNphSe1RJOaSYkb8dwn3o+rYm1vvld/5z3EGSQ== + version "4.2.2" + resolved "https://registry.npmjs.org/rollup-plugin-dts/-/rollup-plugin-dts-4.2.2.tgz#82876b8784213af29b02cf260b45e404ff835ce1" + integrity sha512-A3g6Rogyko/PXeKoUlkjxkP++8UDVpgA7C+Tdl77Xj4fgEaIjPSnxRmR53EzvoYy97VMVwLAOcWJudaVAuxneQ== dependencies: magic-string "^0.26.1" optionalDependencies: @@ -21807,6 +22510,17 @@ rollup-plugin-postcss@*, rollup-plugin-postcss@^4.0.0: safe-identifier "^0.4.2" style-inject "^0.3.0" +rollup-plugin-smart-asset@^2.1.2: + version "2.1.2" + resolved "https://registry.npmjs.org/rollup-plugin-smart-asset/-/rollup-plugin-smart-asset-2.1.2.tgz#08c171122395b848c0c342eb344d2ffcd87261bc" + integrity sha512-Y/gD3abliw3F20uY6ILpmH6sokXKntuTFKvDdLEJxHFcyQRsTb12u2KbsapqPZDa4JN/Nxx7BslYlZ/KmYAtPg== + dependencies: + big.js "^6.1.1" + magic-string "^0.25.7" + mime "^2.5.2" + mkdirp "^1.0.4" + rollup-pluginutils "^2.8.2" + rollup-pluginutils@^2.8.2: version "2.8.2" resolved "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz#72f2af0748b592364dbd3389e600e5a9444a351e" @@ -21814,6 +22528,14 @@ rollup-pluginutils@^2.8.2: dependencies: estree-walker "^0.6.1" +rollup@^0.60: + version "0.60.7" + resolved "https://registry.npmjs.org/rollup/-/rollup-0.60.7.tgz#2b62ef9306f719b1ab85a7814b3e6596ac51fae8" + integrity sha512-Uj5I1A2PnDgA79P+v1dsNs1IHVydNgeJdKWRfoEJJdNMmyx07TRYqUtPUINaZ/gDusncFy1SZsT3lJnBBI8CGw== + dependencies: + "@types/estree" "0.0.39" + "@types/node" "*" + rollup@^0.63.4: version "0.63.5" resolved "https://registry.npmjs.org/rollup/-/rollup-0.63.5.tgz#5543eecac9a1b83b7e1be598b5be84c9c0a089db" @@ -21823,9 +22545,9 @@ rollup@^0.63.4: "@types/node" "*" rollup@^2.60.2: - version "2.72.1" - resolved "https://registry.npmjs.org/rollup/-/rollup-2.72.1.tgz#861c94790537b10008f0ca0fbc60e631aabdd045" - integrity sha512-NTc5UGy/NWFGpSqF1lFY8z9Adri6uhyMLI6LvPAXdBKoPRFhIIiBUpt+Qg2awixqO3xvzSijjhnb4+QEZwJmxA== + version "2.75.7" + resolved "https://registry.npmjs.org/rollup/-/rollup-2.75.7.tgz#221ff11887ae271e37dcc649ba32ce1590aaa0b9" + integrity sha512-VSE1iy0eaAYNCxEXaleThdFXqZJ42qDBatAwrfnPlENEZ8erQ+0LYX4JXOLPceWfZpV1VtZwZ3dFCuOZiSyFtQ== optionalDependencies: fsevents "~2.3.2" @@ -21848,25 +22570,25 @@ run-parallel@^1.1.9: resolved "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.9.tgz#c9dd3a7cf9f4b2c4b6244e173a6ed866e61dd679" integrity sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q== -run-script-webpack-plugin@^0.0.11: - version "0.0.11" - resolved "https://registry.npmjs.org/run-script-webpack-plugin/-/run-script-webpack-plugin-0.0.11.tgz#04c510bed06b907fa2285e75feece71a25691171" - integrity sha512-QmuBhiqBPmhQLpO5vMBHVTAGyoPBnrCM5gQ3IzgieiImBXiBbXcIv4kysCT1gilFNFxQk22oKQfiIhWbT/zXCw== +run-script-webpack-plugin@^0.0.14: + version "0.0.14" + resolved "https://registry.npmjs.org/run-script-webpack-plugin/-/run-script-webpack-plugin-0.0.14.tgz#fe2362b32c1dab7a8af7a6f1246fc043690cedd7" + integrity sha512-DXe6lzzEVXjBr/74zd4m4yOfmz5P6GMjzhQxDDsViOmwG7cap8UCE6RgD5rT7zf4wM83a+ToHnpB3v4efUv5IA== -rxjs@^6.3.3, rxjs@^6.4.0, rxjs@^6.6.0, rxjs@^6.6.3: - version "6.6.7" - resolved "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" - integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== - dependencies: - tslib "^1.9.0" - -rxjs@^7.1.0, rxjs@^7.2.0, rxjs@^7.5.1, rxjs@^7.5.5: +rxjs@7.5.5, rxjs@^7.0.0, rxjs@^7.1.0, rxjs@^7.2.0, rxjs@^7.5.1, rxjs@^7.5.5: version "7.5.5" resolved "https://registry.npmjs.org/rxjs/-/rxjs-7.5.5.tgz#2ebad89af0f560f460ad5cc4213219e1f7dd4e9f" integrity sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw== dependencies: tslib "^2.1.0" +rxjs@^6.3.3, rxjs@^6.6.0, rxjs@^6.6.3: + version "6.6.7" + resolved "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" + integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== + dependencies: + tslib "^1.9.0" + sade@^1.7.3: version "1.8.1" resolved "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz#0a78e81d658d394887be57d2a409bf703a3b2701" @@ -21879,7 +22601,7 @@ safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.1, safe-buffer@~5.2.0: +safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== @@ -22045,7 +22767,7 @@ semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0: resolved "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@^7.1.1, semver@^7.1.3, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@~7.3.0: +semver@^7.0.0, semver@^7.1.1, semver@^7.1.3, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@~7.3.0: version "7.3.7" resolved "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== @@ -22214,7 +22936,7 @@ shallowequal@^1.1.0: shebang-command@^1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" - integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= + integrity sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg== dependencies: shebang-regex "^1.0.0" @@ -22228,7 +22950,7 @@ shebang-command@^2.0.0: shebang-regex@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" - integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= + integrity sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ== shebang-regex@^3.0.0: version "3.0.0" @@ -22364,21 +23086,17 @@ slice-ansi@^5.0.0: ansi-styles "^6.0.0" is-fullwidth-code-point "^4.0.0" -slide@^1.1.6: - version "1.1.6" - resolved "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707" - integrity sha1-VusCfWW00tzmyy4tMsTUr8nh1wc= - smart-buffer@^4.2.0: version "4.2.0" resolved "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== -smartwrap@^1.2.3: - version "1.2.5" - resolved "https://registry.npmjs.org/smartwrap/-/smartwrap-1.2.5.tgz#45ee3e09ac234e5f7f17c16e916f511834f3cd23" - integrity sha512-bzWRwHwu0RnWjwU7dFy7tF68pDAx/zMSu3g7xr9Nx5J0iSImYInglwEVExyHLxXljy6PWMjkSAbwF7t2mPnRmg== +smartwrap@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/smartwrap/-/smartwrap-2.0.2.tgz#7e25d3dd58b51c6ca4aba3a9e391650ea62698a4" + integrity sha512-vCsKNQxb7PnCNd2wY1WClWifAc2lwqsG8OaswpJkVJsvMGcnEntdTCDajZCkk93Ay1U3t/9puJmb525Rg5MZBA== dependencies: + array.prototype.flat "^1.2.3" breakword "^1.0.5" grapheme-splitter "^1.0.4" strip-ansi "^6.0.0" @@ -22475,13 +23193,13 @@ socket.io@^2.2.0: socket.io-client "2.4.0" socket.io-parser "~3.4.0" -sockjs@^0.3.21: - version "0.3.21" - resolved "https://registry.npmjs.org/sockjs/-/sockjs-0.3.21.tgz#b34ffb98e796930b60a0cfa11904d6a339a7d417" - integrity sha512-DhbPFGpxjc6Z3I+uX07Id5ZO2XwYsWOrYjaSeieES78cq+JaJvVe5q/m1uvjIQhXinhIeCFRH6JgXe+mvVMyXw== +sockjs@^0.3.24: + version "0.3.24" + resolved "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz#c9bc8995f33a111bea0395ec30aa3206bdb5ccce" + integrity sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ== dependencies: faye-websocket "^0.11.3" - uuid "^3.4.0" + uuid "^8.3.2" websocket-driver "^0.7.4" socks-proxy-agent@^5.0.0: @@ -22502,7 +23220,16 @@ socks-proxy-agent@^6.0.0, socks-proxy-agent@^6.1.1: debug "^4.3.1" socks "^2.6.1" -socks@^2.3.3, socks@^2.6.1: +socks-proxy-agent@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz#dc069ecf34436621acb41e3efa66ca1b5fed15b6" + integrity sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww== + dependencies: + agent-base "^6.0.2" + debug "^4.3.3" + socks "^2.6.2" + +socks@^2.3.3, socks@^2.6.1, socks@^2.6.2: version "2.6.2" resolved "https://registry.npmjs.org/socks/-/socks-2.6.2.tgz#ec042d7960073d40d94268ff3bb727dc685f111a" integrity sha512-zDZhHhZRY9PxRruRMR7kMhnf3I8hDs4S3f9RecfnGxvcBHQcKcIH/oUcEWffsfl1XxdYlA7nnlGbbTvPz9D8gA== @@ -22612,7 +23339,7 @@ space-separated-tokens@^2.0.0: spawn-command@^0.0.2-1: version "0.0.2-1" resolved "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz#62f5e9466981c1b796dc5929937e11c9c6921bd0" - integrity sha1-YvXpRmmBwbeW3Fkpk34RycaSG9A= + integrity sha512-n98l9E2RMSJ9ON1AKisHzz7V42VDiBQGY6PB1BwRglz99wpVsSuGzQ+jOi6lFXBGVTCrRpltvjm+/XA+tpeJrg== spawndamnit@^2.0.0: version "2.0.0" @@ -22623,30 +23350,30 @@ spawndamnit@^2.0.0: signal-exit "^3.0.2" spdx-correct@^3.0.0: - version "3.1.0" - resolved "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz#fb83e504445268f154b074e218c87c003cd31df4" - integrity sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q== + version "3.1.1" + resolved "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" + integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w== dependencies: spdx-expression-parse "^3.0.0" spdx-license-ids "^3.0.0" spdx-exceptions@^2.1.0: - version "2.2.0" - resolved "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz#2ea450aee74f2a89bfb94519c07fcd6f41322977" - integrity sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA== + version "2.3.0" + resolved "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" + integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== spdx-expression-parse@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz#99e119b7a5da00e05491c9fa338b7904823b41d0" - integrity sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg== + version "3.0.1" + resolved "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" + integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== dependencies: spdx-exceptions "^2.1.0" spdx-license-ids "^3.0.0" spdx-license-ids@^3.0.0: - version "3.0.5" - resolved "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz#3694b5804567a458d3c8045842a6358632f62654" - integrity sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q== + version "3.0.11" + resolved "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz#50c0d8c40a14ec1bf449bae69a0ea4685a9d9f95" + integrity sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g== spdy-transport@^3.0.0: version "3.0.0" @@ -22724,7 +23451,7 @@ sprintf-js@^1.1.2: sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" - integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== sqlstring@^2.3.2: version "2.3.2" @@ -22772,6 +23499,13 @@ ssri@^8.0.0, ssri@^8.0.1: dependencies: minipass "^3.1.1" +ssri@^9.0.0: + version "9.0.1" + resolved "https://registry.npmjs.org/ssri/-/ssri-9.0.1.tgz#544d4c357a8d7b71a19700074b6883fcb4eae057" + integrity sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q== + dependencies: + minipass "^3.1.1" + stable@^0.1.8: version "0.1.8" resolved "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" @@ -22902,7 +23636,7 @@ stream-shift@^1.0.0: resolved "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d" integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ== -stream-transform@^2.0.1: +stream-transform@^2.1.3: version "2.1.3" resolved "https://registry.npmjs.org/stream-transform/-/stream-transform-2.1.3.tgz#a1c3ecd72ddbf500aa8d342b0b9df38f5aa598e3" integrity sha512-9GHUiM5hMiCi6Y03jD2ARC1ettBXkQBoQAe7nJsPknnI0ow10aXjTnew8QtYQmLjzn974BnmWEAJgCY6ZP1DeQ== @@ -22916,6 +23650,13 @@ strict-event-emitter@^0.2.0: dependencies: events "^3.3.0" +strict-event-emitter@^0.2.4: + version "0.2.4" + resolved "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.2.4.tgz#365714f0c95f059db31064ca745d5b33e5b30f6e" + integrity sha512-xIqTLS5azUH1djSUsLH9DbP6UnM/nI18vu8d43JigCQEoVsnY+mrlE+qv6kYqs6/1OkMnMIiL6ffedQSZStuoQ== + dependencies: + events "^3.3.0" + strict-uri-encode@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546" @@ -22988,35 +23729,37 @@ string-width@^5.0.0: emoji-regex "^9.2.2" strip-ansi "^7.0.1" -string.prototype.matchall@^4.0.6: - version "4.0.6" - resolved "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.6.tgz#5abb5dabc94c7b0ea2380f65ba610b3a544b15fa" - integrity sha512-6WgDX8HmQqvEd7J+G6VtAahhsQIssiZ8zl7zKh1VDMFyL3hRTJP4FTNA3RbIp2TOQ9AYNDcc7e3fH0Qbup+DBg== +string.prototype.matchall@^4.0.7: + version "4.0.7" + resolved "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz#8e6ecb0d8a1fb1fda470d81acecb2dba057a481d" + integrity sha512-f48okCX7JiwVi1NXCVWcFnZgADDC/n2vePlQ/KUCNqCikLLilQvwjMO8+BHVKvgzH0JB0J9LEPgxOGT02RoETg== dependencies: call-bind "^1.0.2" define-properties "^1.1.3" es-abstract "^1.19.1" get-intrinsic "^1.1.1" - has-symbols "^1.0.2" + has-symbols "^1.0.3" internal-slot "^1.0.3" - regexp.prototype.flags "^1.3.1" + regexp.prototype.flags "^1.4.1" side-channel "^1.0.4" -string.prototype.trimend@^1.0.4: - version "1.0.4" - resolved "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80" - integrity sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A== +string.prototype.trimend@^1.0.4, string.prototype.trimend@^1.0.5: + version "1.0.5" + resolved "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz#914a65baaab25fbdd4ee291ca7dde57e869cb8d0" + integrity sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog== dependencies: call-bind "^1.0.2" - define-properties "^1.1.3" + define-properties "^1.1.4" + es-abstract "^1.19.5" -string.prototype.trimstart@^1.0.4: - version "1.0.4" - resolved "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz#b36399af4ab2999b4c9c648bd7a3fb2bb26feeed" - integrity sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw== +string.prototype.trimstart@^1.0.4, string.prototype.trimstart@^1.0.5: + version "1.0.5" + resolved "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz#5466d93ba58cfa2134839f81d7f42437e8c01fef" + integrity sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg== dependencies: call-bind "^1.0.2" - define-properties "^1.1.3" + define-properties "^1.1.4" + es-abstract "^1.19.5" string_decoder@^1.0.0, string_decoder@^1.1.1: version "1.3.0" @@ -23092,7 +23835,7 @@ strip-bom@^2.0.0: strip-bom@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" - integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= + integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== strip-bom@^4.0.0: version "4.0.0" @@ -23109,6 +23852,11 @@ strip-final-newline@^2.0.0: resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== +strip-final-newline@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz#52894c313fbff318835280aed60ff71ebf12b8fd" + integrity sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw== + strip-indent@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001" @@ -23170,6 +23918,22 @@ style-to-object@^0.3.0: dependencies: inline-style-parser "0.1.1" +styled-components@^5.3.3: + version "5.3.5" + resolved "https://registry.npmjs.org/styled-components/-/styled-components-5.3.5.tgz#a750a398d01f1ca73af16a241dec3da6deae5ec4" + integrity sha512-ndETJ9RKaaL6q41B69WudeqLzOpY1A/ET/glXkNZ2T7dPjPqpPCXXQjDFYZWwNnE5co0wX+gTCqx9mfxTmSIPg== + dependencies: + "@babel/helper-module-imports" "^7.0.0" + "@babel/traverse" "^7.4.5" + "@emotion/is-prop-valid" "^1.1.0" + "@emotion/stylis" "^0.8.4" + "@emotion/unitless" "^0.7.4" + babel-plugin-styled-components ">= 1.12.0" + css-to-react-native "^3.0.0" + hoist-non-react-statics "^3.0.0" + shallowequal "^1.1.0" + supports-color "^5.5.0" + stylehacks@^5.0.1: version "5.0.1" resolved "https://registry.npmjs.org/stylehacks/-/stylehacks-5.0.1.tgz#323ec554198520986806388c7fdaebc38d2c06fb" @@ -23246,11 +24010,6 @@ supports-color@^8.0.0, supports-color@^8.1.0, supports-color@^8.1.1: dependencies: has-flag "^4.0.0" -supports-color@^9.2.1: - version "9.2.2" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-9.2.2.tgz#502acaf82f2b7ee78eb7c83dcac0f89694e5a7bb" - integrity sha512-XC6g/Kgux+rJXmwokjm9ECpD6k/smUoS5LKlUCcsYr4IY3rW0XyAympon2RmxGrlnZURMpg5T18gWDP9CsHXFA== - supports-hyperlinks@^2.0.0: version "2.1.0" resolved "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.1.0.tgz#f663df252af5f37c5d49bbd7eeefa9e0b9e59e47" @@ -23301,10 +24060,10 @@ swagger-client@^3.18.5: traverse "~0.6.6" url "~0.11.0" -swagger-ui-react@^4.1.3: - version "4.11.1" - resolved "https://registry.npmjs.org/swagger-ui-react/-/swagger-ui-react-4.11.1.tgz#1de7feea2f99aa292d52cfa3d31edf665eaa6181" - integrity sha512-w7WkrEL7uw1EofILX9hNKQXqy5EQhxTU3u+Ob8YcLqeAEUjECewtyTvsQ+79Xypj9yXbqSvgdPkpho1n5Bo2NQ== +swagger-ui-react@^4.11.1: + version "4.12.0" + resolved "https://registry.npmjs.org/swagger-ui-react/-/swagger-ui-react-4.12.0.tgz#bfdf67bd0679fdd62ec3fb98bd827afb81fb7fe6" + integrity sha512-pkSkUe4WVb7omoScK8GG2qgD6LxwZEIA+fKcxeACC7PCW52vFEjle6WO6lFOLj1JmfWriL7Ngi0+wJtVjfhnWw== dependencies: "@babel/runtime-corejs3" "^7.16.8" "@braintree/sanitize-url" "=6.0.0" @@ -23411,19 +24170,6 @@ tar-stream@^2.0.0, tar-stream@^2.1.4, tar-stream@^2.2.0: inherits "^2.0.3" readable-stream "^3.1.1" -tar@^4.4.12: - version "4.4.19" - resolved "https://registry.npmjs.org/tar/-/tar-4.4.19.tgz#2e4d7263df26f2b914dee10c825ab132123742f3" - integrity sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA== - dependencies: - chownr "^1.1.4" - fs-minipass "^1.2.7" - minipass "^2.9.0" - minizlib "^1.3.3" - mkdirp "^0.5.5" - safe-buffer "^5.2.1" - yallist "^3.1.1" - tar@^6.0.2, tar@^6.1.0, tar@^6.1.11, tar@^6.1.2: version "6.1.11" resolved "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621" @@ -23449,20 +24195,20 @@ tdigest@^0.1.1: bintrees "1.0.1" "techdocs-cli-embedded-app@link:packages/techdocs-cli-embedded-app": - version "0.2.70" + version "0.2.71" dependencies: - "@backstage/app-defaults" "^1.0.2" - "@backstage/catalog-model" "^1.0.2" - "@backstage/cli" "^0.17.1" + "@backstage/app-defaults" "^1.0.3" + "@backstage/catalog-model" "^1.0.3" + "@backstage/cli" "^0.17.2" "@backstage/config" "^1.0.1" - "@backstage/core-app-api" "^1.0.2" - "@backstage/core-components" "^0.9.4" - "@backstage/core-plugin-api" "^1.0.2" - "@backstage/integration-react" "^1.1.0" - "@backstage/plugin-catalog" "^1.2.0" - "@backstage/plugin-techdocs" "^1.1.1" - "@backstage/plugin-techdocs-react" "^1.0.0" - "@backstage/test-utils" "^1.1.0" + "@backstage/core-app-api" "^1.0.3" + "@backstage/core-components" "^0.9.5" + "@backstage/core-plugin-api" "^1.0.3" + "@backstage/integration-react" "^1.1.1" + "@backstage/plugin-catalog" "^1.3.0" + "@backstage/plugin-techdocs" "^1.2.0" + "@backstage/plugin-techdocs-react" "^1.0.1" + "@backstage/test-utils" "^1.1.1" "@backstage/theme" "^0.2.15" "@material-ui/core" "^4.11.0" "@material-ui/icons" "^4.9.1" @@ -23473,10 +24219,10 @@ tdigest@^0.1.1: react-router-dom "6.0.0-beta.0" react-use "^17.2.4" -teeny-request@^7.1.3: - version "7.2.0" - resolved "https://registry.npmjs.org/teeny-request/-/teeny-request-7.2.0.tgz#41347ece068f08d741e7b86df38a4498208b2633" - integrity sha512-SyY0pek1zWsi0LRVAALem+avzMLc33MKW/JLLakdP4s9+D7+jHcy5x6P+h94g2QNZsAqQNfX5lsbd3WSeJXrrw== +teeny-request@^8.0.0: + version "8.0.0" + resolved "https://registry.npmjs.org/teeny-request/-/teeny-request-8.0.0.tgz#9614410ba70114fd28ba7bf5077dce3e2f02adf7" + integrity sha512-6KEYxXI4lQPSDkXzXpPmJPNmo7oqduFFbhOEHf8sfsLbXyCsb+umUjBtMGAKhaSToD8JNCtQutTRefu29K64JA== dependencies: http-proxy-agent "^5.0.0" https-proxy-agent "^5.0.0" @@ -23489,17 +24235,6 @@ temp-dir@^1.0.0: resolved "https://registry.npmjs.org/temp-dir/-/temp-dir-1.0.0.tgz#0a7c0ea26d3a39afa7e0ebea9c1fc0bc4daa011d" integrity sha1-CnwOom06Oa+n4OvqnB/AvE2qAR0= -temp-write@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/temp-write/-/temp-write-4.0.0.tgz#cd2e0825fc826ae72d201dc26eef3bf7e6fc9320" - integrity sha512-HIeWmj77uOOHb0QX7siN3OtwV3CTntquin6TNVg6SHOqCP3hYKmox90eeFOGaY1MqJ9WYDDjkyZrW6qS5AWpbw== - dependencies: - graceful-fs "^4.1.15" - is-stream "^2.0.0" - make-dir "^3.0.0" - temp-dir "^1.0.0" - uuid "^3.3.2" - temp@^0.8.4: version "0.8.4" resolved "https://registry.npmjs.org/temp/-/temp-0.8.4.tgz#8c97a33a4770072e0a05f919396c7665a7dd59f2" @@ -23508,9 +24243,9 @@ temp@^0.8.4: rimraf "~2.6.2" term-size@^2.1.0: - version "2.2.0" - resolved "https://registry.npmjs.org/term-size/-/term-size-2.2.0.tgz#1f16adedfe9bdc18800e1776821734086fcc6753" - integrity sha512-a6sumDlzyHVJWb8+YofY4TW112G6p2FCPEAFk+59gIYHv3XHRhm9ltVQ9kli4hNWeQBwSpe8cRN25x0ROunMOw== + version "2.2.1" + resolved "https://registry.npmjs.org/term-size/-/term-size-2.2.1.tgz#2a6a54840432c2fb6320fea0f415531e90189f54" + integrity sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg== terminal-link@^2.0.0: version "2.1.1" @@ -23521,14 +24256,14 @@ terminal-link@^2.0.0: supports-hyperlinks "^2.0.0" terser-webpack-plugin@*, terser-webpack-plugin@^5.1.3: - version "5.3.1" - resolved "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.1.tgz#0320dcc270ad5372c1e8993fabbd927929773e54" - integrity sha512-GvlZdT6wPQKbDNW/GDQzZFg/j4vKU96yl2q6mcUkzKOgW4gwf1Z8cZToUCrz31XHlPWH8MVb1r2tFtdDtTGJ7g== + version "5.3.3" + resolved "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.3.tgz#8033db876dd5875487213e87c627bca323e5ed90" + integrity sha512-Fx60G5HNYknNTNQnzQ1VePRuu89ZVYWfjRAeT5rITuCY/1b08s49e5kSQwHDirKZWuoKOBRFS98EUUoZ9kLEwQ== dependencies: + "@jridgewell/trace-mapping" "^0.3.7" jest-worker "^27.4.5" schema-utils "^3.1.1" serialize-javascript "^6.0.0" - source-map "^0.6.1" terser "^5.7.2" terser@^5.10.0, terser@^5.7.2: @@ -23863,6 +24598,11 @@ treeverse@^1.0.4: resolved "https://registry.npmjs.org/treeverse/-/treeverse-1.0.4.tgz#a6b0ebf98a1bca6846ddc7ecbc900df08cb9cd5f" integrity sha512-whw60l7r+8ZU8Tu/Uc2yxtc4ZTZbR/PF3u1IPNKGQ6p8EICLb3Z2lAgoqw9bqYd8IkgnsaOcLzYHFckjqNsf0g== +treeverse@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/treeverse/-/treeverse-2.0.0.tgz#036dcef04bc3fd79a9b79a68d4da03e882d8a9ca" + integrity sha512-N5gJCkLu1aXccpOTtqV6ddSEi6ZmGkh3hjmbu1IjcavJK4qyOVQmi0myQKM7z5jVGmD68SJoliaVrMmVObhj6A== + trim-newlines@^3.0.0: version "3.0.1" resolved "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz#260a5d962d8b752425b32f3a7db0dcacd176c144" @@ -23910,12 +24650,20 @@ ts-log@^2.2.3: resolved "https://registry.npmjs.org/ts-log/-/ts-log-2.2.3.tgz#4da5640fe25a9fb52642cd32391c886721318efb" integrity sha512-XvB+OdKSJ708Dmf9ore4Uf/q62AYDTzFcAdxc8KNML1mmAWywRFVt/dn1KYJH8Agt5UJNujfM3znU5PxgAzA2w== -ts-node@^10.0.0, ts-node@^10.2.1, ts-node@^10.4.0: - version "10.7.0" - resolved "https://registry.npmjs.org/ts-node/-/ts-node-10.7.0.tgz#35d503d0fab3e2baa672a0e94f4b40653c2463f5" - integrity sha512-TbIGS4xgJoX2i3do417KSaep1uRAW/Lu+WAL2doDHC0D6ummjirVOXU5/7aiZotbQ5p1Zp9tP7U6cYhA0O7M8A== +ts-morph@^15.0.0: + version "15.1.0" + resolved "https://registry.npmjs.org/ts-morph/-/ts-morph-15.1.0.tgz#53deea5296d967ff6eba8f15f99d378aa7074a4e" + integrity sha512-RBsGE2sDzUXFTnv8Ba22QfeuKbgvAGJFuTN7HfmIRUkgT/NaVLfDM/8OFm2NlFkGlWEXdpW5OaFIp1jvqdDuOg== dependencies: - "@cspotcode/source-map-support" "0.7.0" + "@ts-morph/common" "~0.16.0" + code-block-writer "^11.0.0" + +ts-node@^10.0.0, ts-node@^10.2.1, ts-node@^10.4.0: + version "10.8.1" + resolved "https://registry.npmjs.org/ts-node/-/ts-node-10.8.1.tgz#ea2bd3459011b52699d7e88daa55a45a1af4f066" + integrity sha512-Wwsnao4DQoJsN034wePSg5nZiw4YKXf56mPIAeD6wVmiv+RytNSWqc2f3fKvcUoV+Yn2+yocD71VOfQHbmVX4g== + dependencies: + "@cspotcode/source-map-support" "^0.8.0" "@tsconfig/node10" "^1.0.7" "@tsconfig/node12" "^1.0.7" "@tsconfig/node14" "^1.0.0" @@ -23926,7 +24674,7 @@ ts-node@^10.0.0, ts-node@^10.2.1, ts-node@^10.4.0: create-require "^1.1.0" diff "^4.0.1" make-error "^1.1.1" - v8-compile-cache-lib "^3.0.0" + v8-compile-cache-lib "^3.0.1" yn "3.1.1" ts-node@^9: @@ -23951,17 +24699,22 @@ tsconfig-paths@^3.14.1: minimist "^1.2.6" strip-bom "^3.0.0" +tslib@2.0.3: + version "2.0.3" + resolved "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz#8e0741ac45fc0c226e58a17bfc3e64b9bc6ca61c" + integrity sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ== + +tslib@2.3.1, tslib@^2, tslib@^2.0.0, tslib@^2.0.1, tslib@^2.0.3, tslib@^2.2.0, tslib@^2.3.0, tslib@^2.3.1, tslib@~2.3.0: + version "2.3.1" + resolved "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" + integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== + tslib@^1.13.0, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: version "1.14.1" resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2, tslib@^2.0.0, tslib@^2.0.1, tslib@^2.0.3, tslib@^2.2.0, tslib@^2.3.0, tslib@^2.3.1, tslib@~2.3.0: - version "2.3.1" - resolved "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" - integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== - -tslib@^2.1.0, tslib@~2.4.0: +tslib@^2.1.0, tslib@^2.4.0, tslib@~2.4.0: version "2.4.0" resolved "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== @@ -23978,17 +24731,18 @@ tty-browserify@0.0.0: resolved "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" integrity sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY= -tty-table@^2.8.10: - version "2.8.13" - resolved "https://registry.npmjs.org/tty-table/-/tty-table-2.8.13.tgz#d484a416381973eaebbdf19c79136b390e5c6d70" - integrity sha512-eVV/+kB6fIIdx+iUImhXrO22gl7f6VmmYh0Zbu6C196fe1elcHXd7U6LcLXu0YoVPc2kNesWiukYcdK8ZmJ6aQ== +tty-table@^4.1.5: + version "4.1.6" + resolved "https://registry.npmjs.org/tty-table/-/tty-table-4.1.6.tgz#6bd58338f36c94cce478c3337934d8a65ab40a73" + integrity sha512-kRj5CBzOrakV4VRRY5kUWbNYvo/FpOsz65DzI5op9P+cHov3+IqPbo1JE1ZnQGkHdZgNFDsrEjrfqqy/Ply9fw== dependencies: - chalk "^3.0.0" - csv "^5.3.1" - smartwrap "^1.2.3" + chalk "^4.1.2" + csv "^5.5.0" + kleur "^4.1.4" + smartwrap "^2.0.2" strip-ansi "^6.0.0" wcwidth "^1.0.1" - yargs "^15.1.0" + yargs "^17.1.1" tunnel-agent@^0.6.0: version "0.6.0" @@ -24096,28 +24850,29 @@ typedarray@^0.0.6: integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= typescript-json-schema@^0.53.0: - version "0.53.0" - resolved "https://registry.npmjs.org/typescript-json-schema/-/typescript-json-schema-0.53.0.tgz#ac5b89e4b0af55be422f475a041360e0556f88ea" - integrity sha512-BcFxC9nipQQOXxrBGI/jOWU31BwzVh6vqJR008G8VHKJtQ8YrZX6veriXfTK1l+L0/ff0yKl3mZigMLA6ZqkHg== + version "0.53.1" + resolved "https://registry.npmjs.org/typescript-json-schema/-/typescript-json-schema-0.53.1.tgz#9204547f3e145169b40928998366ff6d28b81d32" + integrity sha512-Hg+RnOKUd38MOzC0rDft03a8xvwO+gCcj1F77smw2tCoZYQpFoLtrXWBGdvCX+REliko5WYel2kux17HPFqjLQ== dependencies: "@types/json-schema" "^7.0.9" "@types/node" "^16.9.2" glob "^7.1.7" + path-equal "1.1.2" safe-stable-stringify "^2.2.0" ts-node "^10.2.1" - typescript "~4.5.0" + typescript "~4.6.0" yargs "^17.1.1" -typescript@~4.5.0: - version "4.5.5" - resolved "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz#d8c953832d28924a9e3d37c73d729c846c5896f3" - integrity sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA== - -typescript@~4.6.3, typescript@~4.6.4: +typescript@~4.6.0, typescript@~4.6.3: version "4.6.4" resolved "https://registry.npmjs.org/typescript/-/typescript-4.6.4.tgz#caa78bbc3a59e6a5c510d35703f6a09877ce45e9" integrity sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg== +typescript@~4.7.0: + version "4.7.4" + resolved "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz#1a88596d1cf47d59507a1bcdfb5b9dfe4d488235" + integrity sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ== + ua-parser-js@^0.7.18: version "0.7.28" resolved "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.28.tgz#8ba04e653f35ce210239c64661685bf9121dec31" @@ -24133,11 +24888,6 @@ uglify-js@^3.1.4: resolved "https://registry.npmjs.org/uglify-js/-/uglify-js-3.14.3.tgz#c0f25dfea1e8e5323eccf59610be08b6043c15cf" integrity sha512-mic3aOdiq01DuSVx0TseaEzMIVqebMZ0Z3vaeDhFEh9bsc24hV1TFvN74reA2vs08D0ZWfNjAcJ3UbVLaBss+g== -uid-number@0.0.6: - version "0.0.6" - resolved "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" - integrity sha1-DqEOgDXo61uOREnwbaHHMGY7qoE= - uid-safe@~2.1.5: version "2.1.5" resolved "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz#2b3d5c7240e8fc2e58f8aa269e5ee49c0857bd3a" @@ -24150,19 +24900,14 @@ uid2@0.0.3, uid2@0.0.x: resolved "https://registry.npmjs.org/uid2/-/uid2-0.0.3.tgz#483126e11774df2f71b8b639dcd799c376162b82" integrity sha1-SDEm4Rd03y9xuLY53NeZw3YWK4I= -umask@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/umask/-/umask-1.1.0.tgz#f29cebf01df517912bb58ff9c4e50fde8e33320d" - integrity sha1-8pzr8B31F5ErtY/5xOUP3o4zMg0= - -unbox-primitive@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz#085e215625ec3162574dc8859abee78a59b14471" - integrity sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw== +unbox-primitive@^1.0.1, unbox-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" + integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== dependencies: - function-bind "^1.1.1" - has-bigints "^1.0.1" - has-symbols "^1.0.2" + call-bind "^1.0.2" + has-bigints "^1.0.2" + has-symbols "^1.0.3" which-boxed-primitive "^1.0.2" unbzip2-stream@1.4.3: @@ -24189,9 +24934,9 @@ underscore@^1.12.1, underscore@^1.9.1: integrity sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g== undici@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/undici/-/undici-5.0.0.tgz#3c1e08c7f0df90c485d5d8dbb0517e11e34f2090" - integrity sha512-VhUpiZ3No1DOPPQVQnsDZyfcbTTcHdcgWej1PdFnSvOeJmOVDgiOHkunJmBLfmjt4CqgPQddPVjSWW0dsTs5Yg== + version "5.5.1" + resolved "https://registry.npmjs.org/undici/-/undici-5.5.1.tgz#baaf25844a99eaa0b22e1ef8d205bffe587c8f43" + integrity sha512-MEvryPLf18HvlCbLSzCW0U00IMftKGI5udnjrQbC5D4P0Hodwffhv+iGfWuJwg16Y/TK11ZFK8i+BPVW2z/eAw== unicode-canonical-property-names-ecmascript@^1.0.4: version "1.0.4" @@ -24540,13 +25285,6 @@ util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= -util-promisify@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/util-promisify/-/util-promisify-2.1.0.tgz#3c2236476c4d32c5ff3c47002add7c13b9a82a53" - integrity sha1-PCI2R2xNMsX/PEcAKt18E7moKlM= - dependencies: - object.getownpropertydescriptors "^2.0.3" - util@0.10.3: version "0.10.3" resolved "https://registry.npmjs.org/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" @@ -24578,26 +25316,26 @@ utila@~0.4: resolved "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c" integrity sha1-ihagXURWV6Oupe7MWxKk+lN5dyw= -utils-merge@1.0.1, utils-merge@1.x.x: +utils-merge@1.0.1, utils-merge@1.x.x, utils-merge@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= -uuid@3.3.2: - version "3.3.2" - resolved "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" - integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA== +uuid@8.0.0: + version "8.0.0" + resolved "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz#bc6ccf91b5ff0ac07bbcdbf1c7c4e150db4dbb6c" + integrity sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw== -uuid@^3.3.2, uuid@^3.4.0: - version "3.4.0" - resolved "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" - integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== - -uuid@^8.0.0, uuid@^8.2.0, uuid@^8.3.0, uuid@^8.3.2: +uuid@8.3.2, uuid@^8.0.0, uuid@^8.2.0, uuid@^8.3.0, uuid@^8.3.2: version "8.3.2" resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== +uuid@^3.3.2: + version "3.4.0" + resolved "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" + integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== + uvu@^0.5.0: version "0.5.3" resolved "https://registry.npmjs.org/uvu/-/uvu-0.5.3.tgz#3d83c5bc1230f153451877bfc7f4aea2392219ae" @@ -24608,10 +25346,10 @@ uvu@^0.5.0: kleur "^4.0.3" sade "^1.7.3" -v8-compile-cache-lib@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.0.tgz#0582bcb1c74f3a2ee46487ceecf372e46bce53e8" - integrity sha512-mpSYqfsFvASnSn5qMiwrr4VKfumbPyONLCOPmsR3A6pTY/r0+tSaVbgPWSAIuzbk3lCTa+FForeTiO+wBQGkjA== +v8-compile-cache-lib@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" + integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== v8-compile-cache@^2.0.3: version "2.1.0" @@ -24647,6 +25385,13 @@ validate-npm-package-name@^3.0.0: dependencies: builtins "^1.0.3" +validate-npm-package-name@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-4.0.0.tgz#fe8f1c50ac20afdb86f177da85b3600f0ac0d747" + integrity sha512-mzR0L8ZDktZjpX4OB46KT+56MAhl4EIazWP/+G/HPGuvfdaqg4YsCdtOm6U9+LOFyYDoh4dpnpxZRB9MQQns5Q== + dependencies: + builtins "^5.0.0" + validate.io-array@^1.0.3: version "1.0.6" resolved "https://registry.npmjs.org/validate.io-array/-/validate.io-array-1.0.6.tgz#5b5a2cafd8f8b85abb2f886ba153f2d93a27774d" @@ -24829,7 +25574,7 @@ wbuf@^1.1.0, wbuf@^1.7.3: dependencies: minimalistic-assert "^1.0.0" -wcwidth@^1.0.0, wcwidth@^1.0.1: +wcwidth@>=1.0.1, wcwidth@^1.0.0, wcwidth@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" integrity sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g= @@ -24873,14 +25618,15 @@ webpack-dev-middleware@^5.3.1: schema-utils "^4.0.0" webpack-dev-server@^4.7.3: - version "4.9.0" - resolved "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.9.0.tgz#737dbf44335bb8bde68f8f39127fc401c97a1557" - integrity sha512-+Nlb39iQSOSsFv0lWUuUTim3jDQO8nhK3E68f//J2r5rIcp4lULHXz2oZ0UVdEeWXEh5lSzYUlzarZhDAeAVQw== + version "4.9.2" + resolved "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.9.2.tgz#c188db28c7bff12f87deda2a5595679ebbc3c9bc" + integrity sha512-H95Ns95dP24ZsEzO6G9iT+PNw4Q7ltll1GfJHV4fKphuHWgKFzGHWi4alTlTnpk1SPPk41X+l2RB7rLfIhnB9Q== dependencies: "@types/bonjour" "^3.5.9" "@types/connect-history-api-fallback" "^1.3.5" "@types/express" "^4.17.13" "@types/serve-index" "^1.9.1" + "@types/serve-static" "^1.13.10" "@types/sockjs" "^0.3.33" "@types/ws" "^8.5.1" ansi-html-community "^0.0.8" @@ -24901,7 +25647,7 @@ webpack-dev-server@^4.7.3: schema-utils "^4.0.0" selfsigned "^2.0.1" serve-index "^1.9.1" - sockjs "^0.3.21" + sockjs "^0.3.24" spdy "^4.0.2" webpack-dev-middleware "^5.3.1" ws "^8.4.2" @@ -24925,9 +25671,9 @@ webpack-sources@^3.2.3: integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== webpack@^5, webpack@^5.66.0: - version "5.72.1" - resolved "https://registry.npmjs.org/webpack/-/webpack-5.72.1.tgz#3500fc834b4e9ba573b9f430b2c0a61e1bb57d13" - integrity sha512-dXG5zXCLspQR4krZVR6QgajnZOjW2K/djHvdcRaDQvsjV9z9vaW6+ja5dZOYbqBBjF6kGXka/2ZyxNdc+8Jung== + version "5.73.0" + resolved "https://registry.npmjs.org/webpack/-/webpack-5.73.0.tgz#bbd17738f8a53ee5760ea2f59dce7f3431d35d38" + integrity sha512-svjudQRPPa0YiOYa2lM/Gacw0r6PvxptHj4FuEKQ2kX05ZLkjbVc5MnPs6its5j7IZljnIqSVo/OsY2X0IpHGA== dependencies: "@types/eslint-scope" "^3.7.3" "@types/estree" "^0.0.51" @@ -25026,7 +25772,7 @@ which-module@^1.0.0: which-module@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" - integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= + integrity sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q== which-pm@2.0.0: version "2.0.0" @@ -25063,7 +25809,7 @@ which@^2.0.1, which@^2.0.2: dependencies: isexe "^2.0.0" -wide-align@^1.1.0, wide-align@^1.1.2: +wide-align@^1.1.0, wide-align@^1.1.2, wide-align@^1.1.5: version "1.1.5" resolved "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg== @@ -25098,7 +25844,7 @@ winston-transport@^4.5.0: readable-stream "^3.6.0" triple-beam "^1.3.0" -winston@^3.2.1: +winston@^3.2.1, winston@^3.7.2: version "3.7.2" resolved "https://registry.npmjs.org/winston/-/winston-3.7.2.tgz#95b4eeddbec902b3db1424932ac634f887c400b1" integrity sha512-QziIqtojHBoyzUOdQvQiar1DH0Xp9nF1A1y7NVy2DGEsz82SBDtOalS0ulTRGVT14xPX3WRWkCsdcJKqNflKng== @@ -25223,16 +25969,21 @@ write-pkg@^4.0.0: type-fest "^0.4.1" write-json-file "^3.2.0" -ws@8.6.0, ws@^8.0.0, ws@^8.3.0: - version "8.6.0" - resolved "https://registry.npmjs.org/ws/-/ws-8.6.0.tgz#e5e9f1d9e7ff88083d0c0dd8281ea662a42c9c23" - integrity sha512-AzmM3aH3gk0aX7/rZLYvjdvZooofDu3fFOzGqcSnQ1tOcTWwhM/o+q++E8mAyVVIyUdajrkzWUGftaVSDLn1bw== +ws@8.7.0: + version "8.7.0" + resolved "https://registry.npmjs.org/ws/-/ws-8.7.0.tgz#eaf9d874b433aa00c0e0d8752532444875db3957" + integrity sha512-c2gsP0PRwcLFzUiA8Mkr37/MI7ilIlHQxaEAtd0uNMbVMoy8puJyafRlm0bV9MbGSabUPeLrRRaqIBcFcA2Pqg== ws@^7.3.1, ws@^7.4.6: version "7.5.7" resolved "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz#9e0ac77ee50af70d58326ecff7e85eb3fa375e67" integrity sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A== +ws@^8.0.0, ws@^8.3.0: + version "8.8.0" + resolved "https://registry.npmjs.org/ws/-/ws-8.8.0.tgz#8e71c75e2f6348dbf8d78005107297056cb77769" + integrity sha512-JDAgSYQ1ksuwqfChJusw1LSJ8BizJ2e/vVu5Lxjq3YvNJNlROv1ui4i+c/kUUrPheBvQl4c5UbERhTwKa6QBJQ== + ws@^8.4.2: version "8.5.0" resolved "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz#bfb4be96600757fe5382de12c670dab984a1ed4f" @@ -25379,9 +26130,9 @@ y18n@^5.0.5: yallist@^2.1.2: version "2.1.2" resolved "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" - integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= + integrity sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A== -yallist@^3.0.0, yallist@^3.0.2, yallist@^3.1.1: +yallist@^3.0.2: version "3.1.1" resolved "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== @@ -25401,6 +26152,11 @@ yaml@^1.10.0, yaml@^1.10.2, yaml@^1.7.2, yaml@^1.9.2: resolved "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== +yaml@^2.1.1: + version "2.1.1" + resolved "https://registry.npmjs.org/yaml/-/yaml-2.1.1.tgz#1e06fb4ca46e60d9da07e4f786ea370ed3c3cfec" + integrity sha512-o96x3OPo8GjWeSLF+wOAbrPfhFOGY0W00GNaxCDv+9hkcDJEnev1yh8S7pgHF0ik6zc8sQLuL8hjHjJULZp8bw== + yargs-parser@20.2.4, yargs-parser@^20.2.3: version "20.2.4" resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" @@ -25449,7 +26205,7 @@ yargs@^15.1.0, yargs@^15.3.1: y18n "^4.0.0" yargs-parser "^18.1.2" -yargs@^16.1.1, yargs@^16.2.0: +yargs@^16.2.0: version "16.2.0" resolved "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== @@ -25462,7 +26218,7 @@ yargs@^16.1.1, yargs@^16.2.0: y18n "^5.0.5" yargs-parser "^20.2.2" -yargs@^17.0.0, yargs@^17.0.1, yargs@^17.1.1, yargs@^17.2.1, yargs@^17.3.0: +yargs@^17.0.0, yargs@^17.2.1: version "17.3.1" resolved "https://registry.npmjs.org/yargs/-/yargs-17.3.1.tgz#da56b28f32e2fd45aefb402ed9c26f42be4c07b9" integrity sha512-WUANQeVgjLbNsEmGk20f+nlHgOqzRFpiGWVaBrYGYIGANIIu3lWjoyi0fNlFmJkvfhCZ6BXINe7/W2O2bV4iaA== @@ -25475,7 +26231,7 @@ yargs@^17.0.0, yargs@^17.0.1, yargs@^17.1.1, yargs@^17.2.1, yargs@^17.3.0: y18n "^5.0.5" yargs-parser "^21.0.0" -yargs@^17.3.1: +yargs@^17.1.1, yargs@^17.3.1: version "17.5.1" resolved "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz#e109900cab6fcb7fd44b1d8249166feb0b36e58e" integrity sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA== @@ -25589,6 +26345,11 @@ yn@^4.0.0: resolved "https://registry.npmjs.org/yn/-/yn-4.0.0.tgz#611480051ea43b510da1dfdbe177ed159f00a979" integrity sha512-huWiiCS4TxKc4SfgmTwW1K7JmXPPAmuXWYy4j9qjQo4+27Kni8mGhAAi1cloRWmBe2EqcLgt3IGqQoRL/MtPgg== +yn@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/yn/-/yn-5.0.0.tgz#63fc2e2e0056cf294397eed6ad4a3fbdf707f26f" + integrity sha512-+l37+9TyGEsyxGLaTg6QgYy5KnOp74ZZl4dPFLQpBWSkO99uBC5jnS0pOGwXFViPbiaEtWbParH2KrgWWF2duQ== + yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" @@ -25643,9 +26404,9 @@ zip-stream@^4.1.0: readable-stream "^3.6.0" zod@^3.11.6, zod@^3.9.5: - version "3.16.0" - resolved "https://registry.npmjs.org/zod/-/zod-3.16.0.tgz#edfdbf77fcc9a5af13a2630a44bdda5b90e759b7" - integrity sha512-szrIkryADbTM+xBt2a1KoS2CJQXec4f9xG78bj5MJeEH/XqmmHpnO+fG3IE115AKBJak+2HrbxLZkc9mhdbDKA== + version "3.17.3" + resolved "https://registry.npmjs.org/zod/-/zod-3.17.3.tgz#86abbc670ff0063a4588d85a4dcc917d6e4af2ba" + integrity sha512-4oKP5zvG6GGbMlqBkI5FESOAweldEhSOZ6LI6cG+JzUT7ofj1ZOC0PJudpQOpT1iqOFpYYtX5Pw0+o403y4bcg== zustand@3.6.9: version "3.6.9"