diff --git a/.changeset/beige-mangos-knock.md b/.changeset/beige-mangos-knock.md deleted file mode 100644 index d8723c3f87..0000000000 --- a/.changeset/beige-mangos-knock.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-github-actions': patch ---- - -Show empty state only when workflow API call has completed diff --git a/.changeset/weak-berries-heal.md b/.changeset/weak-berries-heal.md deleted file mode 100644 index 4160d078fc..0000000000 --- a/.changeset/weak-berries-heal.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -'@backstage/plugin-apache-airflow': minor ---- - -Introduces a new plugin for the Apache Airflow workflow management platform. -This implementation has been tested with the Apache Airflow v2 API, -authenticating with basic authentication through the Backstage proxy plugin. - -Supported functionality includes: - -- Information card of version information of the Airflow instance -- Information card of instance health for the meta-database and scheduler -- Table of DAGs with meta information and status, along with a link to view - details in the Airflow UI diff --git a/.github/workflows/changeset.yml b/.github/workflows/changeset.yml index 70afd15eee..a649904a01 100644 --- a/.github/workflows/changeset.yml +++ b/.github/workflows/changeset.yml @@ -10,7 +10,7 @@ jobs: name: Create Changeset PR runs-on: ubuntu-latest steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v2 - name: Install Dependencies run: yarn --frozen-lockfile - name: Create Release Pull Request diff --git a/.github/workflows/fossa.yml b/.github/workflows/fossa.yml index 9d054ff6df..3d0832f958 100644 --- a/.github/workflows/fossa.yml +++ b/.github/workflows/fossa.yml @@ -11,7 +11,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v2.3.4 + uses: actions/checkout@v2 - name: Install Fossa run: "curl -H 'Cache-Control: no-cache' https://raw.githubusercontent.com/fossas/fossa-cli/master/install.sh | bash" diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 96a2a20109..5cbf4e32ef 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -24,7 +24,7 @@ jobs: recent activity from the author. It will be closed if no further activity occurs. If you are the author and the PR has been closed, feel free to re-open the PR and continue the contribution! days-before-pr-stale: 7 - days-before-pr-close: 3 + days-before-pr-close: 5 exempt-pr-labels: reviewer-approved,awaiting-review stale-pr-label: stale operations-per-run: 100 diff --git a/docs/api/deprecations.md b/docs/api/deprecations.md index aca7e42d96..04c3da92bc 100644 --- a/docs/api/deprecations.md +++ b/docs/api/deprecations.md @@ -61,3 +61,125 @@ breaking change to make `theme` optional. This means that if you currently construct the themes that you pass on to `createApp` using `AppTheme` as an intermediate type, you will need to work around this in some way, for example by passing the themes to `createApp` more directly. + +### Generic Auth API Refs + +`Released 2021-12-16 in @backstage/core-plugin-api v0.3.1` + +There are four auth Utility API references in `@backstage/core-plugin-api` that +were too generic to be useful. The APIs in question are `auth0AuthApiRef`, +`oauth2ApiRef`, `oidcAuthApiRef`, and `samlAuthApiRef`. The issue with these +APIs was that they had no actual contract of what the backing auth provider was. +This made it more or less impossible to use these providers in open source +plugins in any meaningful way. We also did not want to keep these Utility API +references around just as helpers either, instead opting to remove them and let +integrators define their own APIs that are more specific to their auth provider. +This is also falls in line with a long-term goal to unify all auth providers to +not have separate frontend implementations. + +If you're currently using one of these API references for either Sign-In or +access delegation within an app, there are a couple of steps you need to take to +migrate to your own custom API. + +First, you'll need to define a new Utility API reference. If you're only using +the API for sign-in, you can put the definition in `packages/app/src/apis.ts`. +However, if you need to access your auth API inside plugins you you'll need to +export it from a common package. If you don't already have one we recommended +creating `@internal/apis` and from there export the API reference. + +```ts +// `ProfileInfoApi & BackstageIdentityApi & SessionApi` are required for sign-in +// Include `OAuthApi & OpenIdConnectApi` only if applicable +export const acmeAuthApiRef: ApiRef< + OAuthApi & + OpenIdConnectApi & + ProfileInfoApi & + BackstageIdentityApi & + SessionApi +> = createApiRef({ + id: 'internal.auth.acme', +}); +``` + +Next you'll want to wire up the API inside `packages/app/src/apis.ts`, which +varies depending on which API you're replacing. If you for example are replacing +the `oauth2ApiRef`, the factory might look like this: + +```ts +// oauth2 +createApiFactory({ + api: acmeAuthApiRef, + deps: { + discoveryApi: discoveryApiRef, + oauthRequestApi: oauthRequestApiRef, + configApi: configApiRef, + }, + factory: ({ discoveryApi, oauthRequestApi, configApi }) => + OAuth2.create({ + discoveryApi, + oauthRequestApi, + environment: configApi.getOptionalString('auth.environment'), + }), +}); +``` + +Provider specific factory implementations, copy the code you need into the +factory method depending on which apiRef you previously used. + +```ts +// samlAuthApiRef +SamlAuth.create({ + discoveryApi, + environment: configApi.getOptionalString('auth.environment'), +}); + +// oidcAuthApiRef +OAuth2.create({ + discoveryApi, + oauthRequestApi, + provider: { + id: 'oidc', + title: 'Your Identity Provider', + icon: () => null, + }, + environment: configApi.getOptionalString('auth.environment'), +}); + +// auth0AuthApiRef +OAuth2.create({ + discoveryApi, + oauthRequestApi, + provider: { + id: 'auth0', + title: 'Auth0', + icon: () => null, + }, + defaultScopes: ['openid', 'email', 'profile'], + environment: configApi.getOptionalString('auth.environment'), +}); +``` + +Finally, for the provider to show up in your settings menu, you also need to +update the settings route in `packages/app/src/App.tsx` to pass the +`acmeAuthApiRef` to the `UserSettingsPage`. This replaces all existing provider +items, so you might want to add back any of the default ones that you are using +from the +[DefaultProviderSettings](https://github.com/backstage/backstage/blob/a3ec122170e0205fd3f9c307b98b1c5e4f55bf5f/plugins/user-settings/src/components/AuthProviders/DefaultProviderSettings.tsx#L35). + +```tsx + + } + /> + } +/> +``` diff --git a/docs/features/search/search-engines.md b/docs/features/search/search-engines.md index 1331c73b72..8342c74096 100644 --- a/docs/features/search/search-engines.md +++ b/docs/features/search/search-engines.md @@ -90,6 +90,18 @@ within your instance. The configuration options are documented in the The underlying functionality is using official ElasticSearch client version 7.x, meaning that ElasticSearch version 7 is the only one confirmed to be supported. +Should you need to create your own bespoke search experiences that require more +than just a query translator (such as faceted search or Relay pagination), you +can access the configuration of the search engine in order to create new elastic +search clients. The version of the client need not be the same as one used +internally by the elastic search engine plugin. For example: + +```typescript +import { Client } from '@elastic/elastic-search'; + +const client = searchEngine.newClient(options => new Client(options)); +``` + ## Example configurations ### AWS diff --git a/package.json b/package.json index 2d63f92d3a..57cf603090 100644 --- a/package.json +++ b/package.json @@ -55,9 +55,9 @@ }, "version": "1.0.0", "dependencies": { - "@microsoft/api-documenter": "^7.13.68", - "@microsoft/api-extractor": "^7.18.7", - "@microsoft/api-extractor-model": "^7.13.5", + "@microsoft/api-documenter": "^7.13.77", + "@microsoft/api-extractor": "^7.19.2", + "@microsoft/api-extractor-model": "^7.15.1", "@microsoft/tsdoc": "^0.13.2" }, "devDependencies": { @@ -78,11 +78,12 @@ "prettier": "^2.2.1", "shx": "^0.3.2", "ts-node": "^10.4.0", + "typescript": "~4.3.5", "yarn-lock-check": "^1.0.5" }, "prettier": "@spotify/prettier-config", "lint-staged": { - "*.{js,jsx,ts,tsx}": [ + "*.{js,jsx,ts,tsx,mjs,cjs}": [ "eslint --fix", "prettier --write" ], diff --git a/packages/app/CHANGELOG.md b/packages/app/CHANGELOG.md index 3f3ac75853..d1055a54bf 100644 --- a/packages/app/CHANGELOG.md +++ b/packages/app/CHANGELOG.md @@ -1,5 +1,23 @@ # example-app +## 0.2.57 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-github-actions@0.4.27 + - @backstage/core-app-api@0.2.1 + - @backstage/plugin-kubernetes@0.5.1 + - @backstage/core-plugin-api@0.3.1 + - @backstage/core-components@0.8.1 + - @backstage/plugin-org@0.3.31 + - @backstage/plugin-azure-devops@0.1.7 + - @backstage/cli@0.10.2 + - @backstage/catalog-model@0.9.8 + - @backstage/plugin-techdocs@0.12.10 + - @backstage/plugin-catalog-react@0.6.7 + - @backstage/plugin-apache-airflow@0.1.0 + ## 0.2.56 ### Patch Changes diff --git a/packages/app/package.json b/packages/app/package.json index 99b90952e3..a9e8e8ddc8 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -1,39 +1,39 @@ { "name": "example-app", - "version": "0.2.56", + "version": "0.2.57", "private": true, "bundled": true, "dependencies": { "@backstage/app-defaults": "^0.1.2", - "@backstage/catalog-model": "^0.9.7", - "@backstage/cli": "^0.10.1", - "@backstage/core-app-api": "^0.2.0", - "@backstage/core-components": "^0.8.0", - "@backstage/core-plugin-api": "^0.3.0", + "@backstage/catalog-model": "^0.9.8", + "@backstage/cli": "^0.10.2", + "@backstage/core-app-api": "^0.2.1", + "@backstage/core-components": "^0.8.1", + "@backstage/core-plugin-api": "^0.3.1", "@backstage/integration-react": "^0.1.15", "@backstage/plugin-api-docs": "^0.6.18", - "@backstage/plugin-azure-devops": "^0.1.6", - "@backstage/plugin-apache-airflow": "^0.0.0", + "@backstage/plugin-azure-devops": "^0.1.7", + "@backstage/plugin-apache-airflow": "^0.1.0", "@backstage/plugin-badges": "^0.2.16", "@backstage/plugin-catalog": "^0.7.4", "@backstage/plugin-catalog-graph": "^0.2.3", "@backstage/plugin-catalog-import": "^0.7.5", - "@backstage/plugin-catalog-react": "^0.6.5", + "@backstage/plugin-catalog-react": "^0.6.7", "@backstage/plugin-circleci": "^0.2.31", "@backstage/plugin-cloudbuild": "^0.2.29", "@backstage/plugin-code-coverage": "^0.1.19", "@backstage/plugin-cost-insights": "^0.11.13", "@backstage/plugin-explore": "^0.3.22", "@backstage/plugin-gcp-projects": "^0.3.10", - "@backstage/plugin-github-actions": "^0.4.26", + "@backstage/plugin-github-actions": "^0.4.27", "@backstage/plugin-graphiql": "^0.2.24", "@backstage/plugin-home": "^0.4.7", "@backstage/plugin-jenkins": "^0.5.14", "@backstage/plugin-kafka": "^0.2.22", - "@backstage/plugin-kubernetes": "^0.5.0", + "@backstage/plugin-kubernetes": "^0.5.1", "@backstage/plugin-lighthouse": "^0.2.31", "@backstage/plugin-newrelic": "^0.3.10", - "@backstage/plugin-org": "^0.3.30", + "@backstage/plugin-org": "^0.3.31", "@backstage/plugin-pagerduty": "0.3.19", "@backstage/plugin-rollbar": "^0.3.20", "@backstage/plugin-scaffolder": "^0.11.14", @@ -41,7 +41,7 @@ "@backstage/plugin-sentry": "^0.3.30", "@backstage/plugin-shortcuts": "^0.1.15", "@backstage/plugin-tech-radar": "^0.4.13", - "@backstage/plugin-techdocs": "^0.12.9", + "@backstage/plugin-techdocs": "^0.12.10", "@backstage/plugin-todo": "^0.1.16", "@backstage/plugin-user-settings": "^0.3.13", "@backstage/search-common": "^0.2.0", diff --git a/packages/app/src/components/catalog/EntityPage.tsx b/packages/app/src/components/catalog/EntityPage.tsx index 9810f49386..a7de64e89a 100644 --- a/packages/app/src/components/catalog/EntityPage.tsx +++ b/packages/app/src/components/catalog/EntityPage.tsx @@ -126,6 +126,8 @@ import { } from '@roadiehq/backstage-plugin-travis-ci'; import React, { ReactNode, useMemo, useState } from 'react'; +const customEntityFilterKind = ['Component', 'API', 'System']; + const EntityLayoutWrapper = (props: { children?: ReactNode }) => { const [badgesDialogOpen, setBadgesDialogOpen] = useState(false); @@ -523,7 +525,10 @@ const userPage = ( - + @@ -539,7 +544,10 @@ const groupPage = ( - + diff --git a/packages/backend-common/CHANGELOG.md b/packages/backend-common/CHANGELOG.md index ad3fb33365..4bd21df721 100644 --- a/packages/backend-common/CHANGELOG.md +++ b/packages/backend-common/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/backend-common +## 0.9.14 + +### Patch Changes + +- fe24bc9a32: Each plugin now saves to a separate sqlite database file when `connection.filename` is provided in the sqlite config. + Any existing sqlite database files will be ignored. + ## 0.9.13 ### Patch Changes diff --git a/packages/backend-common/package.json b/packages/backend-common/package.json index 130a3fcaa5..91b8f46255 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.9.13", + "version": "0.9.14", "main": "src/index.ts", "types": "src/index.ts", "private": false, @@ -81,7 +81,7 @@ } }, "devDependencies": { - "@backstage/cli": "^0.10.1", + "@backstage/cli": "^0.10.2", "@backstage/test-utils": "^0.1.24", "@types/archiver": "^5.1.0", "@types/compression": "^1.7.0", @@ -94,7 +94,7 @@ "@types/recursive-readdir": "^2.2.0", "@types/stoppable": "^1.1.0", "@types/supertest": "^2.0.8", - "@types/tar": "^4.0.3", + "@types/tar": "^6.1.1", "@types/unzipper": "^0.10.3", "@types/webpack-env": "^1.15.2", "aws-sdk-mock": "^5.2.1", diff --git a/packages/backend-common/src/database/DatabaseManager.test.ts b/packages/backend-common/src/database/DatabaseManager.test.ts index bd2e7b9f71..a48b1584aa 100644 --- a/packages/backend-common/src/database/DatabaseManager.test.ts +++ b/packages/backend-common/src/database/DatabaseManager.test.ts @@ -15,6 +15,7 @@ */ import { ConfigReader } from '@backstage/config'; import { omit } from 'lodash'; +import path from 'path'; import { createDatabaseClient, ensureDatabaseExists, @@ -170,28 +171,6 @@ describe('DatabaseManager', () => { ); }); - it('uses top level sqlite database filename if plugin config is not present', async () => { - const testManager = DatabaseManager.fromConfig( - new ConfigReader({ - backend: { - database: { - client: 'sqlite3', - connection: 'some-file-path', - }, - }, - }), - ); - - await testManager.forPlugin('pluginwithoutconfig').getClient(); - const mockCalls = mocked(createDatabaseClient).mock.calls.splice(-1); - const [_, overrides] = mockCalls[0]; - - expect(overrides).toHaveProperty( - 'connection.filename', - expect.stringContaining('some-file-path'), - ); - }); - it('provides an inmemory sqlite database if top level is also inmemory and plugin config is not present', async () => { const testManager = DatabaseManager.fromConfig( new ConfigReader({ @@ -214,6 +193,110 @@ describe('DatabaseManager', () => { ); }); + it('throws if top level sqlite filename is provided', async () => { + const testManager = DatabaseManager.fromConfig( + new ConfigReader({ + backend: { + database: { + client: 'sqlite3', + connection: 'some-file-path', + }, + }, + }), + ); + + await expect( + testManager.forPlugin('pluginwithoutconfig').getClient(), + ).rejects.toBeInstanceOf(Error); + }); + + it('creates plugin-specific sqlite files when plugin config is not present', async () => { + const testManager = DatabaseManager.fromConfig( + new ConfigReader({ + backend: { + database: { + client: 'sqlite3', + connection: { + directory: 'sqlite-files', + }, + }, + }, + }), + ); + + await testManager.forPlugin('pluginwithoutconfig').getClient(); + const mockCalls = mocked(createDatabaseClient).mock.calls.splice(-1); + const [_, overrides] = mockCalls[0]; + + expect(overrides).toHaveProperty( + 'connection.filename', + path.join('sqlite-files', 'pluginwithoutconfig.sqlite'), + ); + }); + + it('uses sqlite directory from top level config and filename from plugin config', async () => { + const testManager = DatabaseManager.fromConfig( + new ConfigReader({ + backend: { + database: { + client: 'sqlite3', + connection: { + directory: 'sqlite-files', + }, + plugin: { + test: { + connection: { + filename: 'other.sqlite', + }, + }, + }, + }, + }, + }), + ); + + await testManager.forPlugin('test').getClient(); + const mockCalls = mocked(createDatabaseClient).mock.calls.splice(-1); + const [_, overrides] = mockCalls[0]; + + expect(overrides).toHaveProperty( + 'connection.filename', + path.join('sqlite-files', 'other.sqlite'), + ); + }); + + it('uses sqlite directory and filename from plugin config', async () => { + const testManager = DatabaseManager.fromConfig( + new ConfigReader({ + backend: { + database: { + client: 'sqlite3', + connection: { + directory: 'sqlite-files', + }, + plugin: { + test: { + connection: { + directory: 'custom-sqlite-files', + filename: 'other.sqlite', + }, + }, + }, + }, + }, + }), + ); + + await testManager.forPlugin('test').getClient(); + const mockCalls = mocked(createDatabaseClient).mock.calls.splice(-1); + const [_, overrides] = mockCalls[0]; + + expect(overrides).toHaveProperty( + 'connection.filename', + path.join('custom-sqlite-files', 'other.sqlite'), + ); + }); + it('connects to a plugin database using a specific database name', async () => { // testdbname.connection.database is set in config await manager.forPlugin('testdbname').getClient(); @@ -282,7 +365,10 @@ describe('DatabaseManager', () => { expect(baseConfig.get().client).toEqual('sqlite3'); // sqlite3 uses 'filename' instead of 'database' - expect(overrides).toHaveProperty('connection.filename'); + expect(overrides).toHaveProperty( + 'connection.filename', + 'plugin_with_different_client', + ); }); it('provides database client specific base from plugin connection string when client set under plugin', async () => { diff --git a/packages/backend-common/src/database/DatabaseManager.ts b/packages/backend-common/src/database/DatabaseManager.ts index 8e23a219ec..5959c60c83 100644 --- a/packages/backend-common/src/database/DatabaseManager.ts +++ b/packages/backend-common/src/database/DatabaseManager.ts @@ -28,6 +28,7 @@ import { normalizeConnection, } from './connection'; import { PluginDatabaseManager } from './types'; +import path from 'path'; /** * Provides a config lookup path for a plugin's config block. @@ -114,10 +115,18 @@ export class DatabaseManager { const connection = this.getConnectionConfig(pluginId); if (this.getClientType(pluginId).client === 'sqlite3') { - // sqlite database name should fallback to ':memory:' as a special case - return ( - (connection as Knex.Sqlite3ConnectionConfig)?.filename ?? ':memory:' - ); + const sqliteFilename: string | undefined = ( + connection as Knex.Sqlite3ConnectionConfig + ).filename; + + if (sqliteFilename === ':memory:') { + return sqliteFilename; + } + + const sqliteDirectory = + (connection as { directory?: string }).directory ?? '.'; + + return path.join(sqliteDirectory, sqliteFilename ?? `${pluginId}.sqlite`); } const databaseName = (connection as Knex.ConnectionConfig)?.database; @@ -205,6 +214,17 @@ export class DatabaseManager { this.config.get('connection'), this.config.getString('client'), ); + + if ( + client === 'sqlite3' && + 'filename' in baseConnection && + baseConnection.filename !== ':memory:' + ) { + throw new Error( + '`connection.filename` is not supported for the base sqlite connection. Prefer `connection.directory` or provide a filename for the plugin connection instead.', + ); + } + // Databases cannot be shared unless the `pluginDivisionMode` is set to `schema`. The // `database` property from the base connection is omitted unless `pluginDivisionMode` // is set to `schema`. SQLite3's `filename` property is an exception as this is used as a diff --git a/packages/backend-common/src/database/connectors/sqlite3.test.ts b/packages/backend-common/src/database/connectors/sqlite3.test.ts index b9da19d247..cfc9f61527 100644 --- a/packages/backend-common/src/database/connectors/sqlite3.test.ts +++ b/packages/backend-common/src/database/connectors/sqlite3.test.ts @@ -73,28 +73,6 @@ describe('sqlite3', () => { }); }); - it('builds a persistent connection per database', () => { - expect( - buildSqliteDatabaseConfig( - createConfig({ - filename: path.join('path', 'to', 'foo'), - }), - { - connection: { - database: 'my-database', - }, - }, - ), - ).toEqual({ - client: 'sqlite3', - connection: { - filename: path.join('path', 'to', 'foo', 'my-database.sqlite'), - database: 'my-database', - }, - useNullAsDefault: true, - }); - }); - it('replaces the connection with an override', () => { expect( buildSqliteDatabaseConfig(createConfig(':memory:'), { diff --git a/packages/backend-common/src/database/connectors/sqlite3.ts b/packages/backend-common/src/database/connectors/sqlite3.ts index 3dfecd7e6b..41f80ed293 100644 --- a/packages/backend-common/src/database/connectors/sqlite3.ts +++ b/packages/backend-common/src/database/connectors/sqlite3.ts @@ -86,19 +86,6 @@ export function buildSqliteDatabaseConfig( overrides, ); - // If we don't create an in-memory database, interpret the connection string - // as a directory that contains multiple sqlite files based on the database - // name. - const database = (config.connection as Knex.ConnectionConfig).database; - const sqliteConnection = config.connection as Knex.Sqlite3ConnectionConfig; - - if (database && sqliteConnection.filename !== ':memory:') { - sqliteConnection.filename = path.join( - sqliteConnection.filename, - `${database}.sqlite`, - ); - } - return config; } diff --git a/packages/backend/CHANGELOG.md b/packages/backend/CHANGELOG.md index fa8632e2ef..9c7b00b61f 100644 --- a/packages/backend/CHANGELOG.md +++ b/packages/backend/CHANGELOG.md @@ -1,5 +1,19 @@ # example-backend +## 0.2.57 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-backend-module-elasticsearch@0.0.7 + - @backstage/plugin-catalog-backend@0.19.2 + - @backstage/plugin-scaffolder-backend@0.15.17 + - @backstage/backend-common@0.9.14 + - @backstage/plugin-azure-devops-backend@0.2.5 + - @backstage/plugin-auth-backend@0.5.1 + - @backstage/catalog-model@0.9.8 + - example-app@0.2.57 + ## 0.2.56 ### Patch Changes diff --git a/packages/backend/package.json b/packages/backend/package.json index ae61bc7a45..80ae3b0998 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -1,6 +1,6 @@ { "name": "example-backend", - "version": "0.2.56", + "version": "0.2.57", "main": "dist/index.cjs.js", "types": "src/index.ts", "license": "Apache-2.0", @@ -24,16 +24,16 @@ "migrate:create": "knex migrate:make -x ts" }, "dependencies": { - "@backstage/backend-common": "^0.9.13", + "@backstage/backend-common": "^0.9.14", "@backstage/catalog-client": "^0.5.2", - "@backstage/catalog-model": "^0.9.7", + "@backstage/catalog-model": "^0.9.8", "@backstage/config": "^0.1.10", "@backstage/integration": "^0.6.10", "@backstage/plugin-app-backend": "^0.3.19", - "@backstage/plugin-auth-backend": "^0.5.0", - "@backstage/plugin-azure-devops-backend": "^0.2.4", + "@backstage/plugin-auth-backend": "^0.5.1", + "@backstage/plugin-azure-devops-backend": "^0.2.5", "@backstage/plugin-badges-backend": "^0.1.13", - "@backstage/plugin-catalog-backend": "^0.19.1", + "@backstage/plugin-catalog-backend": "^0.19.2", "@backstage/plugin-code-coverage-backend": "^0.1.16", "@backstage/plugin-graphql-backend": "^0.1.9", "@backstage/plugin-jenkins-backend": "^0.1.9", @@ -41,11 +41,11 @@ "@backstage/plugin-kafka-backend": "^0.2.12", "@backstage/plugin-proxy-backend": "^0.2.14", "@backstage/plugin-rollbar-backend": "^0.1.16", - "@backstage/plugin-scaffolder-backend": "^0.15.16", + "@backstage/plugin-scaffolder-backend": "^0.15.17", "@backstage/plugin-scaffolder-backend-module-rails": "^0.2.0", "@backstage/plugin-search-backend": "^0.2.8", "@backstage/plugin-search-backend-node": "^0.4.2", - "@backstage/plugin-search-backend-module-elasticsearch": "^0.0.6", + "@backstage/plugin-search-backend-module-elasticsearch": "^0.0.7", "@backstage/plugin-search-backend-module-pg": "^0.2.2", "@backstage/plugin-techdocs-backend": "^0.12.0", "@backstage/plugin-tech-insights-backend": "^0.1.3", @@ -56,7 +56,7 @@ "@octokit/rest": "^18.5.3", "azure-devops-node-api": "^11.0.1", "dockerode": "^3.3.1", - "example-app": "^0.2.56", + "example-app": "^0.2.57", "express": "^4.17.1", "express-promise-router": "^4.1.0", "express-prom-bundle": "^6.3.6", @@ -68,7 +68,7 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/cli": "^0.10.1", + "@backstage/cli": "^0.10.2", "@types/dockerode": "^3.3.0", "@types/express": "^4.17.6", "@types/express-serve-static-core": "^4.17.5" diff --git a/packages/catalog-model/CHANGELOG.md b/packages/catalog-model/CHANGELOG.md index efd6513cdd..8c0a65c20a 100644 --- a/packages/catalog-model/CHANGELOG.md +++ b/packages/catalog-model/CHANGELOG.md @@ -1,5 +1,11 @@ # @backstage/catalog-model +## 0.9.8 + +### Patch Changes + +- ad7338bb48: Added an optional `presence` field to Location spec, which describes whether the target of a location is required to exist or not. It defaults to `'required'`, which is the current behaviour of the catalog. + ## 0.9.7 ### Patch Changes diff --git a/packages/catalog-model/api-report.md b/packages/catalog-model/api-report.md index 52f36493b5..9c0c33fa0e 100644 --- a/packages/catalog-model/api-report.md +++ b/packages/catalog-model/api-report.md @@ -325,6 +325,7 @@ interface LocationEntityV1alpha1 extends Entity { type?: string; target?: string; targets?: string[]; + presence?: 'required' | 'optional'; }; } export { LocationEntityV1alpha1 as LocationEntity }; diff --git a/packages/catalog-model/package.json b/packages/catalog-model/package.json index 60331d9dc8..6fdc89bdfb 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": "0.9.7", + "version": "0.9.8", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -42,7 +42,7 @@ "yup": "^0.32.9" }, "devDependencies": { - "@backstage/cli": "^0.10.0", + "@backstage/cli": "^0.10.2", "@types/jest": "^26.0.7", "@types/lodash": "^4.14.151", "yaml": "^1.9.2" diff --git a/packages/catalog-model/src/kinds/LocationEntityV1alpha1.test.ts b/packages/catalog-model/src/kinds/LocationEntityV1alpha1.test.ts index 7efec38395..6e78c1b96e 100644 --- a/packages/catalog-model/src/kinds/LocationEntityV1alpha1.test.ts +++ b/packages/catalog-model/src/kinds/LocationEntityV1alpha1.test.ts @@ -102,4 +102,23 @@ describe('LocationV1alpha1Validator', () => { (entity as any).spec.targets = 7; await expect(validator.check(entity)).rejects.toThrow(/targets/); }); + + it('accepts good presence', async () => { + (entity as any).spec.presence = 'required'; + await expect(validator.check(entity)).resolves.toBe(true); + (entity as any).spec.presence = 'optional'; + await expect(validator.check(entity)).resolves.toBe(true); + }); + + it('rejects empty presence', async () => { + (entity as any).spec.presence = ''; + await expect(validator.check(entity)).rejects.toThrow(/presence/); + }); + + it('rejects wrong presence', async () => { + (entity as any).spec.presence = 7; + await expect(validator.check(entity)).rejects.toThrow(/presence/); + (entity as any).spec.presence = 'nope'; + await expect(validator.check(entity)).rejects.toThrow(/presence/); + }); }); diff --git a/packages/catalog-model/src/kinds/LocationEntityV1alpha1.ts b/packages/catalog-model/src/kinds/LocationEntityV1alpha1.ts index 1872e43526..93a218aa2d 100644 --- a/packages/catalog-model/src/kinds/LocationEntityV1alpha1.ts +++ b/packages/catalog-model/src/kinds/LocationEntityV1alpha1.ts @@ -30,6 +30,7 @@ export interface LocationEntityV1alpha1 extends Entity { type?: string; target?: string; targets?: string[]; + presence?: 'required' | 'optional'; }; } diff --git a/packages/catalog-model/src/schema/kinds/Location.v1alpha1.schema.json b/packages/catalog-model/src/schema/kinds/Location.v1alpha1.schema.json index d633d30229..b70df9e70e 100644 --- a/packages/catalog-model/src/schema/kinds/Location.v1alpha1.schema.json +++ b/packages/catalog-model/src/schema/kinds/Location.v1alpha1.schema.json @@ -59,6 +59,13 @@ ], "minLength": 1 } + }, + "presence": { + "type": "string", + "description": "Whether the presence of the location target is required and it should be considered an error if it can not be found", + "default": "required", + "examples": ["required"], + "enum": ["required", "optional"] } } } diff --git a/packages/cli/.snyk b/packages/cli/.snyk index 612b4b0525..3ad43bd3de 100644 --- a/packages/cli/.snyk +++ b/packages/cli/.snyk @@ -67,4 +67,16 @@ ignore: reason: Prototype pollution is not an effective attack against a CLI as it already executes arbitrary code expires: 2022-03-06T17:18:55.019Z created: 2021-09-06T17:18:55.027Z + + 'snyk:lic:npm:rollup-plugin-dts:LGPL-3.0': + - '*': + reason: Backstage itself does not redistribute this dependency in minified form + expires: 2031-09-06T17:18:55.027Z + created: 2021-09-06T17:18:55.027Z + + 'snyk:lic:npm:axe-core:MPL-2.0': + - '*': + reason: Backstage itself does not redistribute this dependency in minified form + expires: 2031-09-06T17:18:55.027Z + created: 2021-09-06T17:18:55.027Z patch: {} diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index 0c30e8b251..5d51ea890b 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,11 @@ # @backstage/cli +## 0.10.2 + +### Patch Changes + +- 25dfc2d483: Add support for `.cjs` and `.mjs` extensions in local and dependency modules. + ## 0.10.1 ### Patch Changes diff --git a/packages/cli/config/jest.js b/packages/cli/config/jest.js index 1d33372c60..978c598584 100644 --- a/packages/cli/config/jest.js +++ b/packages/cli/config/jest.js @@ -96,20 +96,22 @@ async function getProjectConfig(targetPath, displayName) { rootDir: path.resolve(targetPath, 'src'), coverageDirectory: path.resolve(targetPath, 'coverage'), coverageProvider: 'v8', - collectCoverageFrom: ['**/*.{js,jsx,ts,tsx}', '!**/*.d.ts'], + collectCoverageFrom: ['**/*.{js,jsx,ts,tsx,mjs,cjs}', '!**/*.d.ts'], moduleNameMapper: { '\\.(css|less|scss|sss|styl)$': require.resolve('jest-css-modules'), }, transform: { - '\\.(js|jsx|ts|tsx)$': require.resolve('./jestSucraseTransform.js'), + '\\.(js|jsx|ts|tsx|mjs|cjs)$': require.resolve( + './jestSucraseTransform.js', + ), '\\.(bmp|gif|jpg|jpeg|png|frag|xml|svg|eot|woff|woff2|ttf)$': require.resolve('./jestFileTransform.js'), '\\.(yaml)$': require.resolve('jest-transform-yaml'), }, // A bit more opinionated - testMatch: ['**/?(*.)test.{js,jsx,mjs,ts,tsx}'], + testMatch: ['**/?(*.)test.{js,jsx,ts,tsx,mjs,cjs}'], transformIgnorePatterns: [`/node_modules/(?:${transformIgnorePattern})/`], }; diff --git a/packages/cli/package.json b/packages/cli/package.json index 984c1c70d1..4b59d46827 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.10.1", + "version": "0.10.2", "private": false, "publishConfig": { "access": "public" @@ -116,11 +116,11 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/backend-common": "^0.9.13", + "@backstage/backend-common": "^0.9.14", "@backstage/config": "^0.1.11", - "@backstage/core-components": "^0.8.0", - "@backstage/core-plugin-api": "^0.3.0", - "@backstage/core-app-api": "^0.2.0", + "@backstage/core-components": "^0.8.1", + "@backstage/core-plugin-api": "^0.3.1", + "@backstage/core-app-api": "^0.2.1", "@backstage/dev-utils": "^0.2.14", "@backstage/test-utils": "^0.1.24", "@backstage/theme": "^0.2.14", @@ -128,13 +128,13 @@ "@types/express": "^4.17.6", "@types/fs-extra": "^9.0.1", "@types/http-proxy": "^1.17.4", - "@types/inquirer": "^7.3.1", + "@types/inquirer": "^8.1.3", "@types/mock-fs": "^4.13.0", "@types/node": "^14.14.32", "@types/recursive-readdir": "^2.2.0", "@types/rollup-plugin-peer-deps-external": "^2.2.0", "@types/rollup-plugin-postcss": "^2.0.0", - "@types/tar": "^4.0.3", + "@types/tar": "^6.1.1", "@types/terser-webpack-plugin": "^5.0.4", "@types/webpack": "^5.28.0", "@types/webpack-dev-server": "^3.11.5", diff --git a/packages/cli/src/commands/lint.ts b/packages/cli/src/commands/lint.ts index 41b454ffff..5c3e0a88e3 100644 --- a/packages/cli/src/commands/lint.ts +++ b/packages/cli/src/commands/lint.ts @@ -20,7 +20,7 @@ import { paths } from '../lib/paths'; export default async (cmd: Command, cmdArgs: string[]) => { const args = [ - '--ext=js,jsx,ts,tsx', + '--ext=js,jsx,ts,tsx,mjs,cjs', '--max-warnings=0', `--format=${cmd.format}`, ...(cmdArgs ?? [paths.targetDir]), diff --git a/packages/cli/src/lib/bundler/transforms.ts b/packages/cli/src/lib/bundler/transforms.ts index 863728b8f8..d1d26f383d 100644 --- a/packages/cli/src/lib/bundler/transforms.ts +++ b/packages/cli/src/lib/bundler/transforms.ts @@ -61,7 +61,7 @@ export const transforms = (options: TransformOptions): Transforms => { }, }, { - test: /\.(jsx?|mjs)$/, + test: /\.(jsx?|mjs|cjs)$/, exclude: /node_modules/, loader: require.resolve('@sucrase/webpack-loader'), options: { @@ -71,7 +71,7 @@ export const transforms = (options: TransformOptions): Transforms => { }, }, { - test: /\.m?js/, + test: /\.(js|mjs|cjs)/, resolve: { fullySpecified: false, }, diff --git a/packages/codemods/CHANGELOG.md b/packages/codemods/CHANGELOG.md index d7e402270a..803579a963 100644 --- a/packages/codemods/CHANGELOG.md +++ b/packages/codemods/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/codemods +## 0.1.26 + +### Patch Changes + +- Updated dependencies + - @backstage/core-app-api@0.2.1 + - @backstage/core-plugin-api@0.3.1 + - @backstage/core-components@0.8.1 + ## 0.1.25 ### Patch Changes diff --git a/packages/codemods/package.json b/packages/codemods/package.json index 32bf6be518..8261e30547 100644 --- a/packages/codemods/package.json +++ b/packages/codemods/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/codemods", "description": "A collection of codemods for Backstage projects", - "version": "0.1.25", + "version": "0.1.26", "private": false, "publishConfig": { "access": "public", diff --git a/packages/core-app-api/CHANGELOG.md b/packages/core-app-api/CHANGELOG.md index ff15f45898..66e0c7d6ed 100644 --- a/packages/core-app-api/CHANGELOG.md +++ b/packages/core-app-api/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/core-app-api +## 0.2.1 + +### Patch Changes + +- c11ce4f552: Deprecated `Auth0Auth`, pointing to using `OAuth2` directly instead. +- 9d6503e86c: Switched out usage of deprecated `OAuthRequestApi` types from `@backstage/core-plugin-api`. +- Updated dependencies + - @backstage/core-plugin-api@0.3.1 + - @backstage/core-components@0.8.1 + ## 0.2.0 ### Minor Changes diff --git a/packages/core-app-api/api-report.md b/packages/core-app-api/api-report.md index 51fd6bd06f..8a3f0edf14 100644 --- a/packages/core-app-api/api-report.md +++ b/packages/core-app-api/api-report.md @@ -17,9 +17,7 @@ import { AppTheme } from '@backstage/core-plugin-api'; import { AppThemeApi } from '@backstage/core-plugin-api'; import { atlassianAuthApiRef } from '@backstage/core-plugin-api'; import { auth0AuthApiRef } from '@backstage/core-plugin-api'; -import { AuthProvider } from '@backstage/core-plugin-api'; -import { AuthRequester } from '@backstage/core-plugin-api'; -import { AuthRequesterOptions } from '@backstage/core-plugin-api'; +import { AuthProviderInfo } from '@backstage/core-plugin-api'; import { AuthRequestOptions } from '@backstage/core-plugin-api'; import { BackstageIdentity } from '@backstage/core-plugin-api'; import { BackstageIdentityApi } from '@backstage/core-plugin-api'; @@ -43,11 +41,13 @@ import { IdentityApi } from '@backstage/core-plugin-api'; import { microsoftAuthApiRef } from '@backstage/core-plugin-api'; import { OAuthApi } from '@backstage/core-plugin-api'; import { OAuthRequestApi } from '@backstage/core-plugin-api'; +import { OAuthRequester } from '@backstage/core-plugin-api'; +import { OAuthRequesterOptions } from '@backstage/core-plugin-api'; import { Observable } from '@backstage/types'; import { oktaAuthApiRef } from '@backstage/core-plugin-api'; import { oneloginAuthApiRef } from '@backstage/core-plugin-api'; import { OpenIdConnectApi } from '@backstage/core-plugin-api'; -import { PendingAuthRequest } from '@backstage/core-plugin-api'; +import { PendingOAuthRequest } from '@backstage/core-plugin-api'; import { PluginOutput } from '@backstage/core-plugin-api'; import { ProfileInfo } from '@backstage/core-plugin-api'; import { ProfileInfoApi } from '@backstage/core-plugin-api'; @@ -254,7 +254,7 @@ export class AtlassianAuth { static create(options: OAuthApiCreateOptions): typeof atlassianAuthApiRef.T; } -// @public +// @public @deprecated export class Auth0Auth { // (undocumented) static create(options: OAuthApiCreateOptions): typeof auth0AuthApiRef.T; @@ -264,9 +264,7 @@ export class Auth0Auth { export type AuthApiCreateOptions = { discoveryApi: DiscoveryApi; environment?: string; - provider?: AuthProvider & { - id: string; - }; + provider?: AuthProviderInfo; }; // @public @@ -515,9 +513,9 @@ export type OAuthApiCreateOptions = AuthApiCreateOptions & { // @public export class OAuthRequestManager implements OAuthRequestApi { // (undocumented) - authRequest$(): Observable; + authRequest$(): Observable; // (undocumented) - createAuthRequester(options: AuthRequesterOptions): AuthRequester; + createAuthRequester(options: OAuthRequesterOptions): OAuthRequester; } // @public @@ -539,9 +537,7 @@ export type OneLoginAuthCreateOptions = { discoveryApi: DiscoveryApi; oauthRequestApi: OAuthRequestApi; environment?: string; - provider?: AuthProvider & { - id: string; - }; + provider?: AuthProviderInfo; }; // @public diff --git a/packages/core-app-api/package.json b/packages/core-app-api/package.json index 23079d7ba0..166f39a484 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": "0.2.0", + "version": "0.2.1", "private": false, "publishConfig": { "access": "public", @@ -30,9 +30,9 @@ }, "dependencies": { "@backstage/app-defaults": "^0.1.2", - "@backstage/core-components": "^0.8.0", + "@backstage/core-components": "^0.8.1", "@backstage/config": "^0.1.11", - "@backstage/core-plugin-api": "^0.3.0", + "@backstage/core-plugin-api": "^0.3.1", "@backstage/theme": "^0.2.14", "@backstage/types": "^0.1.1", "@backstage/version-bridge": "^0.1.1", @@ -49,7 +49,7 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.10.1", + "@backstage/cli": "^0.10.2", "@backstage/test-utils": "^0.1.24", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/packages/core-app-api/src/apis/implementations/OAuthRequestApi/MockOAuthApi.ts b/packages/core-app-api/src/apis/implementations/OAuthRequestApi/MockOAuthApi.ts index 4a0a07ef34..193c474361 100644 --- a/packages/core-app-api/src/apis/implementations/OAuthRequestApi/MockOAuthApi.ts +++ b/packages/core-app-api/src/apis/implementations/OAuthRequestApi/MockOAuthApi.ts @@ -16,14 +16,14 @@ import { OAuthRequestApi, - AuthRequesterOptions, + OAuthRequesterOptions, } from '@backstage/core-plugin-api'; import { OAuthRequestManager } from './OAuthRequestManager'; export default class MockOAuthApi implements OAuthRequestApi { private readonly real = new OAuthRequestManager(); - createAuthRequester(options: AuthRequesterOptions) { + createAuthRequester(options: OAuthRequesterOptions) { return this.real.createAuthRequester(options); } diff --git a/packages/core-app-api/src/apis/implementations/OAuthRequestApi/OAuthRequestManager.ts b/packages/core-app-api/src/apis/implementations/OAuthRequestApi/OAuthRequestManager.ts index 7e7f3c2507..f9d9270b2a 100644 --- a/packages/core-app-api/src/apis/implementations/OAuthRequestApi/OAuthRequestManager.ts +++ b/packages/core-app-api/src/apis/implementations/OAuthRequestApi/OAuthRequestManager.ts @@ -16,9 +16,9 @@ import { OAuthRequestApi, - PendingAuthRequest, - AuthRequester, - AuthRequesterOptions, + PendingOAuthRequest, + OAuthRequester, + OAuthRequesterOptions, } from '@backstage/core-plugin-api'; import { Observable } from '@backstage/types'; import { OAuthPendingRequests, PendingRequest } from './OAuthPendingRequests'; @@ -34,11 +34,17 @@ import { BehaviorSubject } from '../../../lib/subjects'; * @public */ export class OAuthRequestManager implements OAuthRequestApi { - private readonly subject = new BehaviorSubject([]); - private currentRequests: PendingAuthRequest[] = []; + private readonly subject = new BehaviorSubject([]); + private currentRequests: PendingOAuthRequest[] = []; private handlerCount = 0; - createAuthRequester(options: AuthRequesterOptions): AuthRequester { + createAuthRequester(options: OAuthRequesterOptions): OAuthRequester { + if (!options.provider.id) { + // eslint-disable-next-line no-console + console.warn( + 'DEPRECATION WARNING: Not passing a provider id to createAuthRequester is deprecated, it will be required in the future', + ); + } const handler = new OAuthPendingRequests(); const index = this.handlerCount; @@ -67,8 +73,8 @@ export class OAuthRequestManager implements OAuthRequestApi { // Converts the pending request and popup options into a popup request that we can forward to subscribers. private makeAuthRequest( request: PendingRequest, - options: AuthRequesterOptions, - ): PendingAuthRequest | undefined { + options: OAuthRequesterOptions, + ): PendingOAuthRequest | undefined { const { scopes } = request; if (!scopes) { return undefined; @@ -88,7 +94,7 @@ export class OAuthRequestManager implements OAuthRequestApi { }; } - authRequest$(): Observable { + authRequest$(): Observable { return this.subject; } } diff --git a/packages/core-app-api/src/apis/implementations/auth/auth0/Auth0Auth.ts b/packages/core-app-api/src/apis/implementations/auth/auth0/Auth0Auth.ts index d0a9dc5d99..d8f942b94b 100644 --- a/packages/core-app-api/src/apis/implementations/auth/auth0/Auth0Auth.ts +++ b/packages/core-app-api/src/apis/implementations/auth/auth0/Auth0Auth.ts @@ -28,6 +28,23 @@ const DEFAULT_PROVIDER = { * Implements the OAuth flow to Auth0 products. * * @public + * @deprecated Use {@link OAuth2} instead + * + * @example + * + * ```ts + * OAuth2.create({ + * discoveryApi, + * oauthRequestApi, + * provider: { + * id: 'auth0', + * title: 'Auth0', + * icon: () => null, + * }, + * defaultScopes: ['openid', 'email', 'profile'], + * environment: configApi.getOptionalString('auth.environment'), + * }) + * ``` */ export default class Auth0Auth { static create(options: OAuthApiCreateOptions): typeof auth0AuthApiRef.T { diff --git a/packages/core-app-api/src/apis/implementations/auth/onelogin/OneLoginAuth.ts b/packages/core-app-api/src/apis/implementations/auth/onelogin/OneLoginAuth.ts index 9493d0809e..ea9e2cad28 100644 --- a/packages/core-app-api/src/apis/implementations/auth/onelogin/OneLoginAuth.ts +++ b/packages/core-app-api/src/apis/implementations/auth/onelogin/OneLoginAuth.ts @@ -17,7 +17,7 @@ import { oneloginAuthApiRef, OAuthRequestApi, - AuthProvider, + AuthProviderInfo, DiscoveryApi, } from '@backstage/core-plugin-api'; import { OAuth2 } from '../oauth2'; @@ -30,7 +30,7 @@ export type OneLoginAuthCreateOptions = { discoveryApi: DiscoveryApi; oauthRequestApi: OAuthRequestApi; environment?: string; - provider?: AuthProvider & { id: string }; + provider?: AuthProviderInfo; }; const DEFAULT_PROVIDER = { diff --git a/packages/core-app-api/src/apis/implementations/auth/types.ts b/packages/core-app-api/src/apis/implementations/auth/types.ts index 825f433cec..55d6c19098 100644 --- a/packages/core-app-api/src/apis/implementations/auth/types.ts +++ b/packages/core-app-api/src/apis/implementations/auth/types.ts @@ -15,7 +15,7 @@ */ import { - AuthProvider, + AuthProviderInfo, DiscoveryApi, OAuthRequestApi, } from '@backstage/core-plugin-api'; @@ -36,5 +36,5 @@ export type OAuthApiCreateOptions = AuthApiCreateOptions & { export type AuthApiCreateOptions = { discoveryApi: DiscoveryApi; environment?: string; - provider?: AuthProvider & { id: string }; + provider?: AuthProviderInfo; }; diff --git a/packages/core-app-api/src/lib/AuthConnector/DefaultAuthConnector.ts b/packages/core-app-api/src/lib/AuthConnector/DefaultAuthConnector.ts index 261a008d4b..a467957063 100644 --- a/packages/core-app-api/src/lib/AuthConnector/DefaultAuthConnector.ts +++ b/packages/core-app-api/src/lib/AuthConnector/DefaultAuthConnector.ts @@ -15,9 +15,9 @@ */ import { - AuthRequester, + OAuthRequester, OAuthRequestApi, - AuthProvider, + AuthProviderInfo, DiscoveryApi, } from '@backstage/core-plugin-api'; import { showLoginPopup } from '../loginPopup'; @@ -36,7 +36,7 @@ type Options = { * Information about the auth provider to be shown to the user. * The ID Must match the backend auth plugin configuration, for example 'google'. */ - provider: AuthProvider & { id: string }; + provider: AuthProviderInfo; /** * API used to instantiate an auth requester. */ @@ -65,9 +65,9 @@ export class DefaultAuthConnector { private readonly discoveryApi: DiscoveryApi; private readonly environment: string; - private readonly provider: AuthProvider & { id: string }; + private readonly provider: AuthProviderInfo; private readonly joinScopesFunc: (scopes: Set) => string; - private readonly authRequester: AuthRequester; + private readonly authRequester: OAuthRequester; private readonly sessionTransform: (response: any) => Promise; constructor(options: Options) { diff --git a/packages/core-app-api/src/lib/AuthConnector/DirectAuthConnector.ts b/packages/core-app-api/src/lib/AuthConnector/DirectAuthConnector.ts index 61fdd825a2..200ba755ac 100644 --- a/packages/core-app-api/src/lib/AuthConnector/DirectAuthConnector.ts +++ b/packages/core-app-api/src/lib/AuthConnector/DirectAuthConnector.ts @@ -13,18 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { AuthProvider, DiscoveryApi } from '@backstage/core-plugin-api'; +import { AuthProviderInfo, DiscoveryApi } from '@backstage/core-plugin-api'; import { showLoginPopup } from '../loginPopup'; type Options = { discoveryApi: DiscoveryApi; environment?: string; - provider: AuthProvider & { id: string }; + provider: AuthProviderInfo; }; export class DirectAuthConnector { private readonly discoveryApi: DiscoveryApi; private readonly environment: string | undefined; - private readonly provider: AuthProvider & { id: string }; + private readonly provider: AuthProviderInfo; constructor(options: Options) { const { discoveryApi, environment, provider } = options; diff --git a/packages/core-components/CHANGELOG.md b/packages/core-components/CHANGELOG.md index 4eab9cea54..2171a72ccb 100644 --- a/packages/core-components/CHANGELOG.md +++ b/packages/core-components/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/core-components +## 0.8.1 + +### Patch Changes + +- 2c17e5b073: Items in `` are now only active when their full path is active (including search parameters). +- 9d6503e86c: Switched out usage of deprecated `OAuthRequestApi` types from `@backstage/core-plugin-api`. +- 1680a1c5ac: Add Missing Override Components Type for SidebarSpace, SidebarSpacer, and SidebarDivider Components. +- Updated dependencies + - @backstage/core-plugin-api@0.3.1 + ## 0.8.0 ### Minor Changes diff --git a/packages/core-components/api-report.md b/packages/core-components/api-report.md index c31cd7ae88..1213a92943 100644 --- a/packages/core-components/api-report.md +++ b/packages/core-components/api-report.md @@ -1166,6 +1166,11 @@ export const SidebarDivider: React_2.ComponentType< } >; +// Warning: (ae-missing-release-tag) "SidebarDividerClassKey" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type SidebarDividerClassKey = 'root'; + // @public export const SidebarExpandButton: () => JSX.Element | null; @@ -1795,6 +1800,11 @@ export const SidebarSpace: React_2.ComponentType< } >; +// Warning: (ae-missing-release-tag) "SidebarSpaceClassKey" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type SidebarSpaceClassKey = 'root'; + // Warning: (ae-missing-release-tag) "SidebarSpacer" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -2064,6 +2074,11 @@ export const SidebarSpacer: React_2.ComponentType< } >; +// Warning: (ae-missing-release-tag) "SidebarSpacerClassKey" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type SidebarSpacerClassKey = 'root'; + // @public export const SidebarSubmenu: (props: SidebarSubmenuProps) => JSX.Element; diff --git a/packages/core-components/package.json b/packages/core-components/package.json index f47335fce8..b47a221227 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.8.0", + "version": "0.8.1", "private": false, "publishConfig": { "access": "public", @@ -30,7 +30,7 @@ }, "dependencies": { "@backstage/config": "^0.1.11", - "@backstage/core-plugin-api": "^0.3.0", + "@backstage/core-plugin-api": "^0.3.1", "@backstage/errors": "^0.1.5", "@backstage/theme": "^0.2.14", "@material-table/core": "^3.1.0", @@ -72,8 +72,8 @@ "react-dom": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/core-app-api": "^0.2.0", - "@backstage/cli": "^0.10.1", + "@backstage/core-app-api": "^0.2.1", + "@backstage/cli": "^0.10.2", "@backstage/test-utils": "^0.1.24", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/packages/core-components/src/components/OAuthRequestDialog/LoginRequestListItem.tsx b/packages/core-components/src/components/OAuthRequestDialog/LoginRequestListItem.tsx index ef0d2373e4..0c6bd62bcb 100644 --- a/packages/core-components/src/components/OAuthRequestDialog/LoginRequestListItem.tsx +++ b/packages/core-components/src/components/OAuthRequestDialog/LoginRequestListItem.tsx @@ -22,7 +22,7 @@ import Typography from '@material-ui/core/Typography'; import Button from '@material-ui/core/Button'; import React, { useState } from 'react'; import { isError } from '@backstage/errors'; -import { PendingAuthRequest } from '@backstage/core-plugin-api'; +import { PendingOAuthRequest } from '@backstage/core-plugin-api'; export type LoginRequestListItemClassKey = 'root'; @@ -36,7 +36,7 @@ const useItemStyles = makeStyles( ); type RowProps = { - request: PendingAuthRequest; + request: PendingOAuthRequest; busy: boolean; setBusy: (busy: boolean) => void; }; diff --git a/packages/core-components/src/layout/Sidebar/Items.tsx b/packages/core-components/src/layout/Sidebar/Items.tsx index 399fff999e..0aed75460e 100644 --- a/packages/core-components/src/layout/Sidebar/Items.tsx +++ b/packages/core-components/src/layout/Sidebar/Items.tsx @@ -24,6 +24,8 @@ import Typography from '@material-ui/core/Typography'; import { CreateCSSProperties } from '@material-ui/core/styles/withStyles'; import ArrowRightIcon from '@material-ui/icons/ArrowRight'; import SearchIcon from '@material-ui/icons/Search'; +import ArrowDropUp from '@material-ui/icons/ArrowDropUp'; +import ArrowDropDown from '@material-ui/icons/ArrowDropDown'; import classnames from 'classnames'; import React, { forwardRef, @@ -43,10 +45,13 @@ import { SidebarContext, SidebarItemWithSubmenuContext, } from './config'; -import { SidebarSubmenu } from './SidebarSubmenu'; -import ArrowDropUp from '@material-ui/icons/ArrowDropUp'; -import ArrowDropDown from '@material-ui/icons/ArrowDropDown'; -import { SidebarSubmenuItemProps, SidebarSubmenuProps } from '.'; +import { + SidebarSubmenuItemProps, + SidebarSubmenuProps, + SidebarSubmenu, +} from '.'; +import { isLocationMatch } from './utils'; +import { Location } from 'history'; export type SidebarItemClassKey = | 'root' @@ -226,7 +231,7 @@ const useLocationMatch = ( useElementFilter( submenu.props.children, elements => { - let isLocationMatch = false; + let active = false; elements .getElements() .forEach( @@ -235,25 +240,97 @@ const useLocationMatch = ( }: { props: Partial; }) => { - if (!isLocationMatch) { + if (!active) { if (dropdownItems?.length) { dropdownItems.forEach( ({ to: _to }) => - (isLocationMatch = - isLocationMatch || locationPathname.includes(_to)), + (active = active || locationPathname.includes(_to)), ); return; } if (to) { - isLocationMatch = locationPathname.includes(to); + active = locationPathname.includes(to); } } }, ); - return isLocationMatch; + return active; }, [locationPathname], ); +/* +function isSidebarItemWithSubmenuActive( + submenu: ReactNode, + currentLocation: Location, +) { + // Item is active if any of submenu items have active paths + const toPathnames: string[] = []; + let isActive = false; + let submenuItems: ReactNode; + Children.forEach(submenu, element => { + if (!React.isValidElement(element)) return; + submenuItems = element.props.children; + }); + Children.forEach(submenuItems, element => { + if (!React.isValidElement(element)) return; + if (element.props.dropdownItems) { + element.props.dropdownItems.map((item: { to: string }) => + toPathnames.push(item.to), + ); + } else if (element.props.to) { + toPathnames.push(element.props.to); + } + }); + isActive = toPathnames.some(to => { + const toLocation = resolvePath(to); + return isLocationMatch(currentLocation, toLocation); + }); + return isActive; +} + +const SidebarItemWithSubmenu = ({ + text, + hasNotifications = false, + icon: Icon, + children, +}: PropsWithChildren) => { + const classes = useStyles(); + const [isHoveredOn, setIsHoveredOn] = useState(false); + const currentLocation = useLocation(); + const isActive = isSidebarItemWithSubmenuActive(children, currentLocation); + + const handleMouseEnter = () => { + setIsHoveredOn(true); + }; + const handleMouseLeave = () => { + setIsHoveredOn(false); + }; + + const { isOpen } = useContext(SidebarContext); + const itemIcon = ( + + + + ); + const openContent = ( + <> +
+ {itemIcon} +
+ {text && ( + + {text} + + )} +
{}
+ + );*/ type SidebarItemBaseProps = { icon: IconComponent; @@ -570,6 +647,8 @@ export function SidebarSearchField(props: SidebarSearchFieldProps) { ); } +export type SidebarSpaceClassKey = 'root'; + export const SidebarSpace = styled('div')( { flex: 1, @@ -577,6 +656,8 @@ export const SidebarSpace = styled('div')( { name: 'BackstageSidebarSpace' }, ); +export type SidebarSpacerClassKey = 'root'; + export const SidebarSpacer = styled('div')( { height: 8, @@ -584,6 +665,8 @@ export const SidebarSpacer = styled('div')( { name: 'BackstageSidebarSpacer' }, ); +export type SidebarDividerClassKey = 'root'; + export const SidebarDivider = styled('hr')( { height: 1, diff --git a/packages/core-components/src/layout/Sidebar/Sidebar.stories.tsx b/packages/core-components/src/layout/Sidebar/Sidebar.stories.tsx index 67da1a7704..53e8e88a1d 100644 --- a/packages/core-components/src/layout/Sidebar/Sidebar.stories.tsx +++ b/packages/core-components/src/layout/Sidebar/Sidebar.stories.tsx @@ -19,12 +19,10 @@ import AddCircleOutlineIcon from '@material-ui/icons/AddCircleOutline'; import HomeOutlinedIcon from '@material-ui/icons/HomeOutlined'; import MenuIcon from '@material-ui/icons/Menu'; import BuildRoundedIcon from '@material-ui/icons/BuildRounded'; -import LibraryBooksOutlinedIcon from '@material-ui/icons/LibraryBooksOutlined'; -import WebOutlinedIcon from '@material-ui/icons/WebOutlined'; import MenuBookIcon from '@material-ui/icons/MenuBook'; import CloudQueueIcon from '@material-ui/icons/CloudQueue'; -import SettingsApplications from '@material-ui/icons/SettingsApplications'; import AcUnitIcon from '@material-ui/icons/AcUnit'; +import AppsIcon from '@material-ui/icons/Apps'; import React, { ComponentType } from 'react'; import { Sidebar, @@ -83,17 +81,7 @@ export const SampleScalableSidebar = () => ( - - - + (theme => ({ item: { @@ -121,8 +122,6 @@ export type SidebarSubmenuItemProps = { export const SidebarSubmenuItem = (props: SidebarSubmenuItemProps) => { const { title, to, icon: Icon, dropdownItems } = props; const classes = useStyles(); - const { pathname: locationPathname } = useLocation(); - const { pathname: toPathname } = useResolvedPath(to); const { setIsHoveredOn } = useContext(SidebarItemWithSubmenuContext); const { setOpen } = useContext(SidebarContext); @@ -132,8 +131,9 @@ export const SidebarSubmenuItem = (props: SidebarSubmenuItemProps) => { setOpen(false); } }; - - let isActive = locationPathname === toPathname; + const toLocation = useResolvedPath(to); + const currentLocation = useLocation(); + let isActive = isLocationMatch(currentLocation, toLocation); const [showDropDown, setShowDropDown] = useState(false); const handleClickDropdown = () => { @@ -142,7 +142,8 @@ export const SidebarSubmenuItem = (props: SidebarSubmenuItemProps) => { if (dropdownItems !== undefined) { dropdownItems.some(item => { const resolvedPath = resolvePath(item.to); - isActive = locationPathname === resolvedPath.pathname; + isActive = isLocationMatch(currentLocation, resolvedPath); + return isActive; }); return (
diff --git a/packages/core-components/src/layout/Sidebar/index.ts b/packages/core-components/src/layout/Sidebar/index.ts index 7e4baf9dde..b0b8ca70a5 100644 --- a/packages/core-components/src/layout/Sidebar/index.ts +++ b/packages/core-components/src/layout/Sidebar/index.ts @@ -36,7 +36,12 @@ export { SidebarSpacer, SidebarScrollWrapper, } from './Items'; -export type { SidebarItemClassKey } from './Items'; +export type { + SidebarItemClassKey, + SidebarSpaceClassKey, + SidebarSpacerClassKey, + SidebarDividerClassKey, +} from './Items'; export { IntroCard, SidebarIntro } from './Intro'; export type { SidebarIntroClassKey } from './Intro'; export { diff --git a/packages/core-components/src/layout/Sidebar/utils.test.ts b/packages/core-components/src/layout/Sidebar/utils.test.ts new file mode 100644 index 0000000000..b08dcda0fc --- /dev/null +++ b/packages/core-components/src/layout/Sidebar/utils.test.ts @@ -0,0 +1,98 @@ +/* + * 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 { Location, Path } from 'history'; +import { isLocationMatch } from './utils'; + +describe('isLocationMatching', () => { + let currentLocation: Location; + let toLocation: Path; + + it('return false when pathname in target and current location differ', async () => { + currentLocation = { + pathname: '/catalog', + search: '?kind=component', + state: null, + hash: '', + key: '', + }; + toLocation = { + pathname: '/catalog-a', + search: '?kind=component', + hash: '', + }; + expect(isLocationMatch(currentLocation, toLocation)).toBe(false); + }); + + it('return true when exact match between current and target location parameters', async () => { + currentLocation = { + pathname: '/catalog', + search: '?kind=component', + state: null, + hash: '', + key: '', + }; + toLocation = { pathname: '/catalog', search: '?kind=component', hash: '' }; + expect(isLocationMatch(currentLocation, toLocation)).toBe(true); + }); + + it('return true when target query parameters are subset of current location query parameters', async () => { + currentLocation = { + pathname: '/catalog', + search: '?x=foo&y=bar', + state: null, + hash: '', + key: '', + }; + toLocation = { pathname: '/catalog', search: '?x=foo', hash: '' }; + expect(isLocationMatch(currentLocation, toLocation)).toBe(true); + }); + + it('return false when no matching query parameters between target and current location', async () => { + currentLocation = { + pathname: '/catalog', + search: '?y=bar', + state: null, + hash: '', + key: '', + }; + toLocation = { pathname: '/catalog', search: '?x=foo', hash: '' }; + expect(isLocationMatch(currentLocation, toLocation)).toBe(false); + }); + + it('return true when query parameters match in different order', async () => { + currentLocation = { + pathname: '/catalog', + search: '?y=bar&x=foo', + state: null, + hash: '', + key: '', + }; + toLocation = { pathname: '/catalog', search: '?x=foo&y=bar', hash: '' }; + expect(isLocationMatch(currentLocation, toLocation)).toBe(true); + }); + + it('return true when there is a matching query parameter alongside extra parameters', async () => { + currentLocation = { + pathname: '/catalog', + search: '?y=bar&x=foo', + state: null, + hash: '', + key: '', + }; + toLocation = { pathname: '/catalog', search: '', hash: '' }; + expect(isLocationMatch(currentLocation, toLocation)).toBe(true); + }); +}); diff --git a/packages/core-components/src/layout/Sidebar/utils.ts b/packages/core-components/src/layout/Sidebar/utils.ts new file mode 100644 index 0000000000..1afebe796d --- /dev/null +++ b/packages/core-components/src/layout/Sidebar/utils.ts @@ -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 { Location, Path } from 'history'; +import { isEqual, isMatch } from 'lodash'; +import qs from 'qs'; + +export function isLocationMatch(currentLocation: Location, toLocation: Path) { + const toDecodedSearch = new URLSearchParams(toLocation.search).toString(); + const toQueryParameters = qs.parse(toDecodedSearch); + + const currentDecodedSearch = new URLSearchParams( + currentLocation.search, + ).toString(); + const currentQueryParameters = qs.parse(currentDecodedSearch); + + const matching = + isEqual(toLocation.pathname, currentLocation.pathname) && + isMatch(currentQueryParameters, toQueryParameters); + + return matching; +} diff --git a/packages/core-components/src/overridableComponents.ts b/packages/core-components/src/overridableComponents.ts index afad472d77..8044da4c81 100644 --- a/packages/core-components/src/overridableComponents.ts +++ b/packages/core-components/src/overridableComponents.ts @@ -81,6 +81,11 @@ import { CardActionsTopRightClassKey, ItemCardGridClassKey, ItemCardHeaderClassKey, + PageClassKey, + SidebarClassKey, + SidebarSpaceClassKey, + SidebarSpacerClassKey, + SidebarDividerClassKey, SidebarIntroClassKey, CustomProviderClassKey, SignInPageClassKey, @@ -151,6 +156,11 @@ type BackstageComponentsNameToClassKey = { BackstageInfoCardCardActionsTopRight: CardActionsTopRightClassKey; BackstageItemCardGrid: ItemCardGridClassKey; BackstageItemCardHeader: ItemCardHeaderClassKey; + BackstagePage: PageClassKey; + BackstageSidebar: SidebarClassKey; + BackstageSidebarSpace: SidebarSpaceClassKey; + BackstageSidebarSpacer: SidebarSpacerClassKey; + BackstageSidebarDivider: SidebarDividerClassKey; BackstageSidebarIntro: SidebarIntroClassKey; BackstageCustomProvider: CustomProviderClassKey; BackstageSignInPage: SignInPageClassKey; diff --git a/packages/core-plugin-api/CHANGELOG.md b/packages/core-plugin-api/CHANGELOG.md index fdf64779fb..06d36ef486 100644 --- a/packages/core-plugin-api/CHANGELOG.md +++ b/packages/core-plugin-api/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/core-plugin-api +## 0.3.1 + +### Patch Changes + +- 18d4f500af: Deprecated the `AnyAnalyticsContext` type and mark the `AnalyticsApi` experimental. +- 8a7372cfd5: Deprecated `auth0AuthApiRef`, `oauth2ApiRef`, `oidcAuthApiRef`, `samlAuthApiRef`, and marked the rest of the auth `ApiRef`s as experimental. For more information on how to address the deprecations, see https://backstage.io/docs/api/deprecations#generic-auth-api-refs. +- 760791a642: Renamed `AuthProvider` to `AuthProviderInfo` and add a required 'id' property to match the majority of usage. The `AuthProvider` type without the `id` property still exists but is deprecated, and all usage of it without an `id` is deprecated as well. For example, calling `createAuthRequest` without a `provider.id` is deprecated and it will be required in the future. + + The following types have been renamed. The old names are still exported but deprecated, and are scheduled for removal in a future release. + + - Renamed `AuthRequesterOptions` to `OAuthRequesterOptions` + - Renamed `AuthRequester` to `OAuthRequester` + - Renamed `PendingAuthRequest` to `PendingOAuthRequest` + ## 0.3.0 ### Minor Changes diff --git a/packages/core-plugin-api/api-report.md b/packages/core-plugin-api/api-report.md index d0ac0b5352..8403a06b6f 100644 --- a/packages/core-plugin-api/api-report.md +++ b/packages/core-plugin-api/api-report.md @@ -35,25 +35,26 @@ export type AlertMessage = { severity?: 'success' | 'info' | 'warning' | 'error'; }; -// @public +// @alpha export type AnalyticsApi = { captureEvent(event: AnalyticsEvent): void; }; -// @public +// @alpha export const analyticsApiRef: ApiRef; -// @public +// @alpha export const AnalyticsContext: (options: { attributes: Partial; children: ReactNode; }) => JSX.Element; -// @public -export type AnalyticsContextValue = CommonAnalyticsContext & - AnyAnalyticsContext; +// @alpha +export type AnalyticsContextValue = CommonAnalyticsContext & { + [param in string]: string | boolean | number | undefined; +}; -// @public +// @alpha export type AnalyticsEvent = { action: string; subject: string; @@ -62,12 +63,12 @@ export type AnalyticsEvent = { context: AnalyticsContextValue; }; -// @public +// @alpha export type AnalyticsEventAttributes = { [attribute in string]: string | boolean | number; }; -// @public +// @alpha export type AnalyticsTracker = { captureEvent: ( action: string, @@ -79,7 +80,7 @@ export type AnalyticsTracker = { ) => void; }; -// @public +// @public @deprecated export type AnyAnalyticsContext = { [param in string]: string | boolean | number | undefined; }; @@ -194,7 +195,7 @@ export type AppThemeApi = { // @public export const appThemeApiRef: ApiRef; -// @public +// @alpha export const atlassianAuthApiRef: ApiRef< OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi >; @@ -206,27 +207,26 @@ export function attachComponentData

( data: unknown, ): void; -// @public +// @public @deprecated export const auth0AuthApiRef: ApiRef< OpenIdConnectApi & ProfileInfoApi & BackstageIdentityApi & SessionApi >; +// @public @deprecated (undocumented) +export type AuthProvider = Omit; + // @public -export type AuthProvider = { +export type AuthProviderInfo = { + id: string; title: string; icon: IconComponent; }; -// @public -export type AuthRequester = ( - scopes: Set, -) => Promise; +// @public @deprecated (undocumented) +export type AuthRequester = OAuthRequester; -// @public -export type AuthRequesterOptions = { - provider: AuthProvider; - onAuthRequest(scopes: Set): Promise; -}; +// @public @deprecated (undocumented) +export type AuthRequesterOptions = OAuthRequesterOptions; // @public export type AuthRequestOptions = { @@ -272,7 +272,7 @@ export type BackstageUserIdentity = { ownershipEntityRefs: string[]; }; -// @public +// @alpha export const bitbucketAuthApiRef: ApiRef< OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi >; @@ -283,7 +283,7 @@ export type BootErrorPageProps = { error: Error; }; -// @public +// @alpha export type CommonAnalyticsContext = { pluginId: string; routeRef: string; @@ -512,17 +512,17 @@ export function getComponentData( type: string, ): T | undefined; -// @public +// @alpha export const githubAuthApiRef: ApiRef< OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi >; -// @public +// @alpha export const gitlabAuthApiRef: ApiRef< OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi >; -// @public +// @alpha export const googleAuthApiRef: ApiRef< OAuthApi & OpenIdConnectApi & @@ -570,7 +570,7 @@ export type MergeParams< P2 extends AnyParams, > = (P1[keyof P1] extends never ? {} : P1) & (P2 extends undefined ? {} : P2); -// @public +// @alpha export const microsoftAuthApiRef: ApiRef< OAuthApi & OpenIdConnectApi & @@ -579,7 +579,7 @@ export const microsoftAuthApiRef: ApiRef< SessionApi >; -// @public +// @public @deprecated export const oauth2ApiRef: ApiRef< OAuthApi & OpenIdConnectApi & @@ -598,15 +598,28 @@ export type OAuthApi = { // @public export type OAuthRequestApi = { - createAuthRequester( - options: AuthRequesterOptions, - ): AuthRequester; - authRequest$(): Observable_2; + createAuthRequester( + options: OAuthRequesterOptions, + ): OAuthRequester; + authRequest$(): Observable_2; }; // @public export const oauthRequestApiRef: ApiRef; +// @public +export type OAuthRequester = ( + scopes: Set, +) => Promise; + +// @public +export type OAuthRequesterOptions = { + provider: Omit & { + id?: string; + }; + onAuthRequest(scopes: Set): Promise; +}; + // @public export type OAuthScope = string | string[]; @@ -616,7 +629,7 @@ export type Observable = Observable_2; // @public @deprecated export type Observer = Observer_2; -// @public +// @public @deprecated export const oidcAuthApiRef: ApiRef< OAuthApi & OpenIdConnectApi & @@ -625,7 +638,7 @@ export const oidcAuthApiRef: ApiRef< SessionApi >; -// @public +// @alpha export const oktaAuthApiRef: ApiRef< OAuthApi & OpenIdConnectApi & @@ -637,7 +650,7 @@ export const oktaAuthApiRef: ApiRef< // @public export type OldIconComponent = ComponentType; -// @public +// @alpha export const oneloginAuthApiRef: ApiRef< OAuthApi & OpenIdConnectApi & @@ -679,10 +692,15 @@ export type PathParams = { [name in ParamNames]: string; }; +// @public @deprecated (undocumented) +export type PendingAuthRequest = PendingOAuthRequest; + // @public -export type PendingAuthRequest = { - provider: AuthProvider; - reject: () => void; +export type PendingOAuthRequest = { + provider: Omit & { + id?: string; + }; + reject(): void; trigger(): Promise; }; @@ -738,7 +756,7 @@ export type RouteRef = { title?: string; }; -// @public +// @public @deprecated export const samlAuthApiRef: ApiRef< ProfileInfoApi & BackstageIdentityApi & SessionApi >; @@ -803,7 +821,7 @@ export type TypesToApiRefs = { [key in keyof T]: ApiRef; }; -// @public +// @alpha export function useAnalytics(): AnalyticsTracker; // @public diff --git a/packages/core-plugin-api/package.json b/packages/core-plugin-api/package.json index 72827bde73..20479acfeb 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": "0.3.0", + "version": "0.3.1", "private": false, "publishConfig": { "access": "public", @@ -45,8 +45,8 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.10.1", - "@backstage/core-app-api": "^0.2.0", + "@backstage/cli": "^0.10.2", + "@backstage/core-app-api": "^0.2.1", "@backstage/test-utils": "^0.1.24", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/packages/core-plugin-api/src/analytics/AnalyticsContext.tsx b/packages/core-plugin-api/src/analytics/AnalyticsContext.tsx index 075cdff3ff..af362cd921 100644 --- a/packages/core-plugin-api/src/analytics/AnalyticsContext.tsx +++ b/packages/core-plugin-api/src/analytics/AnalyticsContext.tsx @@ -60,7 +60,7 @@ export const useAnalyticsContext = (): AnalyticsContextValue => { * Analytics contexts are additive, meaning the context ultimately emitted with * an event is the combination of all contexts in the parent tree. * - * @public + * @alpha */ export const AnalyticsContext = (options: { attributes: Partial; diff --git a/packages/core-plugin-api/src/analytics/types.ts b/packages/core-plugin-api/src/analytics/types.ts index d001c38d3e..431e453a61 100644 --- a/packages/core-plugin-api/src/analytics/types.ts +++ b/packages/core-plugin-api/src/analytics/types.ts @@ -17,7 +17,7 @@ /** * Common analytics context attributes. * - * @public + * @alpha */ export type CommonAnalyticsContext = { /** @@ -40,6 +40,7 @@ export type CommonAnalyticsContext = { * Allows arbitrary scalar values as context attributes too. * * @public + * @deprecated Will be removed, use `AnalyticsContextValue` instead */ export type AnyAnalyticsContext = { [param in string]: string | boolean | number | undefined; @@ -48,7 +49,8 @@ export type AnyAnalyticsContext = { /** * Analytics context envelope. * - * @public + * @alpha */ -export type AnalyticsContextValue = CommonAnalyticsContext & - AnyAnalyticsContext; +export type AnalyticsContextValue = CommonAnalyticsContext & { + [param in string]: string | boolean | number | undefined; +}; diff --git a/packages/core-plugin-api/src/analytics/useAnalytics.tsx b/packages/core-plugin-api/src/analytics/useAnalytics.tsx index 5a047e5a63..f1297c5e4e 100644 --- a/packages/core-plugin-api/src/analytics/useAnalytics.tsx +++ b/packages/core-plugin-api/src/analytics/useAnalytics.tsx @@ -35,7 +35,7 @@ function useAnalyticsApi(): AnalyticsApi { /** * Gets a pre-configured analytics tracker. * - * @public + * @alpha */ export function useAnalytics(): AnalyticsTracker { const trackerRef = useRef(null); diff --git a/packages/core-plugin-api/src/apis/definitions/AnalyticsApi.ts b/packages/core-plugin-api/src/apis/definitions/AnalyticsApi.ts index ef190d6c21..dc38d7a250 100644 --- a/packages/core-plugin-api/src/apis/definitions/AnalyticsApi.ts +++ b/packages/core-plugin-api/src/apis/definitions/AnalyticsApi.ts @@ -21,7 +21,7 @@ import { AnalyticsContextValue } from '../../analytics/types'; * Represents an event worth tracking in an analytics system that could inform * how users of a Backstage instance are using its features. * - * @public + * @alpha */ export type AnalyticsEvent = { /** @@ -79,7 +79,7 @@ export type AnalyticsEvent = { * A structure allowing other arbitrary metadata to be provided by analytics * event emitters. * - * @public + * @alpha */ export type AnalyticsEventAttributes = { [attribute in string]: string | boolean | number; @@ -89,7 +89,7 @@ export type AnalyticsEventAttributes = { * Represents a tracker with methods that can be called to track events in a * configured analytics service. * - * @public + * @alpha */ export type AnalyticsTracker = { captureEvent: ( @@ -103,6 +103,8 @@ export type AnalyticsTracker = { }; /** + * **EXPERIMENTAL** + * * The Analytics API is used to track user behavior in a Backstage instance. * * @remarks @@ -111,7 +113,7 @@ export type AnalyticsTracker = { * useAnalytics() hook. This will return a pre-configured AnalyticsTracker * with relevant methods for instrumentation. * - * @public + * @alpha */ export type AnalyticsApi = { /** @@ -122,9 +124,11 @@ export type AnalyticsApi = { }; /** + * **EXPERIMENTAL** + * * The {@link ApiRef} of {@link AnalyticsApi}. * - * @public + * @alpha */ export const analyticsApiRef: ApiRef = createApiRef({ id: 'core.analytics', diff --git a/packages/core-plugin-api/src/apis/definitions/ErrorApi.ts b/packages/core-plugin-api/src/apis/definitions/ErrorApi.ts index 45ee0901c8..364a7bd29a 100644 --- a/packages/core-plugin-api/src/apis/definitions/ErrorApi.ts +++ b/packages/core-plugin-api/src/apis/definitions/ErrorApi.ts @@ -41,7 +41,15 @@ export type Error = ErrorApiError; * @public */ export type ErrorApiErrorContext = { - // If set to true, this error should not be displayed to the user. Defaults to false. + /** + * If set to true, this error should not be displayed to the user. + * + * Hidden errors are typically not displayed in the UI, but the ErrorApi + * implementation may still report them to error tracking services + * or other utilities that care about all errors. + * + * @defaultValue false + */ hidden?: boolean; }; diff --git a/packages/core-plugin-api/src/apis/definitions/OAuthRequestApi.ts b/packages/core-plugin-api/src/apis/definitions/OAuthRequestApi.ts index 03e129ef6a..5b8dab29f7 100644 --- a/packages/core-plugin-api/src/apis/definitions/OAuthRequestApi.ts +++ b/packages/core-plugin-api/src/apis/definitions/OAuthRequestApi.ts @@ -14,31 +14,15 @@ * limitations under the License. */ -import { IconComponent } from '../../icons/types'; import { Observable } from '@backstage/types'; import { ApiRef, createApiRef } from '../system'; +import { AuthProviderInfo } from './auth'; /** - * Information about the auth provider that we're requesting a login towards. - * - * @remarks - * - * This should be shown to the user so that they can be informed about what login is being requested - * before a popup is shown. - * * @public + * @deprecated Use AuthProviderInfo instead */ -export type AuthProvider = { - /** - * Title for the auth provider, for example "GitHub" - */ - title: string; - - /** - * Icon for the auth provider. - */ - icon: IconComponent; -}; +export type AuthProvider = Omit; /** * Describes how to handle auth requests. Both how to show them to the user, and what to do when @@ -46,26 +30,34 @@ export type AuthProvider = { * * @public */ -export type AuthRequesterOptions = { +export type OAuthRequesterOptions = { /** * Information about the auth provider, which will be forwarded to auth requests. + * + * Not passing in an `id` is deprecated, and it will be required in the future. */ - provider: AuthProvider; + provider: Omit & { id?: string }; /** * Implementation of the auth flow, which will be called synchronously when * trigger() is called on an auth requests. */ - onAuthRequest(scopes: Set): Promise; + onAuthRequest(scopes: Set): Promise; }; +/** + * @public + * @deprecated Use OAuthRequesterOptions instead + */ +export type AuthRequesterOptions = OAuthRequesterOptions; + /** * Function used to trigger new auth requests for a set of scopes. * * @remarks * * The returned promise will resolve to the same value returned by the onAuthRequest in the - * {@link AuthRequesterOptions}. Or rejected, if the request is rejected. + * {@link OAuthRequesterOptions}. Or rejected, if the request is rejected. * * This function can be called multiple times before the promise resolves. All calls * will be merged into one request, and the scopes forwarded to the onAuthRequest will be the @@ -73,9 +65,15 @@ export type AuthRequesterOptions = { * * @public */ -export type AuthRequester = ( +export type OAuthRequester = ( scopes: Set, -) => Promise; +) => Promise; + +/** + * @public + * @deprecated Use OAuthRequester instead + */ +export type AuthRequester = OAuthRequester; /** * An pending auth request for a single auth provider. The request will remain in this pending @@ -88,16 +86,18 @@ export type AuthRequester = ( * * @public */ -export type PendingAuthRequest = { +export type PendingOAuthRequest = { /** * Information about the auth provider, as given in the AuthRequesterOptions + * + * Not passing in an `id` is deprecated, and it will be required in the future. */ - provider: AuthProvider; + provider: Omit & { id?: string }; /** * Rejects the request, causing all pending AuthRequester calls to fail with "RejectedError". */ - reject: () => void; + reject(): void; /** * Trigger the auth request to continue the auth flow, by for example showing a popup. @@ -107,6 +107,12 @@ export type PendingAuthRequest = { trigger(): Promise; }; +/** + * @public + * @deprecated Use PendingOAuthRequest instead + */ +export type PendingAuthRequest = PendingOAuthRequest; + /** * Provides helpers for implemented OAuth login flows within Backstage. * @@ -125,9 +131,9 @@ export type OAuthRequestApi = { * * See AuthRequesterOptions, AuthRequester, and handleAuthRequests for more info. */ - createAuthRequester( - options: AuthRequesterOptions, - ): AuthRequester; + createAuthRequester( + options: OAuthRequesterOptions, + ): OAuthRequester; /** * Observers pending auth requests. The returned observable will emit all @@ -140,7 +146,7 @@ export type OAuthRequestApi = { * If a auth is triggered, and the auth handler resolves successfully, then all currently pending * AuthRequester calls will resolve to the value returned by the onAuthRequest call. */ - authRequest$(): Observable; + authRequest$(): Observable; }; /** diff --git a/packages/core-plugin-api/src/apis/definitions/auth.ts b/packages/core-plugin-api/src/apis/definitions/auth.ts index 37308ec29b..a362d16750 100644 --- a/packages/core-plugin-api/src/apis/definitions/auth.ts +++ b/packages/core-plugin-api/src/apis/definitions/auth.ts @@ -15,6 +15,7 @@ */ import { ApiRef, createApiRef } from '../system'; +import { IconComponent } from '../../icons/types'; import { Observable } from '@backstage/types'; /** @@ -28,6 +29,33 @@ import { Observable } from '@backstage/types'; * const googleAuthApiRef = createApiRef({ ... }) */ +/** + * Information about the auth provider. + * + * @remarks + * + * This information is used both to connect the correct auth provider in the backend, as + * well as displaying the provider to the user. + * + * @public + */ +export type AuthProviderInfo = { + /** + * The ID of the auth provider. This should match with ID of the provider in the `@backstage/auth-backend`. + */ + id: string; + + /** + * Title for the auth provider, for example "GitHub" + */ + title: string; + + /** + * Icon for the auth provider. + */ + icon: IconComponent; +}; + /** * An array of scopes, or a scope string formatted according to the * auth provider, which is typically a space separated list. @@ -282,14 +310,14 @@ export type SessionApi = { /** * Provides authentication towards Google APIs and identities. * + * @alpha This API is **EXPERIMENTAL** and might change in the future. + * * @remarks * * See {@link https://developers.google.com/identity/protocols/googlescopes} for a full list of supported scopes. * * Note that the ID token payload is only guaranteed to contain the user's numerical Google ID, * email and expiration information. Do not rely on any other fields, as they might not be present. - * - * @public */ export const googleAuthApiRef: ApiRef< OAuthApi & @@ -304,12 +332,12 @@ export const googleAuthApiRef: ApiRef< /** * Provides authentication towards GitHub APIs. * + * @alpha This API is **EXPERIMENTAL** and might change in the future. + * * @remarks * * See {@link https://developer.github.com/apps/building-oauth-apps/understanding-scopes-for-oauth-apps/} * for a full list of supported scopes. - * - * @public */ export const githubAuthApiRef: ApiRef< OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi @@ -320,12 +348,12 @@ export const githubAuthApiRef: ApiRef< /** * Provides authentication towards Okta APIs. * + * @alpha This API is **EXPERIMENTAL** and might change in the future. + * * @remarks * * See {@link https://developer.okta.com/docs/guides/implement-oauth-for-okta/scopes/} * for a full list of supported scopes. - * - * @public */ export const oktaAuthApiRef: ApiRef< OAuthApi & @@ -340,12 +368,12 @@ export const oktaAuthApiRef: ApiRef< /** * Provides authentication towards GitLab APIs. * + * @alpha This API is **EXPERIMENTAL** and might change in the future. + * * @remarks * * See {@link https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html#limiting-scopes-of-a-personal-access-token} * for a full list of supported scopes. - * - * @public */ export const gitlabAuthApiRef: ApiRef< OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi @@ -362,6 +390,7 @@ export const gitlabAuthApiRef: ApiRef< * for a full list of supported scopes. * * @public + * @deprecated See https://backstage.io/docs/api/deprecations#generic-auth-api-refs */ export const auth0AuthApiRef: ApiRef< OpenIdConnectApi & ProfileInfoApi & BackstageIdentityApi & SessionApi @@ -372,13 +401,13 @@ export const auth0AuthApiRef: ApiRef< /** * Provides authentication towards Microsoft APIs and identities. * + * @alpha This API is **EXPERIMENTAL** and might change in the future. + * * @remarks * * For more info and a full list of supported scopes, see: * - {@link https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-permissions-and-consent} * - {@link https://docs.microsoft.com/en-us/graph/permissions-reference} - * - * @public */ export const microsoftAuthApiRef: ApiRef< OAuthApi & @@ -394,6 +423,7 @@ export const microsoftAuthApiRef: ApiRef< * Provides authentication for custom identity providers. * * @public + * @deprecated See https://backstage.io/docs/api/deprecations#generic-auth-api-refs */ export const oauth2ApiRef: ApiRef< OAuthApi & @@ -409,6 +439,7 @@ export const oauth2ApiRef: ApiRef< * Provides authentication for custom OpenID Connect identity providers. * * @public + * @deprecated See https://backstage.io/docs/api/deprecations#generic-auth-api-refs */ export const oidcAuthApiRef: ApiRef< OAuthApi & @@ -424,6 +455,7 @@ export const oidcAuthApiRef: ApiRef< * Provides authentication for SAML-based identity providers. * * @public + * @deprecated See https://backstage.io/docs/api/deprecations#generic-auth-api-refs */ export const samlAuthApiRef: ApiRef< ProfileInfoApi & BackstageIdentityApi & SessionApi @@ -434,7 +466,7 @@ export const samlAuthApiRef: ApiRef< /** * Provides authentication towards OneLogin APIs. * - * @public + * @alpha This API is **EXPERIMENTAL** and might change in the future. */ export const oneloginAuthApiRef: ApiRef< OAuthApi & @@ -449,12 +481,11 @@ export const oneloginAuthApiRef: ApiRef< /** * Provides authentication towards Bitbucket APIs. * + * @alpha This API is **EXPERIMENTAL** and might change in the future. * @remarks * * See {@link https://support.atlassian.com/bitbucket-cloud/docs/use-oauth-on-bitbucket-cloud/} * for a full list of supported scopes. - * - * @public */ export const bitbucketAuthApiRef: ApiRef< OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi @@ -465,12 +496,11 @@ export const bitbucketAuthApiRef: ApiRef< /** * Provides authentication towards Atlassian APIs. * + * @alpha This API is **EXPERIMENTAL** and might change in the future. * @remarks * * See {@link https://developer.atlassian.com/cloud/jira/platform/scopes-for-connect-and-oauth-2-3LO-apps/} * for a full list of supported scopes. - * - * @public */ export const atlassianAuthApiRef: ApiRef< OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi diff --git a/packages/create-app/CHANGELOG.md b/packages/create-app/CHANGELOG.md index cdcf7f4952..60d8a55635 100644 --- a/packages/create-app/CHANGELOG.md +++ b/packages/create-app/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/create-app +## 0.4.8 + +### Patch Changes + +- 25dfc2d483: Updated the root `package.json` to include files with `.cjs` and `.mjs` extensions in the `"lint-staged"` configuration. + + To make this change to an existing app, apply the following changes to the `package.json` file: + + ```diff + "lint-staged": { + - "*.{js,jsx,ts,tsx}": [ + + "*.{js,jsx,ts,tsx,mjs,cjs}": [ + ``` + ## 0.4.7 ### Patch Changes diff --git a/packages/create-app/package.json b/packages/create-app/package.json index ec739820b9..7a6c7ad551 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.7", + "version": "0.4.8", "private": false, "publishConfig": { "access": "public" @@ -41,7 +41,7 @@ }, "devDependencies": { "@types/fs-extra": "^9.0.1", - "@types/inquirer": "^7.3.1", + "@types/inquirer": "^8.1.3", "@types/node": "^14.14.32", "@types/recursive-readdir": "^2.2.0", "mock-fs": "^5.1.1", diff --git a/packages/create-app/templates/default-app/package.json.hbs b/packages/create-app/templates/default-app/package.json.hbs index b0747c4454..a73045c050 100644 --- a/packages/create-app/templates/default-app/package.json.hbs +++ b/packages/create-app/templates/default-app/package.json.hbs @@ -38,7 +38,7 @@ }, "prettier": "@spotify/prettier-config", "lint-staged": { - "*.{js,jsx,ts,tsx}": [ + "*.{js,jsx,ts,tsx,mjs,cjs}": [ "eslint --fix", "prettier --write" ], diff --git a/packages/storybook/.storybook/main.js b/packages/storybook/.storybook/main.js index 72ff42311d..f5852b0164 100644 --- a/packages/storybook/.storybook/main.js +++ b/packages/storybook/.storybook/main.js @@ -59,7 +59,7 @@ module.exports = ({ args }) => { }, }, { - test: /\.(jsx?|mjs)$/, + test: /\.(jsx?|mjs|cjs)$/, exclude: /node_modules/, loader: require.resolve('@sucrase/webpack-loader'), options: { diff --git a/plugins/allure/package.json b/plugins/allure/package.json index 8b3ee3b882..261c5e1879 100644 --- a/plugins/allure/package.json +++ b/plugins/allure/package.json @@ -23,8 +23,8 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.7", - "@backstage/core-components": "^0.8.0", - "@backstage/core-plugin-api": "^0.3.0", + "@backstage/core-components": "^0.8.1", + "@backstage/core-plugin-api": "^0.3.1", "@backstage/plugin-catalog-react": "^0.6.5", "@backstage/theme": "^0.2.14", "@material-ui/core": "^4.12.2", @@ -37,8 +37,8 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.10.1", - "@backstage/core-app-api": "^0.2.0", + "@backstage/cli": "^0.10.2", + "@backstage/core-app-api": "^0.2.1", "@backstage/dev-utils": "^0.2.14", "@backstage/test-utils": "^0.1.24", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/analytics-module-ga/package.json b/plugins/analytics-module-ga/package.json index 15055b2a4f..5f48d4404e 100644 --- a/plugins/analytics-module-ga/package.json +++ b/plugins/analytics-module-ga/package.json @@ -22,8 +22,8 @@ }, "dependencies": { "@backstage/config": "^0.1.5", - "@backstage/core-components": "^0.8.0", - "@backstage/core-plugin-api": "^0.3.0", + "@backstage/core-components": "^0.8.1", + "@backstage/core-plugin-api": "^0.3.1", "@backstage/theme": "^0.2.14", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -35,8 +35,8 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.10.1", - "@backstage/core-app-api": "^0.2.0", + "@backstage/cli": "^0.10.2", + "@backstage/core-app-api": "^0.2.1", "@backstage/dev-utils": "^0.2.14", "@backstage/test-utils": "^0.1.24", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/apache-airflow/CHANGELOG.md b/plugins/apache-airflow/CHANGELOG.md new file mode 100644 index 0000000000..64abdb929a --- /dev/null +++ b/plugins/apache-airflow/CHANGELOG.md @@ -0,0 +1,22 @@ +# @backstage/plugin-apache-airflow + +## 0.1.0 + +### Minor Changes + +- 9aea335911: Introduces a new plugin for the Apache Airflow workflow management platform. + This implementation has been tested with the Apache Airflow v2 API, + authenticating with basic authentication through the Backstage proxy plugin. + + Supported functionality includes: + + - Information card of version information of the Airflow instance + - Information card of instance health for the meta-database and scheduler + - Table of DAGs with meta information and status, along with a link to view + details in the Airflow UI + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@0.3.1 + - @backstage/core-components@0.8.1 diff --git a/plugins/apache-airflow/package.json b/plugins/apache-airflow/package.json index a361961d12..58c5dceb77 100644 --- a/plugins/apache-airflow/package.json +++ b/plugins/apache-airflow/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-apache-airflow", - "version": "0.0.0", + "version": "0.1.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,8 +20,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.8.0", - "@backstage/core-plugin-api": "^0.3.0", + "@backstage/core-components": "^0.8.1", + "@backstage/core-plugin-api": "^0.3.1", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", @@ -33,8 +33,8 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.10.1", - "@backstage/core-app-api": "^0.2.0", + "@backstage/cli": "^0.10.2", + "@backstage/core-app-api": "^0.2.1", "@backstage/dev-utils": "^0.2.14", "@backstage/test-utils": "^0.1.24", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/api-docs/package.json b/plugins/api-docs/package.json index 93df7e68aa..fd057cf358 100644 --- a/plugins/api-docs/package.json +++ b/plugins/api-docs/package.json @@ -32,8 +32,8 @@ "dependencies": { "@asyncapi/react-component": "^1.0.0-next.25", "@backstage/catalog-model": "^0.9.7", - "@backstage/core-components": "^0.8.0", - "@backstage/core-plugin-api": "^0.3.0", + "@backstage/core-components": "^0.8.1", + "@backstage/core-plugin-api": "^0.3.1", "@backstage/plugin-catalog": "^0.7.4", "@backstage/plugin-catalog-react": "^0.6.5", "@backstage/theme": "^0.2.14", @@ -54,8 +54,8 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.10.1", - "@backstage/core-app-api": "^0.2.0", + "@backstage/cli": "^0.10.2", + "@backstage/core-app-api": "^0.2.1", "@backstage/dev-utils": "^0.2.14", "@backstage/test-utils": "^0.1.24", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/auth-backend/CHANGELOG.md b/plugins/auth-backend/CHANGELOG.md index dc45804449..cfdae9f4ef 100644 --- a/plugins/auth-backend/CHANGELOG.md +++ b/plugins/auth-backend/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-auth-backend +## 0.5.1 + +### Patch Changes + +- 699c2e9ddc: export minimal typescript types for OIDC provider +- Updated dependencies + - @backstage/backend-common@0.9.14 + - @backstage/catalog-model@0.9.8 + ## 0.5.0 ### Minor Changes diff --git a/plugins/auth-backend/api-report.md b/plugins/auth-backend/api-report.md index a637118130..9ed523c678 100644 --- a/plugins/auth-backend/api-report.md +++ b/plugins/auth-backend/api-report.md @@ -47,6 +47,16 @@ export type AtlassianProviderOptions = { }; }; +// @public +export type AuthHandler = ( + input: AuthResult, +) => Promise; + +// @public +export type AuthHandlerResult = { + profile: ProfileInfo; +}; + // Warning: (ae-missing-release-tag) "AuthProviderFactory" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -258,7 +268,6 @@ export const createOAuth2Provider: ( options?: OAuth2ProviderOptions | undefined, ) => AuthProviderFactory; -// Warning: (ae-forgotten-export) The symbol "OidcProviderOptions" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "createOidcProvider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -534,6 +543,20 @@ export type OAuthState = { origin?: string; }; +// @public +export type OidcAuthResult = { + tokenset: TokenSet; + userinfo: UserinfoResponse; +}; + +// @public +export type OidcProviderOptions = { + authHandler?: AuthHandler; + signIn?: { + resolver?: SignInResolver; + }; +}; + // Warning: (ae-missing-release-tag) "oktaEmailSignInResolver" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -606,6 +629,22 @@ export type SamlProviderOptions = { }; }; +// @public +export type SignInInfo = { + profile: ProfileInfo; + result: AuthResult; +}; + +// @public +export type SignInResolver = ( + info: SignInInfo, + context: { + tokenIssuer: TokenIssuer; + catalogIdentityClient: CatalogIdentityClient; + logger: Logger_2; + }, +) => Promise; + // Warning: (ae-missing-release-tag) "TokenIssuer" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public @@ -637,8 +676,6 @@ export type WebMessageResponse = // Warnings were encountered during analysis: // // src/identity/types.d.ts:31:9 - (ae-forgotten-export) The symbol "AnyJWK" needs to be exported by the entry point index.d.ts -// src/providers/atlassian/provider.d.ts:37:5 - (ae-forgotten-export) The symbol "AuthHandler" needs to be exported by the entry point index.d.ts -// src/providers/atlassian/provider.d.ts:42:9 - (ae-forgotten-export) The symbol "SignInResolver" needs to be exported by the entry point index.d.ts // src/providers/aws-alb/provider.d.ts:77:5 - (ae-forgotten-export) The symbol "AwsAlbResult" needs to be exported by the entry point index.d.ts // src/providers/github/provider.d.ts:71:58 - (tsdoc-escape-greater-than) The ">" character should be escaped using a backslash to avoid confusion with an HTML tag // src/providers/github/provider.d.ts:71:90 - (tsdoc-escape-greater-than) The ">" character should be escaped using a backslash to avoid confusion with an HTML tag diff --git a/plugins/auth-backend/package.json b/plugins/auth-backend/package.json index b2822c4f33..c7d208eef2 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.5.0", + "version": "0.5.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -30,9 +30,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.9.13", + "@backstage/backend-common": "^0.9.14", "@backstage/catalog-client": "^0.5.2", - "@backstage/catalog-model": "^0.9.7", + "@backstage/catalog-model": "^0.9.8", "@backstage/config": "^0.1.11", "@backstage/errors": "^0.1.5", "@backstage/test-utils": "^0.1.24", @@ -73,7 +73,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.10.1", + "@backstage/cli": "^0.10.2", "@types/body-parser": "^1.19.0", "@types/cookie-parser": "^1.4.2", "@types/express-session": "^1.17.2", diff --git a/plugins/auth-backend/src/providers/index.ts b/plugins/auth-backend/src/providers/index.ts index 4ecbb98fd2..9589e97265 100644 --- a/plugins/auth-backend/src/providers/index.ts +++ b/plugins/auth-backend/src/providers/index.ts @@ -34,6 +34,10 @@ export type { AuthProviderRouteHandlers, AuthProviderFactoryOptions, AuthProviderFactory, + AuthHandler, + AuthHandlerResult, + SignInResolver, + SignInInfo, } from './types'; // These types are needed for a postMessage from the login pop-up diff --git a/plugins/auth-backend/src/providers/oidc/index.ts b/plugins/auth-backend/src/providers/oidc/index.ts index 20d014a783..39bd02928d 100644 --- a/plugins/auth-backend/src/providers/oidc/index.ts +++ b/plugins/auth-backend/src/providers/oidc/index.ts @@ -13,5 +13,5 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - +export type { OidcAuthResult, OidcProviderOptions } from './provider'; export { createOidcProvider } from './provider'; diff --git a/plugins/auth-backend/src/providers/oidc/provider.ts b/plugins/auth-backend/src/providers/oidc/provider.ts index 158af6f830..fe4c042500 100644 --- a/plugins/auth-backend/src/providers/oidc/provider.ts +++ b/plugins/auth-backend/src/providers/oidc/provider.ts @@ -56,7 +56,11 @@ type OidcImpl = { client: Client; }; -type AuthResult = { +/** + * authentication result for the OIDC which includes the token set and user information (a profile response sent by OIDC server) + * @public + */ +export type OidcAuthResult = { tokenset: TokenSet; userinfo: UserinfoResponse; }; @@ -66,8 +70,8 @@ export type Options = OAuthProviderOptions & { scope?: string; prompt?: string; tokenSignedResponseAlg?: string; - signInResolver?: SignInResolver; - authHandler: AuthHandler; + signInResolver?: SignInResolver; + authHandler: AuthHandler; tokenIssuer: TokenIssuer; catalogIdentityClient: CatalogIdentityClient; logger: Logger; @@ -78,8 +82,8 @@ export class OidcAuthProvider implements OAuthHandlers { private readonly scope?: string; private readonly prompt?: string; - private readonly signInResolver?: SignInResolver; - private readonly authHandler: AuthHandler; + private readonly signInResolver?: SignInResolver; + private readonly authHandler: AuthHandler; private readonly tokenIssuer: TokenIssuer; private readonly catalogIdentityClient: CatalogIdentityClient; private readonly logger: Logger; @@ -113,7 +117,7 @@ export class OidcAuthProvider implements OAuthHandlers { ): Promise<{ response: OAuthResponse; refreshToken?: string }> { const { strategy } = await this.implementation; const strategyResponse = await executeFrameHandlerStrategy< - AuthResult, + OidcAuthResult, PrivateInfo >(req, strategy); const { @@ -158,7 +162,7 @@ export class OidcAuthProvider implements OAuthHandlers { ( tokenset: TokenSet, userinfo: UserinfoResponse, - done: PassportDoneCallback, + done: PassportDoneCallback, ) => { if (typeof done !== 'function') { throw new Error( @@ -180,7 +184,7 @@ export class OidcAuthProvider implements OAuthHandlers { // Use this function to grab the user profile info from the token // Then populate the profile with it - private async handleResult(result: AuthResult): Promise { + private async handleResult(result: OidcAuthResult): Promise { const { profile } = await this.authHandler(result); const response: OAuthResponse = { providerInfo: { @@ -210,27 +214,37 @@ export class OidcAuthProvider implements OAuthHandlers { } } -export const oAuth2DefaultSignInResolver: SignInResolver = async ( - info, - ctx, -) => { - const { profile } = info; +export const oAuth2DefaultSignInResolver: SignInResolver = + async (info, ctx) => { + const { profile } = info; - if (!profile.email) { - throw new Error('Profile contained no email'); - } - const userId = profile.email.split('@')[0]; - const token = await ctx.tokenIssuer.issueToken({ - claims: { sub: userId, ent: [`user:default/${userId}`] }, - }); - return { id: userId, token }; -}; + if (!profile.email) { + throw new Error('Profile contained no email'); + } + const userId = profile.email.split('@')[0]; + const token = await ctx.tokenIssuer.issueToken({ + claims: { sub: userId, ent: [`user:default/${userId}`] }, + }); + return { id: userId, token }; + }; +/** + * OIDC provider callback options. An auth handler and a sign in resolver + * can be passed while creating a OIDC provider. + * + * authHandler : called after sign in was successful, a new object must be returned which includes a profile + * signInResolver: called after sign in was successful, expects to return a new {@link BackstageSignInResult} + * + * Both options are optional. There is fallback for authHandler where the default handler expect an e-mail explicitly + * otherwise it throws an error + * + * @public + */ export type OidcProviderOptions = { - authHandler?: AuthHandler; + authHandler?: AuthHandler; signIn?: { - resolver?: SignInResolver; + resolver?: SignInResolver; }; }; @@ -260,7 +274,7 @@ export const createOidcProvider = ( tokenIssuer, }); - const authHandler: AuthHandler = options?.authHandler + const authHandler: AuthHandler = options?.authHandler ? options.authHandler : async ({ userinfo }) => ({ profile: { @@ -271,7 +285,7 @@ export const createOidcProvider = ( }); const signInResolverFn = options?.signIn?.resolver ?? oAuth2DefaultSignInResolver; - const signInResolver: SignInResolver = info => + const signInResolver: SignInResolver = info => signInResolverFn(info, { catalogIdentityClient, tokenIssuer, diff --git a/plugins/auth-backend/src/providers/types.ts b/plugins/auth-backend/src/providers/types.ts index dafa52163c..ecb466c768 100644 --- a/plugins/auth-backend/src/providers/types.ts +++ b/plugins/auth-backend/src/providers/types.ts @@ -240,6 +240,10 @@ export type ProfileInfo = { picture?: string; }; +/** + * type of sign in information context, includes the profile information and authentication result which contains auth. related information + * @public + */ export type SignInInfo = { /** * The simple profile passed down for use in the frontend. @@ -252,6 +256,11 @@ export type SignInInfo = { result: AuthResult; }; +/** + * Sign in resolver type describes the function which handles the result of a successful authentication + * and it must return a valid {@link BackstageSignInResult} + * @public + */ export type SignInResolver = ( info: SignInInfo, context: { @@ -261,6 +270,10 @@ export type SignInResolver = ( }, ) => Promise; +/** + * The return type of authentication handler which must contain a valid profile information + * @public + */ export type AuthHandlerResult = { profile: ProfileInfo }; /** @@ -270,6 +283,8 @@ export type AuthHandlerResult = { profile: ProfileInfo }; * * Throwing an error in the function will cause the authentication to fail, making it * possible to use this function as a way to limit access to a certain group of users. + * + * @public */ export type AuthHandler = ( input: AuthResult, diff --git a/plugins/azure-devops-backend/CHANGELOG.md b/plugins/azure-devops-backend/CHANGELOG.md index e0e74a9bc6..ff802a30e1 100644 --- a/plugins/azure-devops-backend/CHANGELOG.md +++ b/plugins/azure-devops-backend/CHANGELOG.md @@ -1,5 +1,66 @@ # @backstage/plugin-azure-devops-backend +## 0.2.5 + +### Patch Changes + +- daf32e2c9b: Created some initial filters that can be used to create pull request columns: + + - All + - AssignedToUser + - AssignedToCurrentUser + - AssignedToTeam + - AssignedToTeams + - AssignedToCurrentUsersTeams + - CreatedByUser + - CreatedByCurrentUser + - CreatedByTeam + - CreatedByTeams + - CreatedByCurrentUsersTeams + + Example custom column creation: + + ```tsx + const COLUMN_CONFIGS: PullRequestColumnConfig[] = [ + { + title: 'Created by me', + filters: [{ type: FilterType.CreatedByCurrentUser }], + }, + { + title: 'Created by Backstage Core', + filters: [ + { + type: FilterType.CreatedByTeam, + teamName: 'Backstage Core', + }, + ], + }, + { + title: 'Assigned to my teams', + filters: [{ type: FilterType.AssignedToCurrentUsersTeams }], + }, + { + title: 'Other PRs', + filters: [{ type: FilterType.All }], + simplified: true, + }, + ]; + + + } + />; + ``` + +- Updated dependencies + - @backstage/backend-common@0.9.14 + - @backstage/plugin-azure-devops-common@0.1.3 + ## 0.2.4 ### Patch Changes diff --git a/plugins/azure-devops-backend/package.json b/plugins/azure-devops-backend/package.json index 9741380fb1..a7aec3f2d6 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.2.4", + "version": "0.2.5", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,9 +20,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.9.13", + "@backstage/backend-common": "^0.9.14", "@backstage/config": "^0.1.11", - "@backstage/plugin-azure-devops-common": "^0.1.2", + "@backstage/plugin-azure-devops-common": "^0.1.3", "@types/express": "^4.17.6", "azure-devops-node-api": "^11.0.1", "express": "^4.17.1", @@ -31,7 +31,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.10.1", + "@backstage/cli": "^0.10.2", "@types/supertest": "^2.0.8", "supertest": "^4.0.2", "msw": "^0.35.0" diff --git a/plugins/azure-devops-backend/src/utils/azure-devops-utils.ts b/plugins/azure-devops-backend/src/utils/azure-devops-utils.ts index aa843e9c17..7a0d0a279e 100644 --- a/plugins/azure-devops-backend/src/utils/azure-devops-utils.ts +++ b/plugins/azure-devops-backend/src/utils/azure-devops-utils.ts @@ -215,6 +215,7 @@ function convertReviewer( return { id: identityRef.id, displayName: identityRef.displayName, + uniqueName: identityRef.uniqueName, imageUrl: getAvatarUrl(identityRef), isRequired: identityRef.isRequired, isContainer: identityRef.isContainer, diff --git a/plugins/azure-devops-common/CHANGELOG.md b/plugins/azure-devops-common/CHANGELOG.md index b2c8645e94..84c2cae9a5 100644 --- a/plugins/azure-devops-common/CHANGELOG.md +++ b/plugins/azure-devops-common/CHANGELOG.md @@ -1,5 +1,62 @@ # @backstage/plugin-azure-devops-common +## 0.1.3 + +### Patch Changes + +- daf32e2c9b: Created some initial filters that can be used to create pull request columns: + + - All + - AssignedToUser + - AssignedToCurrentUser + - AssignedToTeam + - AssignedToTeams + - AssignedToCurrentUsersTeams + - CreatedByUser + - CreatedByCurrentUser + - CreatedByTeam + - CreatedByTeams + - CreatedByCurrentUsersTeams + + Example custom column creation: + + ```tsx + const COLUMN_CONFIGS: PullRequestColumnConfig[] = [ + { + title: 'Created by me', + filters: [{ type: FilterType.CreatedByCurrentUser }], + }, + { + title: 'Created by Backstage Core', + filters: [ + { + type: FilterType.CreatedByTeam, + teamName: 'Backstage Core', + }, + ], + }, + { + title: 'Assigned to my teams', + filters: [{ type: FilterType.AssignedToCurrentUsersTeams }], + }, + { + title: 'Other PRs', + filters: [{ type: FilterType.All }], + simplified: true, + }, + ]; + + + } + />; + ``` + ## 0.1.2 ### Patch Changes diff --git a/plugins/azure-devops-common/api-report.md b/plugins/azure-devops-common/api-report.md index bb458f169a..85a9e54847 100644 --- a/plugins/azure-devops-common/api-report.md +++ b/plugins/azure-devops-common/api-report.md @@ -61,6 +61,10 @@ export interface CreatedBy { // (undocumented) imageUrl?: string; // (undocumented) + teamIds?: string[]; + // (undocumented) + teamNames?: string[]; + // (undocumented) uniqueName?: string; } @@ -254,6 +258,8 @@ export interface Reviewer { // (undocumented) isRequired?: boolean; // (undocumented) + uniqueName?: string; + // (undocumented) voteStatus: PullRequestVoteStatus; } diff --git a/plugins/azure-devops-common/package.json b/plugins/azure-devops-common/package.json index 7b82296c40..f8a9622bff 100644 --- a/plugins/azure-devops-common/package.json +++ b/plugins/azure-devops-common/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-azure-devops-common", - "version": "0.1.2", + "version": "0.1.3", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -29,7 +29,7 @@ "clean": "backstage-cli clean" }, "devDependencies": { - "@backstage/cli": "^0.10.1" + "@backstage/cli": "^0.10.2" }, "files": [ "dist" diff --git a/plugins/azure-devops-common/src/types.ts b/plugins/azure-devops-common/src/types.ts index e6bd174079..0befcf0b60 100644 --- a/plugins/azure-devops-common/src/types.ts +++ b/plugins/azure-devops-common/src/types.ts @@ -145,6 +145,7 @@ export interface DashboardPullRequest { export interface Reviewer { id?: string; displayName?: string; + uniqueName?: string; imageUrl?: string; isRequired?: boolean; isContainer?: boolean; @@ -164,6 +165,8 @@ export interface CreatedBy { displayName?: string; uniqueName?: string; imageUrl?: string; + teamIds?: string[]; + teamNames?: string[]; } export interface Repository { diff --git a/plugins/azure-devops/CHANGELOG.md b/plugins/azure-devops/CHANGELOG.md index 57f80f508b..c79c3abdbc 100644 --- a/plugins/azure-devops/CHANGELOG.md +++ b/plugins/azure-devops/CHANGELOG.md @@ -1,5 +1,69 @@ # @backstage/plugin-azure-devops +## 0.1.7 + +### Patch Changes + +- daf32e2c9b: Created some initial filters that can be used to create pull request columns: + + - All + - AssignedToUser + - AssignedToCurrentUser + - AssignedToTeam + - AssignedToTeams + - AssignedToCurrentUsersTeams + - CreatedByUser + - CreatedByCurrentUser + - CreatedByTeam + - CreatedByTeams + - CreatedByCurrentUsersTeams + + Example custom column creation: + + ```tsx + const COLUMN_CONFIGS: PullRequestColumnConfig[] = [ + { + title: 'Created by me', + filters: [{ type: FilterType.CreatedByCurrentUser }], + }, + { + title: 'Created by Backstage Core', + filters: [ + { + type: FilterType.CreatedByTeam, + teamName: 'Backstage Core', + }, + ], + }, + { + title: 'Assigned to my teams', + filters: [{ type: FilterType.AssignedToCurrentUsersTeams }], + }, + { + title: 'Other PRs', + filters: [{ type: FilterType.All }], + simplified: true, + }, + ]; + + + } + />; + ``` + +- Updated dependencies + - @backstage/core-plugin-api@0.3.1 + - @backstage/core-components@0.8.1 + - @backstage/plugin-azure-devops-common@0.1.3 + - @backstage/catalog-model@0.9.8 + - @backstage/plugin-catalog-react@0.6.7 + ## 0.1.6 ### Patch Changes diff --git a/plugins/azure-devops/api-report.md b/plugins/azure-devops/api-report.md index 0f3e3310ef..11e238e6f5 100644 --- a/plugins/azure-devops/api-report.md +++ b/plugins/azure-devops/api-report.md @@ -6,9 +6,55 @@ /// import { BackstagePlugin } from '@backstage/core-plugin-api'; +import { DashboardPullRequest } from '@backstage/plugin-azure-devops-common'; import { Entity } from '@backstage/catalog-model'; import { SvgIconProps } from '@material-ui/core'; +// Warning: (ae-missing-release-tag) "AllFilter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type AllFilter = BaseFilter & { + type: FilterType.All; +}; + +// Warning: (ae-missing-release-tag) "AssignedToTeamFilter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type AssignedToTeamFilter = BaseFilter & { + type: FilterType.AssignedToTeam; + teamId: string; +}; + +// Warning: (ae-missing-release-tag) "AssignedToTeamsFilter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type AssignedToTeamsFilter = BaseFilter & + ( + | { + type: FilterType.AssignedToTeams; + teamIds: string[]; + } + | { + type: FilterType.AssignedToCurrentUsersTeams; + teamIds?: string[]; + } + ); + +// Warning: (ae-missing-release-tag) "AssignedToUserFilter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type AssignedToUserFilter = BaseFilter & + ( + | { + type: FilterType.AssignedToUser; + email: string; + } + | { + type: FilterType.AssignedToCurrentUser; + email?: string; + } + ); + // 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) @@ -25,11 +71,71 @@ export const AzurePullRequestsIcon: (props: SvgIconProps) => JSX.Element; export const AzurePullRequestsPage: ({ projectName, pollingInterval, + defaultColumnConfigs, }: { projectName?: string | undefined; pollingInterval?: number | undefined; + defaultColumnConfigs?: PullRequestColumnConfig[] | undefined; }) => JSX.Element; +// Warning: (ae-missing-release-tag) "BaseFilter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type BaseFilter = { + type: FilterType; +}; + +// Warning: (ae-missing-release-tag) "CreatedByTeamFilter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type CreatedByTeamFilter = BaseFilter & + ({ + type: FilterType.CreatedByTeam; + } & ( + | { + teamId: string; + } + | { + teamName: string; + } + )); + +// Warning: (ae-missing-release-tag) "CreatedByTeamsFilter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type CreatedByTeamsFilter = BaseFilter & + ( + | ({ + type: FilterType.CreatedByTeams; + } & ( + | { + teamIds: string[]; + } + | { + teamNames: string[]; + } + )) + | { + type: FilterType.CreatedByCurrentUsersTeams; + teamIds?: string[]; + } + ); + +// Warning: (ae-missing-release-tag) "CreatedByUserFilter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type CreatedByUserFilter = BaseFilter & + ( + | { + type: FilterType.CreatedByUser; + email: string; + } + | { + type: FilterType.CreatedByCurrentUser; + email?: string; + } + ); + // Warning: (ae-missing-release-tag) "EntityAzurePipelinesContent" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -48,10 +154,67 @@ export const EntityAzurePullRequestsContent: ({ defaultLimit?: number | undefined; }) => JSX.Element; +// Warning: (ae-missing-release-tag) "Filter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type Filter = + | AssignedToUserFilter + | CreatedByUserFilter + | AssignedToTeamFilter + | CreatedByTeamFilter + | AssignedToTeamsFilter + | CreatedByTeamsFilter + | AllFilter; + +// Warning: (ae-missing-release-tag) "FilterType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export enum FilterType { + // (undocumented) + All = 'All', + // (undocumented) + AssignedToCurrentUser = 'AssignedToCurrentUser', + // (undocumented) + AssignedToCurrentUsersTeams = 'AssignedToCurrentUsersTeams', + // (undocumented) + AssignedToTeam = 'AssignedToTeam', + // (undocumented) + AssignedToTeams = 'AssignedToTeams', + // (undocumented) + AssignedToUser = 'AssignedToUser', + // (undocumented) + CreatedByCurrentUser = 'CreatedByCurrentUser', + // (undocumented) + CreatedByCurrentUsersTeams = 'CreatedByCurrentUsersTeams', + // (undocumented) + CreatedByTeam = 'CreatedByTeam', + // (undocumented) + CreatedByTeams = 'CreatedByTeams', + // (undocumented) + CreatedByUser = 'CreatedByUser', +} + // Warning: (ae-missing-release-tag) "isAzureDevOpsAvailable" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) export const isAzureDevOpsAvailable: (entity: Entity) => boolean; +// Warning: (ae-missing-release-tag) "PullRequestColumnConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface PullRequestColumnConfig { + // (undocumented) + filters: Filter[]; + // (undocumented) + simplified?: boolean; + // (undocumented) + title: string; +} + +// Warning: (ae-missing-release-tag) "PullRequestFilter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type PullRequestFilter = (pullRequest: DashboardPullRequest) => boolean; + // (No @packageDocumentation comment for this package) ``` diff --git a/plugins/azure-devops/package.json b/plugins/azure-devops/package.json index a37259739f..5d048c6471 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.6", + "version": "0.1.7", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -27,12 +27,12 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.9.7", - "@backstage/core-components": "^0.8.0", - "@backstage/core-plugin-api": "^0.3.0", + "@backstage/catalog-model": "^0.9.8", + "@backstage/core-components": "^0.8.1", + "@backstage/core-plugin-api": "^0.3.1", "@backstage/errors": "^0.1.4", - "@backstage/plugin-azure-devops-common": "^0.1.2", - "@backstage/plugin-catalog-react": "^0.6.5", + "@backstage/plugin-azure-devops-common": "^0.1.3", + "@backstage/plugin-catalog-react": "^0.6.7", "@backstage/theme": "^0.2.14", "@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.10.1", - "@backstage/core-app-api": "^0.2.0", + "@backstage/cli": "^0.10.2", + "@backstage/core-app-api": "^0.2.1", "@backstage/dev-utils": "^0.2.14", "@backstage/test-utils": "^0.1.24", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/azure-devops/src/components/PullRequestsPage/PullRequestsPage.tsx b/plugins/azure-devops/src/components/PullRequestsPage/PullRequestsPage.tsx index 9f21c4b81b..158c4bca5b 100644 --- a/plugins/azure-devops/src/components/PullRequestsPage/PullRequestsPage.tsx +++ b/plugins/azure-devops/src/components/PullRequestsPage/PullRequestsPage.tsx @@ -21,56 +21,17 @@ import { Progress, ResponseErrorPanel, } from '@backstage/core-components'; -import { PullRequestGroup, PullRequestGroupConfig } from './lib/types'; -import React, { useEffect, useState } from 'react'; -import { getCreatedByUserFilter, getPullRequestGroups } from './lib/utils'; -import { useDashboardPullRequests, useUserEmail } from '../../hooks'; +import { PullRequestColumnConfig, PullRequestGroup } from './lib/types'; +import React, { useState } from 'react'; +import { getPullRequestGroupConfigs, getPullRequestGroups } from './lib/utils'; -import { DashboardPullRequest } from '@backstage/plugin-azure-devops-common'; +import { FilterType } from './lib/filters'; import { PullRequestGrid } from './lib/PullRequestGrid'; - -function usePullRequestGroupConfigs( - userEmail: string | undefined, -): PullRequestGroupConfig[] { - const [pullRequestGroupConfigs, setPullRequestGroupConfigs] = useState< - PullRequestGroupConfig[] - >([]); - - useEffect(() => { - const prGroupConfigs: PullRequestGroupConfig[] = [ - { title: 'Created by me', filter: getCreatedByUserFilter(userEmail) }, - { title: 'Other PRs', filter: _ => true, simplified: false }, - ]; - - setPullRequestGroupConfigs(prGroupConfigs); - }, [userEmail]); - - return pullRequestGroupConfigs; -} - -function usePullRequestGroups( - pullRequests: DashboardPullRequest[] | undefined, - pullRequestGroupConfigs: PullRequestGroupConfig[], -): PullRequestGroup[] { - const [pullRequestGroups, setPullRequestGroups] = useState< - PullRequestGroup[] - >([]); - - useEffect(() => { - if (pullRequests) { - const groups = getPullRequestGroups( - pullRequests, - pullRequestGroupConfigs, - ); - setPullRequestGroups(groups); - } - }, [pullRequests, pullRequestGroupConfigs]); - - return pullRequestGroups; -} +import { useDashboardPullRequests } from '../../hooks'; +import { useFilterProcessor } from './lib/hooks'; type PullRequestsPageContentProps = { - pullRequestGroups: PullRequestGroup[]; + pullRequestGroups: PullRequestGroup[] | undefined; loading: boolean; error?: Error; }; @@ -80,7 +41,7 @@ const PullRequestsPageContent = ({ loading, error, }: PullRequestsPageContentProps) => { - if (loading && pullRequestGroups.length <= 0) { + if (loading && (!pullRequestGroups || pullRequestGroups.length <= 0)) { return ; } @@ -88,25 +49,50 @@ const PullRequestsPageContent = ({ return ; } - return ; + return ; }; +const DEFAULT_COLUMN_CONFIGS: PullRequestColumnConfig[] = [ + { + title: 'Created by me', + filters: [{ type: FilterType.CreatedByCurrentUser }], + simplified: false, + }, + { + title: 'Other PRs', + filters: [{ type: FilterType.All }], + simplified: true, + }, +]; + type PullRequestsPageProps = { projectName?: string; pollingInterval?: number; + defaultColumnConfigs?: PullRequestColumnConfig[]; }; export const PullRequestsPage = ({ projectName, pollingInterval, + defaultColumnConfigs, }: PullRequestsPageProps) => { const { pullRequests, loading, error } = useDashboardPullRequests( projectName, pollingInterval, ); - const userEmail = useUserEmail(); - const pullRequestGroupConfigs = usePullRequestGroupConfigs(userEmail); - const pullRequestGroups = usePullRequestGroups( + + const [columnConfigs] = useState( + defaultColumnConfigs ?? DEFAULT_COLUMN_CONFIGS, + ); + + const filterProcessor = useFilterProcessor(); + + const pullRequestGroupConfigs = getPullRequestGroupConfigs( + columnConfigs, + filterProcessor, + ); + + const pullRequestGroups = getPullRequestGroups( pullRequests, pullRequestGroupConfigs, ); diff --git a/plugins/azure-devops/src/components/PullRequestsPage/index.ts b/plugins/azure-devops/src/components/PullRequestsPage/index.ts index e8b6cfa6bb..4061c72a8e 100644 --- a/plugins/azure-devops/src/components/PullRequestsPage/index.ts +++ b/plugins/azure-devops/src/components/PullRequestsPage/index.ts @@ -15,3 +15,17 @@ */ export { PullRequestsPage } from './PullRequestsPage'; +export type { PullRequestColumnConfig } from './lib/types'; +export { FilterType } from './lib/filters'; +export type { + BaseFilter, + Filter, + PullRequestFilter, + AssignedToUserFilter, + CreatedByUserFilter, + AssignedToTeamFilter, + CreatedByTeamFilter, + AssignedToTeamsFilter, + CreatedByTeamsFilter, + AllFilter, +} from './lib/filters'; diff --git a/plugins/azure-devops/src/components/PullRequestsPage/lib/filters/allFilter.ts b/plugins/azure-devops/src/components/PullRequestsPage/lib/filters/allFilter.ts new file mode 100644 index 0000000000..90ec822ef6 --- /dev/null +++ b/plugins/azure-devops/src/components/PullRequestsPage/lib/filters/allFilter.ts @@ -0,0 +1,27 @@ +/* + * 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 { BaseFilter, FilterType, PullRequestFilter } from './types'; + +import { DashboardPullRequest } from '@backstage/plugin-azure-devops-common'; + +export type AllFilter = BaseFilter & { + type: FilterType.All; +}; + +export function createAllFilter(): PullRequestFilter { + return (_pullRequest: DashboardPullRequest): boolean => true; +} diff --git a/plugins/azure-devops/src/components/PullRequestsPage/lib/filters/assignedToTeamFilter.ts b/plugins/azure-devops/src/components/PullRequestsPage/lib/filters/assignedToTeamFilter.ts new file mode 100644 index 0000000000..8e816db2c3 --- /dev/null +++ b/plugins/azure-devops/src/components/PullRequestsPage/lib/filters/assignedToTeamFilter.ts @@ -0,0 +1,39 @@ +/* + * 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 { BaseFilter, FilterType, PullRequestFilter } from './types'; + +import { DashboardPullRequest } from '@backstage/plugin-azure-devops-common'; +import { stringArrayHas } from '../../../../utils'; + +export type AssignedToTeamFilter = BaseFilter & { + type: FilterType.AssignedToTeam; + teamId: string; +}; + +export function createAssignedToTeamFilter( + filter: AssignedToTeamFilter, +): PullRequestFilter { + return (pullRequest: DashboardPullRequest): boolean => { + const reviewerIds = pullRequest.reviewers?.map(reviewer => reviewer.id); + + if (!reviewerIds) { + return false; + } + + return stringArrayHas(reviewerIds, filter.teamId, true); + }; +} diff --git a/plugins/azure-devops/src/components/PullRequestsPage/lib/filters/assignedToTeamsFilter.ts b/plugins/azure-devops/src/components/PullRequestsPage/lib/filters/assignedToTeamsFilter.ts new file mode 100644 index 0000000000..ce5e5bcc11 --- /dev/null +++ b/plugins/azure-devops/src/components/PullRequestsPage/lib/filters/assignedToTeamsFilter.ts @@ -0,0 +1,51 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { BaseFilter, FilterType, PullRequestFilter } from './types'; + +import { DashboardPullRequest } from '@backstage/plugin-azure-devops-common'; +import { createAssignedToTeamFilter } from './assignedToTeamFilter'; + +export type AssignedToTeamsFilter = BaseFilter & + ( + | { + type: FilterType.AssignedToTeams; + teamIds: string[]; + } + | { + type: FilterType.AssignedToCurrentUsersTeams; + teamIds?: string[]; + } + ); + +export function createAssignedToTeamsFilter( + filter: AssignedToTeamsFilter, +): PullRequestFilter { + const teamIds = filter.teamIds; + + return (pullRequest: DashboardPullRequest): boolean => { + if (!teamIds) { + return false; + } + + return teamIds.some(teamId => { + return createAssignedToTeamFilter({ + type: FilterType.AssignedToTeam, + teamId, + })(pullRequest); + }); + }; +} diff --git a/plugins/azure-devops/src/components/PullRequestsPage/lib/filters/assignedToUserFilter.ts b/plugins/azure-devops/src/components/PullRequestsPage/lib/filters/assignedToUserFilter.ts new file mode 100644 index 0000000000..95ee3d5a59 --- /dev/null +++ b/plugins/azure-devops/src/components/PullRequestsPage/lib/filters/assignedToUserFilter.ts @@ -0,0 +1,50 @@ +/* + * 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 { BaseFilter, FilterType, PullRequestFilter } from './types'; + +import { DashboardPullRequest } from '@backstage/plugin-azure-devops-common'; +import { stringArrayHas } from '../../../../utils'; + +export type AssignedToUserFilter = BaseFilter & + ( + | { + type: FilterType.AssignedToUser; + email: string; + } + | { + type: FilterType.AssignedToCurrentUser; + email?: string; + } + ); + +export function createAssignedToUserFilter( + filter: AssignedToUserFilter, +): PullRequestFilter { + const email = filter.email; + + return (pullRequest: DashboardPullRequest): boolean => { + const uniqueNames = pullRequest.reviewers?.map( + reviewer => reviewer.uniqueName, + ); + + if (!email || !uniqueNames) { + return false; + } + + return stringArrayHas(uniqueNames, email, true); + }; +} diff --git a/plugins/azure-devops/src/components/PullRequestsPage/lib/filters/createFilter.test.ts b/plugins/azure-devops/src/components/PullRequestsPage/lib/filters/createFilter.test.ts new file mode 100644 index 0000000000..b0ac4d402b --- /dev/null +++ b/plugins/azure-devops/src/components/PullRequestsPage/lib/filters/createFilter.test.ts @@ -0,0 +1,150 @@ +/* + * 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 { Filter, FilterType } from './types'; + +import { DashboardPullRequest } from '@backstage/plugin-azure-devops-common'; +import { createFilter } from './createFilter'; + +describe('createFilter', () => { + const pullRequest = { + createdBy: { + uniqueName: 'user1@backstage.com', + teamIds: ['team1Id', 'team2Id'], + }, + reviewers: [ + { uniqueName: 'user2@backstage.com' }, + { id: 'team2Id' }, + { id: 'team3Id' }, + ], + } as DashboardPullRequest; + + const testCases: Array<{ filter: Filter; result: boolean }> = [ + { + filter: { + type: FilterType.AssignedToUser, + email: 'user2@backstage.com', + }, + result: true, + }, + { + filter: { + type: FilterType.AssignedToUser, + email: 'random-user@backstage.com', + }, + result: false, + }, + { + filter: { + type: FilterType.CreatedByUser, + email: 'user1@backstage.com', + }, + result: true, + }, + { + filter: { + type: FilterType.CreatedByUser, + email: 'random-user@backstage.com', + }, + result: false, + }, + { + filter: { + type: FilterType.AssignedToTeam, + teamId: 'team2Id', + }, + result: true, + }, + { + filter: { + type: FilterType.AssignedToTeam, + teamId: 'randomTeamId', + }, + result: false, + }, + { + filter: { + type: FilterType.CreatedByTeam, + teamId: 'team1Id', + }, + result: true, + }, + { + filter: { + type: FilterType.CreatedByTeam, + teamId: 'randomTeamId', + }, + result: false, + }, + { + filter: { + type: FilterType.AssignedToTeams, + teamIds: ['team2Id', 'randomTeamId'], + }, + result: true, + }, + { + filter: { + type: FilterType.AssignedToTeams, + teamIds: ['team2Id', 'team3Id'], + }, + result: true, + }, + { + filter: { + type: FilterType.AssignedToTeams, + teamIds: ['randomTeam1Id', 'randomTeam2Id'], + }, + result: false, + }, + { + filter: { + type: FilterType.CreatedByTeams, + teamIds: ['team1Id', 'randomTeamId'], + }, + result: true, + }, + { + filter: { + type: FilterType.CreatedByTeams, + teamIds: ['team1Id', 'team2Id'], + }, + result: true, + }, + { + filter: { + type: FilterType.CreatedByTeams, + teamIds: ['randomTeam1Id', 'randomTeam2Id'], + }, + result: false, + }, + { + filter: { + type: FilterType.All, + }, + result: true, + }, + ]; + + testCases.forEach(({ filter, result }) => { + it(`should return ${String(result)} when pull request ${ + result ? 'is' : 'is not' + } ${filter.type}`, () => { + const pullRequestFilter = createFilter(filter); + expect(pullRequestFilter(pullRequest)).toBe(result); + }); + }); +}); diff --git a/plugins/azure-devops/src/components/PullRequestsPage/lib/filters/createFilter.ts b/plugins/azure-devops/src/components/PullRequestsPage/lib/filters/createFilter.ts new file mode 100644 index 0000000000..492d0539c5 --- /dev/null +++ b/plugins/azure-devops/src/components/PullRequestsPage/lib/filters/createFilter.ts @@ -0,0 +1,71 @@ +/* + * 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 { Filter, FilterType, PullRequestFilter } from './types'; + +import { DashboardPullRequest } from '@backstage/plugin-azure-devops-common'; +import { createAllFilter } from './allFilter'; +import { createAssignedToTeamFilter } from './assignedToTeamFilter'; +import { createAssignedToTeamsFilter } from './assignedToTeamsFilter'; +import { createAssignedToUserFilter } from './assignedToUserFilter'; +import { createCreatedByTeamFilter } from './createdByTeamFilter'; +import { createCreatedByTeamsFilter } from './createdByTeamsFilter'; +import { createCreatedByUserFilter } from './createdByUserFilter'; + +export function createFilter(filters: Filter | Filter[]): PullRequestFilter { + const mapFilter = (filter: Filter): PullRequestFilter => { + switch (filter.type) { + case FilterType.AssignedToUser: + case FilterType.AssignedToCurrentUser: + return createAssignedToUserFilter(filter); + + case FilterType.CreatedByUser: + case FilterType.CreatedByCurrentUser: + return createCreatedByUserFilter(filter); + + case FilterType.AssignedToTeam: + return createAssignedToTeamFilter(filter); + + case FilterType.CreatedByTeam: + return createCreatedByTeamFilter(filter); + + case FilterType.AssignedToTeams: + case FilterType.AssignedToCurrentUsersTeams: + return createAssignedToTeamsFilter(filter); + + case FilterType.CreatedByTeams: + case FilterType.CreatedByCurrentUsersTeams: + return createCreatedByTeamsFilter(filter); + + case FilterType.All: + return createAllFilter(); + + default: + return _ => false; + } + }; + + if (Array.isArray(filters)) { + if (filters.length === 1) { + return mapFilter(filters[0]); + } + + return (pullRequest: DashboardPullRequest): boolean => + filters.every(filter => mapFilter(filter)(pullRequest)); + } + + return mapFilter(filters); +} diff --git a/plugins/azure-devops/src/components/PullRequestsPage/lib/filters/createdByTeamFilter.ts b/plugins/azure-devops/src/components/PullRequestsPage/lib/filters/createdByTeamFilter.ts new file mode 100644 index 0000000000..70a388b540 --- /dev/null +++ b/plugins/azure-devops/src/components/PullRequestsPage/lib/filters/createdByTeamFilter.ts @@ -0,0 +1,42 @@ +/* + * 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 { BaseFilter, FilterType, PullRequestFilter } from './types'; + +import { DashboardPullRequest } from '@backstage/plugin-azure-devops-common'; +import { stringArrayHas } from '../../../../utils'; + +export type CreatedByTeamFilter = BaseFilter & + ({ + type: FilterType.CreatedByTeam; + } & ({ teamId: string } | { teamName: string })); + +export function createCreatedByTeamFilter( + filter: CreatedByTeamFilter, +): PullRequestFilter { + return (pullRequest: DashboardPullRequest): boolean => { + const [createdByTeams, team] = + 'teamId' in filter + ? [pullRequest.createdBy?.teamIds, filter.teamId] + : [pullRequest.createdBy?.teamNames, filter.teamName]; + + if (!createdByTeams) { + return false; + } + + return stringArrayHas(createdByTeams, team, true); + }; +} diff --git a/plugins/azure-devops/src/components/PullRequestsPage/lib/filters/createdByTeamsFilter.ts b/plugins/azure-devops/src/components/PullRequestsPage/lib/filters/createdByTeamsFilter.ts new file mode 100644 index 0000000000..34ed81db5d --- /dev/null +++ b/plugins/azure-devops/src/components/PullRequestsPage/lib/filters/createdByTeamsFilter.ts @@ -0,0 +1,65 @@ +/* + * 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 { BaseFilter, FilterType, PullRequestFilter } from './types'; + +import { DashboardPullRequest } from '@backstage/plugin-azure-devops-common'; +import { createCreatedByTeamFilter } from './createdByTeamFilter'; + +export type CreatedByTeamsFilter = BaseFilter & + ( + | ({ + type: FilterType.CreatedByTeams; + } & ({ teamIds: string[] } | { teamNames: string[] })) + | { + type: FilterType.CreatedByCurrentUsersTeams; + teamIds?: string[]; + } + ); + +export function createCreatedByTeamsFilter( + filter: CreatedByTeamsFilter, +): PullRequestFilter { + return (pullRequest: DashboardPullRequest): boolean => { + if ('teamNames' in filter) { + const teamNames = filter.teamNames; + + if (!teamNames) { + return false; + } + + return teamNames.some(teamName => { + return createCreatedByTeamFilter({ + type: FilterType.CreatedByTeam, + teamName, + })(pullRequest); + }); + } + + const teamIds = filter.teamIds; + + if (!teamIds) { + return false; + } + + return teamIds.some(teamId => { + return createCreatedByTeamFilter({ + type: FilterType.CreatedByTeam, + teamId, + })(pullRequest); + }); + }; +} diff --git a/plugins/azure-devops/src/components/PullRequestsPage/lib/filters/createdByUserFilter.ts b/plugins/azure-devops/src/components/PullRequestsPage/lib/filters/createdByUserFilter.ts new file mode 100644 index 0000000000..69d4f23db5 --- /dev/null +++ b/plugins/azure-devops/src/components/PullRequestsPage/lib/filters/createdByUserFilter.ts @@ -0,0 +1,48 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { BaseFilter, FilterType, PullRequestFilter } from './types'; + +import { DashboardPullRequest } from '@backstage/plugin-azure-devops-common'; +import { equalsIgnoreCase } from '../../../../utils'; + +export type CreatedByUserFilter = BaseFilter & + ( + | { + type: FilterType.CreatedByUser; + email: string; + } + | { + type: FilterType.CreatedByCurrentUser; + email?: string; + } + ); + +export function createCreatedByUserFilter( + filter: CreatedByUserFilter, +): PullRequestFilter { + const email = filter.email; + + return (pullRequest: DashboardPullRequest): boolean => { + const uniqueName = pullRequest.createdBy?.uniqueName; + + if (!email || !uniqueName) { + return false; + } + + return equalsIgnoreCase(email, uniqueName); + }; +} diff --git a/plugins/azure-devops/src/components/PullRequestsPage/lib/filters/index.ts b/plugins/azure-devops/src/components/PullRequestsPage/lib/filters/index.ts new file mode 100644 index 0000000000..df165dc9ca --- /dev/null +++ b/plugins/azure-devops/src/components/PullRequestsPage/lib/filters/index.ts @@ -0,0 +1,30 @@ +/* + * 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 { createFilter } from './createFilter'; +export { FilterTypes, FilterType } from './types'; +export type { + BaseFilter, + Filter, + PullRequestFilter, + AssignedToUserFilter, + CreatedByUserFilter, + AssignedToTeamFilter, + CreatedByTeamFilter, + AssignedToTeamsFilter, + CreatedByTeamsFilter, + AllFilter, +} from './types'; diff --git a/plugins/azure-devops/src/components/PullRequestsPage/lib/filters/types.ts b/plugins/azure-devops/src/components/PullRequestsPage/lib/filters/types.ts new file mode 100644 index 0000000000..bc4643d51b --- /dev/null +++ b/plugins/azure-devops/src/components/PullRequestsPage/lib/filters/types.ts @@ -0,0 +1,83 @@ +/* + * 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 { AllFilter } from './allFilter'; +import { AssignedToTeamFilter } from './assignedToTeamFilter'; +import { AssignedToTeamsFilter } from './assignedToTeamsFilter'; +import { AssignedToUserFilter } from './assignedToUserFilter'; +import { CreatedByTeamFilter } from './createdByTeamFilter'; +import { CreatedByTeamsFilter } from './createdByTeamsFilter'; +import { CreatedByUserFilter } from './createdByUserFilter'; +import { DashboardPullRequest } from '@backstage/plugin-azure-devops-common'; + +export enum FilterType { + All = 'All', + + // Assigned To + AssignedToUser = 'AssignedToUser', + AssignedToCurrentUser = 'AssignedToCurrentUser', + AssignedToTeam = 'AssignedToTeam', + AssignedToTeams = 'AssignedToTeams', + AssignedToCurrentUsersTeams = 'AssignedToCurrentUsersTeams', + + // Created By + CreatedByUser = 'CreatedByUser', + CreatedByCurrentUser = 'CreatedByCurrentUser', + CreatedByTeam = 'CreatedByTeam', + CreatedByTeams = 'CreatedByTeams', + CreatedByCurrentUsersTeams = 'CreatedByCurrentUsersTeams', +} + +export const FilterTypes = [ + FilterType.All, + + FilterType.AssignedToUser, + FilterType.AssignedToCurrentUser, + FilterType.AssignedToTeam, + FilterType.AssignedToTeams, + FilterType.AssignedToCurrentUsersTeams, + + FilterType.CreatedByUser, + FilterType.CreatedByCurrentUser, + FilterType.CreatedByTeam, + FilterType.CreatedByTeams, + FilterType.CreatedByCurrentUsersTeams, +] as const; + +export type BaseFilter = { + type: FilterType; +}; + +export type Filter = + | AssignedToUserFilter + | CreatedByUserFilter + | AssignedToTeamFilter + | CreatedByTeamFilter + | AssignedToTeamsFilter + | CreatedByTeamsFilter + | AllFilter; + +export type { + AssignedToUserFilter, + CreatedByUserFilter, + AssignedToTeamFilter, + CreatedByTeamFilter, + AssignedToTeamsFilter, + CreatedByTeamsFilter, + AllFilter, +}; + +export type PullRequestFilter = (pullRequest: DashboardPullRequest) => boolean; diff --git a/plugins/azure-devops/src/components/PullRequestsPage/lib/hooks/index.ts b/plugins/azure-devops/src/components/PullRequestsPage/lib/hooks/index.ts new file mode 100644 index 0000000000..2e87dfea0c --- /dev/null +++ b/plugins/azure-devops/src/components/PullRequestsPage/lib/hooks/index.ts @@ -0,0 +1,17 @@ +/* + * 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 * from './useFilterProcessor'; diff --git a/plugins/azure-devops/src/components/PullRequestsPage/lib/hooks/useFilterProcessor.ts b/plugins/azure-devops/src/components/PullRequestsPage/lib/hooks/useFilterProcessor.ts new file mode 100644 index 0000000000..546b23430d --- /dev/null +++ b/plugins/azure-devops/src/components/PullRequestsPage/lib/hooks/useFilterProcessor.ts @@ -0,0 +1,39 @@ +/* + * 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 { Filter, FilterType } from '../filters'; + +import { useUserEmail } from '../../../../hooks'; + +export function useFilterProcessor(): (filters: Filter[]) => Filter[] { + const userEmail = useUserEmail(); + + return (filters: Filter[]): Filter[] => { + for (const filter of filters) { + switch (filter.type) { + case FilterType.AssignedToCurrentUser: + case FilterType.CreatedByCurrentUser: + filter.email = userEmail; + break; + + default: + break; + } + } + + return filters; + }; +} diff --git a/plugins/azure-devops/src/components/PullRequestsPage/lib/types.ts b/plugins/azure-devops/src/components/PullRequestsPage/lib/types.ts index 2d936f7b64..74c8ebe605 100644 --- a/plugins/azure-devops/src/components/PullRequestsPage/lib/types.ts +++ b/plugins/azure-devops/src/components/PullRequestsPage/lib/types.ts @@ -14,23 +14,24 @@ * limitations under the License. */ -import { - DashboardPullRequest, - Team, -} from '@backstage/plugin-azure-devops-common'; +import { Filter, PullRequestFilter } from './filters'; -export interface PullRequestGroup { +import { DashboardPullRequest } from '@backstage/plugin-azure-devops-common'; + +export interface PullRequestColumnConfig { title: string; - pullRequests: DashboardPullRequest[]; + filters: Filter[]; simplified?: boolean; } -export type PullRequestFilter = (pullRequest: DashboardPullRequest) => boolean; - -export type TeamFilter = (team: Team) => boolean; - export interface PullRequestGroupConfig { title: string; filter: PullRequestFilter; simplified?: boolean; } + +export interface PullRequestGroup { + title: string; + pullRequests: DashboardPullRequest[]; + simplified?: boolean; +} diff --git a/plugins/azure-devops/src/components/PullRequestsPage/lib/utils.test.ts b/plugins/azure-devops/src/components/PullRequestsPage/lib/utils.test.ts index da27b2e06e..d128cbd1db 100644 --- a/plugins/azure-devops/src/components/PullRequestsPage/lib/utils.test.ts +++ b/plugins/azure-devops/src/components/PullRequestsPage/lib/utils.test.ts @@ -19,33 +19,7 @@ import { PullRequestVoteStatus, Reviewer, } from '@backstage/plugin-azure-devops-common'; -import { - arrayExtract, - getCreatedByUserFilter, - getPullRequestGroups, - reviewerFilter, -} from './utils'; - -describe('getCreatedByUserFilter', () => { - it('should filter if pull request is created by user', () => { - const userEmail = 'user1@backstage.com'; - const pr = { - createdBy: { uniqueName: userEmail }, - } as DashboardPullRequest; - const result = getCreatedByUserFilter(userEmail)(pr); - expect(result).toBe(true); - }); - - it('should not filter if pull request is not created by user', () => { - const userEmail1 = 'user1@backstage.com'; - const userEmail2 = 'user2@backstage.com'; - const pr = { - createdBy: { uniqueName: userEmail1 }, - } as DashboardPullRequest; - const result = getCreatedByUserFilter(userEmail2)(pr); - expect(result).toBe(false); - }); -}); +import { arrayExtract, getPullRequestGroups, reviewerFilter } from './utils'; describe('reviewerFilter', () => { it('should return false if reviewer has no vote and is not required', () => { @@ -128,11 +102,15 @@ describe('getPullRequestGroups', () => { ]; const configs = [ - { title: 'Created by me', filter: getCreatedByUserFilter(userEmail) }, + { + title: 'Created by me', + filter: (pullRequest: DashboardPullRequest): boolean => + pullRequest.createdBy?.uniqueName === userEmail, + }, { title: 'Other PRs', filter: (_: unknown) => true, simplified: true }, ]; - const result = getPullRequestGroups(pullRequests, configs); + const result = getPullRequestGroups(pullRequests, configs) ?? []; expect(result.length).toBe(2); diff --git a/plugins/azure-devops/src/components/PullRequestsPage/lib/utils.ts b/plugins/azure-devops/src/components/PullRequestsPage/lib/utils.ts index c9cf866010..c4fcdc9f7e 100644 --- a/plugins/azure-devops/src/components/PullRequestsPage/lib/utils.ts +++ b/plugins/azure-devops/src/components/PullRequestsPage/lib/utils.ts @@ -19,25 +19,13 @@ import { PullRequestVoteStatus, Reviewer, } from '@backstage/plugin-azure-devops-common'; +import { Filter, createFilter } from './filters'; import { - PullRequestFilter, + PullRequestColumnConfig, PullRequestGroup, PullRequestGroupConfig, } from './types'; -/** - * Creates a filter that matches pull requests created by `userEmail`. - * @param userEmail an email to filter by. - * @returns a filter for pull requests created by `userEmail`. - */ -export function getCreatedByUserFilter( - userEmail: string | undefined, -): PullRequestFilter { - return (pullRequest: DashboardPullRequest): boolean => - pullRequest.createdBy?.uniqueName?.toLocaleLowerCase() === - userEmail?.toLocaleLowerCase(); -} - /** * Filters a reviewer based on vote status and if the reviewer is required. * @param reviewer a reviewer to filter. @@ -97,9 +85,13 @@ export function arrayExtract(arr: T[], filter: (value: T) => unknown): T[] { * @returns a list of pull request groups. */ export function getPullRequestGroups( - pullRequests: DashboardPullRequest[], + pullRequests: DashboardPullRequest[] | undefined, configs: PullRequestGroupConfig[], -): PullRequestGroup[] { +): PullRequestGroup[] | undefined { + if (!pullRequests) { + return undefined; + } + const remainingPullRequests: DashboardPullRequest[] = [...pullRequests]; const pullRequestGroups: PullRequestGroup[] = []; @@ -115,3 +107,17 @@ export function getPullRequestGroups( return pullRequestGroups; } + +export function getPullRequestGroupConfigs( + columnConfigs: PullRequestColumnConfig[], + filterProcessor: (filters: Filter[]) => Filter[], +): PullRequestGroupConfig[] { + return columnConfigs.map(columnConfig => { + const filters = filterProcessor(columnConfig.filters); + return { + title: columnConfig.title, + filter: createFilter(filters), + simplified: columnConfig.simplified, + }; + }); +} diff --git a/plugins/azure-devops/src/index.ts b/plugins/azure-devops/src/index.ts index c9b804bc8d..8289a93a5c 100644 --- a/plugins/azure-devops/src/index.ts +++ b/plugins/azure-devops/src/index.ts @@ -23,3 +23,18 @@ export { } from './plugin'; export { AzurePullRequestsIcon } from './components/AzurePullRequestsIcon'; + +export { FilterType } from './components/PullRequestsPage'; +export type { + PullRequestColumnConfig, + BaseFilter, + Filter, + PullRequestFilter, + AssignedToUserFilter, + CreatedByUserFilter, + AssignedToTeamFilter, + CreatedByTeamFilter, + AssignedToTeamsFilter, + CreatedByTeamsFilter, + AllFilter, +} from './components/PullRequestsPage'; diff --git a/plugins/azure-devops/src/utils/arrayHas.ts b/plugins/azure-devops/src/utils/arrayHas.ts new file mode 100644 index 0000000000..8dac8b8ec9 --- /dev/null +++ b/plugins/azure-devops/src/utils/arrayHas.ts @@ -0,0 +1,34 @@ +/* + * 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 function arrayHas(arr: T[], value: T): boolean { + return new Set(arr).has(value); +} + +export function stringArrayHas( + arr: Array, + value: string | undefined, + ignoreCase: boolean = false, +): boolean { + if (ignoreCase) { + return arrayHas( + arr.map(a => a?.toLocaleLowerCase('en-US')), + value?.toLocaleLowerCase('en-US'), + ); + } + + return arrayHas(arr, value); +} diff --git a/plugins/azure-devops/src/utils/equalsIgnoreCase.ts b/plugins/azure-devops/src/utils/equalsIgnoreCase.ts new file mode 100644 index 0000000000..12b058e6ae --- /dev/null +++ b/plugins/azure-devops/src/utils/equalsIgnoreCase.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 function equalsIgnoreCase(str1: string, str2: string): boolean { + return str1.toLocaleLowerCase('en-US') === str2.toLocaleLowerCase('en-US'); +} diff --git a/plugins/azure-devops/src/utils/index.ts b/plugins/azure-devops/src/utils/index.ts new file mode 100644 index 0000000000..f2214e8106 --- /dev/null +++ b/plugins/azure-devops/src/utils/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 * from './arrayHas'; +export * from './equalsIgnoreCase'; +export * from './getDurationFromDates'; diff --git a/plugins/badges/package.json b/plugins/badges/package.json index 441f72c550..9d35a3b9e2 100644 --- a/plugins/badges/package.json +++ b/plugins/badges/package.json @@ -28,8 +28,8 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.7", - "@backstage/core-components": "^0.8.0", - "@backstage/core-plugin-api": "^0.3.0", + "@backstage/core-components": "^0.8.1", + "@backstage/core-plugin-api": "^0.3.1", "@backstage/errors": "^0.1.5", "@backstage/plugin-catalog-react": "^0.6.5", "@backstage/theme": "^0.2.14", @@ -43,8 +43,8 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.10.1", - "@backstage/core-app-api": "^0.2.0", + "@backstage/cli": "^0.10.2", + "@backstage/core-app-api": "^0.2.1", "@backstage/dev-utils": "^0.2.14", "@backstage/test-utils": "^0.1.24", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/bazaar/package.json b/plugins/bazaar/package.json index 3db79b9cdf..88da003778 100644 --- a/plugins/bazaar/package.json +++ b/plugins/bazaar/package.json @@ -23,8 +23,8 @@ "dependencies": { "@backstage/catalog-model": "^0.9.7", "@backstage/cli": "^0.10.1", - "@backstage/core-components": "^0.8.0", - "@backstage/core-plugin-api": "^0.3.0", + "@backstage/core-components": "^0.8.1", + "@backstage/core-plugin-api": "^0.3.1", "@backstage/plugin-catalog": "^0.7.4", "@backstage/plugin-catalog-react": "^0.6.5", "@date-io/luxon": "1.x", @@ -42,7 +42,7 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.10.1", + "@backstage/cli": "^0.10.2", "@backstage/dev-utils": "^0.2.14", "@testing-library/jest-dom": "^5.10.1", "cross-fetch": "^3.0.6" diff --git a/plugins/bitrise/package.json b/plugins/bitrise/package.json index 35a0a9e079..c4a02c7403 100644 --- a/plugins/bitrise/package.json +++ b/plugins/bitrise/package.json @@ -22,8 +22,8 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.7", - "@backstage/core-components": "^0.8.0", - "@backstage/core-plugin-api": "^0.3.0", + "@backstage/core-components": "^0.8.1", + "@backstage/core-plugin-api": "^0.3.1", "@backstage/plugin-catalog-react": "^0.6.5", "@backstage/theme": "^0.2.14", "@material-ui/core": "^4.12.2", @@ -40,8 +40,8 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.10.1", - "@backstage/core-app-api": "^0.2.0", + "@backstage/cli": "^0.10.2", + "@backstage/core-app-api": "^0.2.1", "@backstage/dev-utils": "^0.2.14", "@backstage/test-utils": "^0.1.24", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/catalog-backend/CHANGELOG.md b/plugins/catalog-backend/CHANGELOG.md index dc7fc50f76..ce502f8f46 100644 --- a/plugins/catalog-backend/CHANGELOG.md +++ b/plugins/catalog-backend/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-catalog-backend +## 0.19.2 + +### Patch Changes + +- 3368f27aef: Fixed the handling of optional locations so that the catalog no longer logs `NotFoundError`s for missing optional locations. +- Updated dependencies + - @backstage/backend-common@0.9.14 + - @backstage/catalog-model@0.9.8 + ## 0.19.1 ### Patch Changes diff --git a/plugins/catalog-backend/package.json b/plugins/catalog-backend/package.json index 6cfb73a3d1..d49723b7d2 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": "0.19.1", + "version": "0.19.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -30,9 +30,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.9.13", + "@backstage/backend-common": "^0.9.14", "@backstage/catalog-client": "^0.5.2", - "@backstage/catalog-model": "^0.9.7", + "@backstage/catalog-model": "^0.9.8", "@backstage/config": "^0.1.11", "@backstage/errors": "^0.1.5", "@backstage/integration": "^0.6.10", @@ -63,7 +63,7 @@ }, "devDependencies": { "@backstage/backend-test-utils": "^0.1.10", - "@backstage/cli": "^0.10.1", + "@backstage/cli": "^0.10.2", "@backstage/test-utils": "^0.1.24", "@types/core-js": "^2.5.4", "@types/git-url-parse": "^9.0.0", diff --git a/plugins/catalog-backend/src/ingestion/processors/AzureDevOpsDiscoveryProcessor.test.ts b/plugins/catalog-backend/src/ingestion/processors/AzureDevOpsDiscoveryProcessor.test.ts index ab053e8748..463d982ca2 100644 --- a/plugins/catalog-backend/src/ingestion/processors/AzureDevOpsDiscoveryProcessor.test.ts +++ b/plugins/catalog-backend/src/ingestion/processors/AzureDevOpsDiscoveryProcessor.test.ts @@ -165,6 +165,7 @@ describe('AzureDevOpsDiscoveryProcessor', () => { type: 'url', target: 'https://dev.azure.com/shopify/engineering/_git/backstage?path=/catalog-info.yaml', + presence: 'optional', }, optional: true, }); @@ -174,6 +175,7 @@ describe('AzureDevOpsDiscoveryProcessor', () => { type: 'url', target: 'https://dev.azure.com/shopify/engineering/_git/ios-app?path=/src/catalog-info.yaml', + presence: 'optional', }, optional: true, }); @@ -211,6 +213,7 @@ describe('AzureDevOpsDiscoveryProcessor', () => { type: 'url', target: 'https://dev.azure.com/shopify/engineering/_git/backstage?path=/catalog-info.yaml', + presence: 'optional', }, optional: true, }); @@ -249,6 +252,7 @@ describe('AzureDevOpsDiscoveryProcessor', () => { type: 'url', target: 'https://dev.azure.com/shopify/engineering/_git/backstage?path=/src/main/catalog.yaml', + presence: 'optional', }, optional: true, }); diff --git a/plugins/catalog-backend/src/ingestion/processors/AzureDevOpsDiscoveryProcessor.ts b/plugins/catalog-backend/src/ingestion/processors/AzureDevOpsDiscoveryProcessor.ts index 58ee2155e9..aeac64f54d 100644 --- a/plugins/catalog-backend/src/ingestion/processors/AzureDevOpsDiscoveryProcessor.ts +++ b/plugins/catalog-backend/src/ingestion/processors/AzureDevOpsDiscoveryProcessor.ts @@ -95,6 +95,7 @@ export class AzureDevOpsDiscoveryProcessor implements CatalogProcessor { { type: 'url', target: `${baseUrl}/${org}/${project}/_git/${file.repository.name}?path=${file.path}`, + presence: 'optional', }, // Not all locations may actually exist, since the user defined them as a wildcard pattern. // Thus, we emit them as optional and let the downstream processor find them while not outputting diff --git a/plugins/catalog-backend/src/ingestion/processors/BitbucketDiscoveryProcessor.test.ts b/plugins/catalog-backend/src/ingestion/processors/BitbucketDiscoveryProcessor.test.ts index 2a63605b0d..3f8d0ba0fb 100644 --- a/plugins/catalog-backend/src/ingestion/processors/BitbucketDiscoveryProcessor.test.ts +++ b/plugins/catalog-backend/src/ingestion/processors/BitbucketDiscoveryProcessor.test.ts @@ -208,6 +208,7 @@ describe('BitbucketDiscoveryProcessor', () => { type: 'url', target: 'https://bitbucket.mycompany.com/projects/backstage/repos/backstage/browse/catalog.yaml', + presence: 'optional', }, optional: true, }); @@ -217,6 +218,7 @@ describe('BitbucketDiscoveryProcessor', () => { type: 'url', target: 'https://bitbucket.mycompany.com/projects/demo/repos/demo/browse/catalog.yaml', + presence: 'optional', }, optional: true, }); @@ -242,6 +244,7 @@ describe('BitbucketDiscoveryProcessor', () => { type: 'url', target: 'https://bitbucket.mycompany.com/projects/backstage/repos/techdocs-cli/browse/catalog.yaml', + presence: 'optional', }, optional: true, }); @@ -263,6 +266,7 @@ describe('BitbucketDiscoveryProcessor', () => { type: 'url', target: 'https://bitbucket.mycompany.com/projects/backstage/repos/test/browse/catalog.yaml', + presence: 'optional', }, optional: true, }); @@ -291,6 +295,7 @@ describe('BitbucketDiscoveryProcessor', () => { type: 'url', target: 'https://bitbucket.mycompany.com/projects/backstage/repos/techdocs-cli/browse/catalog-info.yaml', + presence: 'optional', }, optional: true, }); @@ -334,6 +339,7 @@ describe('BitbucketDiscoveryProcessor', () => { type: 'url', target: 'https://bitbucket.org/myworkspace/repository-one/src/master/catalog-info.yaml', + presence: 'optional', }, optional: true, }); @@ -343,6 +349,7 @@ describe('BitbucketDiscoveryProcessor', () => { type: 'url', target: 'https://bitbucket.org/myworkspace/repository-two/src/master/catalog-info.yaml', + presence: 'optional', }, optional: true, }); @@ -370,6 +377,7 @@ describe('BitbucketDiscoveryProcessor', () => { type: 'url', target: 'https://bitbucket.org/myworkspace/repository-one/src/master/my/nested/path/catalog.yaml', + presence: 'optional', }, optional: true, }); @@ -379,6 +387,7 @@ describe('BitbucketDiscoveryProcessor', () => { type: 'url', target: 'https://bitbucket.org/myworkspace/repository-two/src/master/my/nested/path/catalog.yaml', + presence: 'optional', }, optional: true, }); @@ -406,6 +415,7 @@ describe('BitbucketDiscoveryProcessor', () => { type: 'url', target: 'https://bitbucket.org/myworkspace/repository-one/src/master/catalog.yaml', + presence: 'optional', }, optional: true, }); @@ -415,6 +425,7 @@ describe('BitbucketDiscoveryProcessor', () => { type: 'url', target: 'https://bitbucket.org/myworkspace/repository-two/src/master/catalog.yaml', + presence: 'optional', }, optional: true, }); @@ -441,6 +452,7 @@ describe('BitbucketDiscoveryProcessor', () => { type: 'url', target: 'https://bitbucket.org/myworkspace/repository-one/src/master/catalog.yaml', + presence: 'optional', }, optional: true, }); @@ -468,6 +480,7 @@ describe('BitbucketDiscoveryProcessor', () => { type: 'url', target: 'https://bitbucket.org/myworkspace/repository-three/src/master/catalog.yaml', + presence: 'optional', }, optional: true, }); @@ -493,6 +506,7 @@ describe('BitbucketDiscoveryProcessor', () => { type: 'url', target: 'https://bitbucket.org/myworkspace/repository-one/src/master/catalog-info.yaml', + presence: 'optional', }, optional: true, }); @@ -528,6 +542,7 @@ describe('BitbucketDiscoveryProcessor', () => { type: 'url', target: 'https://bitbucket.org/myworkspace/repository-one/src/master/catalog-info.yaml', + presence: 'optional', }, optional: true, }); diff --git a/plugins/catalog-backend/src/ingestion/processors/GitLabDiscoveryProcessor.test.ts b/plugins/catalog-backend/src/ingestion/processors/GitLabDiscoveryProcessor.test.ts index a0e44d7648..311de05673 100644 --- a/plugins/catalog-backend/src/ingestion/processors/GitLabDiscoveryProcessor.test.ts +++ b/plugins/catalog-backend/src/ingestion/processors/GitLabDiscoveryProcessor.test.ts @@ -201,6 +201,7 @@ describe('GitlabDiscoveryProcessor', () => { location: { type: 'url', target: 'https://gitlab.fake/1/-/blob/main/catalog-info.yaml', + presence: 'optional', }, optional: true, }, @@ -209,6 +210,7 @@ describe('GitlabDiscoveryProcessor', () => { location: { type: 'url', target: 'https://gitlab.fake/2/-/blob/master/catalog-info.yaml', + presence: 'optional', }, optional: true, }, @@ -246,6 +248,7 @@ describe('GitlabDiscoveryProcessor', () => { location: { type: 'url', target: 'https://gitlab.fake/1/-/blob/master/catalog-info.yaml', + presence: 'optional', }, optional: true, }, diff --git a/plugins/catalog-backend/src/ingestion/processors/GitLabDiscoveryProcessor.ts b/plugins/catalog-backend/src/ingestion/processors/GitLabDiscoveryProcessor.ts index 445f02829e..70ccd44793 100644 --- a/plugins/catalog-backend/src/ingestion/processors/GitLabDiscoveryProcessor.ts +++ b/plugins/catalog-backend/src/ingestion/processors/GitLabDiscoveryProcessor.ts @@ -113,6 +113,7 @@ export class GitLabDiscoveryProcessor implements CatalogProcessor { // The alternative is using the `buildRawUrl` function, which does not support subgroups, so providing a raw // URL here won't work either. target: `${project.web_url}/-/blob/${project_branch}/${catalogPath}`, + presence: 'optional', }, true, ), diff --git a/plugins/catalog-backend/src/ingestion/processors/GithubDiscoveryProcessor.test.ts b/plugins/catalog-backend/src/ingestion/processors/GithubDiscoveryProcessor.test.ts index 12d9e66659..59c25bb051 100644 --- a/plugins/catalog-backend/src/ingestion/processors/GithubDiscoveryProcessor.test.ts +++ b/plugins/catalog-backend/src/ingestion/processors/GithubDiscoveryProcessor.test.ts @@ -157,6 +157,7 @@ describe('GithubDiscoveryProcessor', () => { type: 'url', target: 'https://github.com/backstage/backstage/blob/master/catalog.yaml', + presence: 'optional', }, optional: true, }); @@ -165,6 +166,7 @@ describe('GithubDiscoveryProcessor', () => { location: { type: 'url', target: 'https://github.com/backstage/demo/blob/master/catalog.yaml', + presence: 'optional', }, optional: true, }); @@ -197,6 +199,7 @@ describe('GithubDiscoveryProcessor', () => { type: 'url', target: 'https://github.com/backstage/tech-docs/blob/main/catalog.yaml', + presence: 'optional', }, optional: true, }); @@ -251,6 +254,7 @@ describe('GithubDiscoveryProcessor', () => { type: 'url', target: 'https://github.com/backstage/backstage/blob/master/catalog-info.yaml', + presence: 'optional', }, optional: true, }); @@ -306,6 +310,7 @@ describe('GithubDiscoveryProcessor', () => { type: 'url', target: 'https://github.com/backstage/techdocs-cli/blob/master/catalog.yaml', + presence: 'optional', }, optional: true, }); @@ -315,6 +320,7 @@ describe('GithubDiscoveryProcessor', () => { type: 'url', target: 'https://github.com/backstage/techdocs-container/blob/master/catalog.yaml', + presence: 'optional', }, optional: true, }); @@ -370,6 +376,7 @@ describe('GithubDiscoveryProcessor', () => { location: { type: 'url', target: 'https://github.com/backstage/test/blob/master/catalog.yaml', + presence: 'optional', }, optional: true, }); diff --git a/plugins/catalog-backend/src/ingestion/processors/GithubDiscoveryProcessor.ts b/plugins/catalog-backend/src/ingestion/processors/GithubDiscoveryProcessor.ts index b104d87542..9bd6cc775e 100644 --- a/plugins/catalog-backend/src/ingestion/processors/GithubDiscoveryProcessor.ts +++ b/plugins/catalog-backend/src/ingestion/processors/GithubDiscoveryProcessor.ts @@ -125,6 +125,7 @@ export class GithubDiscoveryProcessor implements CatalogProcessor { { type: 'url', target: `${repository.url}${path}`, + presence: 'optional', }, // Not all locations may actually exist, since the user defined them as a wildcard pattern. // Thus, we emit them as optional and let the downstream processor find them while not outputting diff --git a/plugins/catalog-backend/src/ingestion/processors/bitbucket/BitbucketRepositoryParser.test.ts b/plugins/catalog-backend/src/ingestion/processors/bitbucket/BitbucketRepositoryParser.test.ts index bc6c5a540f..397d259bae 100644 --- a/plugins/catalog-backend/src/ingestion/processors/bitbucket/BitbucketRepositoryParser.test.ts +++ b/plugins/catalog-backend/src/ingestion/processors/bitbucket/BitbucketRepositoryParser.test.ts @@ -29,6 +29,7 @@ describe('BitbucketRepositoryParser', () => { { type: 'url', target: `${browseUrl}${path}`, + presence: 'optional', }, true, ), diff --git a/plugins/catalog-backend/src/ingestion/processors/bitbucket/BitbucketRepositoryParser.ts b/plugins/catalog-backend/src/ingestion/processors/bitbucket/BitbucketRepositoryParser.ts index 27f07d58a2..34a9284b3e 100644 --- a/plugins/catalog-backend/src/ingestion/processors/bitbucket/BitbucketRepositoryParser.ts +++ b/plugins/catalog-backend/src/ingestion/processors/bitbucket/BitbucketRepositoryParser.ts @@ -30,6 +30,7 @@ export const defaultRepositoryParser: BitbucketRepositoryParser = { type: 'url', target: target, + presence: 'optional', }, // Not all locations may actually exist, since the user defined them as a wildcard pattern. // Thus, we emit them as optional and let the downstream processor find them while not outputting diff --git a/plugins/catalog-backend/src/processing/DefaultCatalogProcessingOrchestrator.ts b/plugins/catalog-backend/src/processing/DefaultCatalogProcessingOrchestrator.ts index b42c8839cf..756b6a3639 100644 --- a/plugins/catalog-backend/src/processing/DefaultCatalogProcessingOrchestrator.ts +++ b/plugins/catalog-backend/src/processing/DefaultCatalogProcessingOrchestrator.ts @@ -284,7 +284,7 @@ export class DefaultCatalogProcessingOrchestrator entity: LocationEntity, context: Context, ): Promise { - const { type = context.location.type } = entity.spec; + const { type = context.location.type, presence = 'required' } = entity.spec; const targets = new Array(); if (entity.spec.target) { targets.push(entity.spec.target); @@ -318,9 +318,9 @@ export class DefaultCatalogProcessingOrchestrator { type, target, - presence: 'required', + presence, }, - false, + presence === 'optional', context.collector.onEmit, this.options.parser, context.cache.forProcessor(processor, target), diff --git a/plugins/catalog-backend/src/processing/ProcessorOutputCollector.ts b/plugins/catalog-backend/src/processing/ProcessorOutputCollector.ts index 436038db66..b0c46b4b9f 100644 --- a/plugins/catalog-backend/src/processing/ProcessorOutputCollector.ts +++ b/plugins/catalog-backend/src/processing/ProcessorOutputCollector.ts @@ -105,8 +105,9 @@ export class ProcessorOutputCollector { this.deferredEntities.push({ entity, locationKey: location }); } else if (i.type === 'location') { + const presence = i.optional ? 'optional' : 'required'; const entity = locationSpecToLocationEntity( - i.location, + { presence, ...i.location }, this.parentEntity, ); const locationKey = getEntityLocationRef(entity); diff --git a/plugins/catalog-backend/src/util/conversion.ts b/plugins/catalog-backend/src/util/conversion.ts index a834f81255..559a472c25 100644 --- a/plugins/catalog-backend/src/util/conversion.ts +++ b/plugins/catalog-backend/src/util/conversion.ts @@ -81,6 +81,7 @@ export function locationSpecToLocationEntity( spec: { type: location.type, target: location.target, + presence: location.presence, }, }; diff --git a/plugins/catalog-graph/package.json b/plugins/catalog-graph/package.json index b421a15265..59afadc62e 100644 --- a/plugins/catalog-graph/package.json +++ b/plugins/catalog-graph/package.json @@ -23,8 +23,8 @@ "dependencies": { "@backstage/catalog-client": "^0.5.2", "@backstage/catalog-model": "^0.9.7", - "@backstage/core-components": "^0.8.0", - "@backstage/core-plugin-api": "^0.3.0", + "@backstage/core-components": "^0.8.1", + "@backstage/core-plugin-api": "^0.3.1", "@backstage/plugin-catalog-react": "^0.6.5", "@backstage/theme": "^0.2.14", "@material-ui/core": "^4.12.2", @@ -42,8 +42,8 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.10.1", - "@backstage/core-app-api": "^0.2.0", + "@backstage/cli": "^0.10.2", + "@backstage/core-app-api": "^0.2.1", "@backstage/dev-utils": "^0.2.14", "@backstage/test-utils": "^0.1.24", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/catalog-import/package.json b/plugins/catalog-import/package.json index d417a337df..e017c45d09 100644 --- a/plugins/catalog-import/package.json +++ b/plugins/catalog-import/package.json @@ -33,8 +33,8 @@ "dependencies": { "@backstage/catalog-client": "^0.5.2", "@backstage/catalog-model": "^0.9.7", - "@backstage/core-components": "^0.8.0", - "@backstage/core-plugin-api": "^0.3.0", + "@backstage/core-components": "^0.8.1", + "@backstage/core-plugin-api": "^0.3.1", "@backstage/errors": "^0.1.3", "@backstage/integration": "^0.6.10", "@backstage/integration-react": "^0.1.15", @@ -56,8 +56,8 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.10.1", - "@backstage/core-app-api": "^0.2.0", + "@backstage/cli": "^0.10.2", + "@backstage/core-app-api": "^0.2.1", "@backstage/dev-utils": "^0.2.14", "@backstage/test-utils": "^0.1.24", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/catalog-react/CHANGELOG.md b/plugins/catalog-react/CHANGELOG.md index 5ebe626fad..0f7e08ead2 100644 --- a/plugins/catalog-react/CHANGELOG.md +++ b/plugins/catalog-react/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-catalog-react +## 0.6.7 + +### Patch Changes + +- 6156fb8730: `useEntityTypeFilter`: Skip updating selected types if a kind filter change did not change them. +- Updated dependencies + - @backstage/core-plugin-api@0.3.1 + - @backstage/core-components@0.8.1 + - @backstage/catalog-model@0.9.8 + ## 0.6.6 ### Patch Changes diff --git a/plugins/catalog-react/api-report.md b/plugins/catalog-react/api-report.md index c4aa254226..c95a8dc508 100644 --- a/plugins/catalog-react/api-report.md +++ b/plugins/catalog-react/api-report.md @@ -271,14 +271,13 @@ export const EntityRefLink: React_2.ForwardRefExoticComponent< | 'children' | 'key' | 'id' - | 'className' | 'classes' - | 'innerRef' | 'defaultChecked' | 'defaultValue' | 'suppressContentEditableWarning' | 'suppressHydrationWarning' | 'accessKey' + | 'className' | 'contentEditable' | 'contextMenu' | 'draggable' @@ -519,6 +518,7 @@ export const EntityRefLink: React_2.ForwardRefExoticComponent< | 'onTransitionEndCapture' | 'component' | 'variant' + | 'innerRef' | 'download' | 'href' | 'hrefLang' diff --git a/plugins/catalog-react/package.json b/plugins/catalog-react/package.json index da8300ae49..0bdffc0724 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": "0.6.6", + "version": "0.6.7", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -30,9 +30,9 @@ }, "dependencies": { "@backstage/catalog-client": "^0.5.2", - "@backstage/catalog-model": "^0.9.7", - "@backstage/core-components": "^0.8.0", - "@backstage/core-plugin-api": "^0.3.0", + "@backstage/catalog-model": "^0.9.8", + "@backstage/core-components": "^0.8.1", + "@backstage/core-plugin-api": "^0.3.1", "@backstage/errors": "^0.1.4", "@backstage/integration": "^0.6.10", "@backstage/types": "^0.1.1", @@ -52,8 +52,8 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.10.1", - "@backstage/core-app-api": "^0.2.0", + "@backstage/cli": "^0.10.2", + "@backstage/core-app-api": "^0.2.1", "@backstage/test-utils": "^0.1.24", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/catalog-react/src/hooks/useEntityTypeFilter.tsx b/plugins/catalog-react/src/hooks/useEntityTypeFilter.tsx index 5a8ed6775a..bf5777becc 100644 --- a/plugins/catalog-react/src/hooks/useEntityTypeFilter.tsx +++ b/plugins/catalog-react/src/hooks/useEntityTypeFilter.tsx @@ -16,6 +16,7 @@ import { useEffect, useMemo, useRef, useState } from 'react'; import { useAsync } from 'react-use'; +import isEqual from 'lodash/isEqual'; import { useApi } from '@backstage/core-plugin-api'; import { catalogApiRef } from '../api'; import { useEntityListProvider } from './useEntityListProvider'; @@ -107,7 +108,9 @@ export function useEntityTypeFilter(): EntityTypeReturn { const stillValidTypes = selectedTypes.filter(value => newTypes.includes(value), ); - setSelectedTypes(stillValidTypes); + if (!isEqual(selectedTypes, stillValidTypes)) { + setSelectedTypes(stillValidTypes); + } }, [loading, kind, selectedTypes, setSelectedTypes, entities]); useEffect(() => { diff --git a/plugins/catalog/package.json b/plugins/catalog/package.json index 57e14b5475..00504d7777 100644 --- a/plugins/catalog/package.json +++ b/plugins/catalog/package.json @@ -33,8 +33,8 @@ "dependencies": { "@backstage/catalog-client": "^0.5.2", "@backstage/catalog-model": "^0.9.7", - "@backstage/core-components": "^0.8.0", - "@backstage/core-plugin-api": "^0.3.0", + "@backstage/core-components": "^0.8.1", + "@backstage/core-plugin-api": "^0.3.1", "@backstage/errors": "^0.1.3", "@backstage/integration-react": "^0.1.15", "@backstage/plugin-catalog-react": "^0.6.5", @@ -53,8 +53,8 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.10.1", - "@backstage/core-app-api": "^0.2.0", + "@backstage/cli": "^0.10.2", + "@backstage/core-app-api": "^0.2.1", "@backstage/dev-utils": "^0.2.14", "@backstage/test-utils": "^0.1.24", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/circleci/package.json b/plugins/circleci/package.json index e2aa415043..f449e5556c 100644 --- a/plugins/circleci/package.json +++ b/plugins/circleci/package.json @@ -33,8 +33,8 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.7", - "@backstage/core-components": "^0.8.0", - "@backstage/core-plugin-api": "^0.3.0", + "@backstage/core-components": "^0.8.1", + "@backstage/core-plugin-api": "^0.3.1", "@backstage/plugin-catalog-react": "^0.6.5", "@backstage/theme": "^0.2.14", "@material-ui/core": "^4.12.2", @@ -52,8 +52,8 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.10.1", - "@backstage/core-app-api": "^0.2.0", + "@backstage/cli": "^0.10.2", + "@backstage/core-app-api": "^0.2.1", "@backstage/dev-utils": "^0.2.14", "@backstage/test-utils": "^0.1.24", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/cloudbuild/package.json b/plugins/cloudbuild/package.json index 6e91b22fcb..44dafc8245 100644 --- a/plugins/cloudbuild/package.json +++ b/plugins/cloudbuild/package.json @@ -32,8 +32,8 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.7", - "@backstage/core-components": "^0.8.0", - "@backstage/core-plugin-api": "^0.3.0", + "@backstage/core-components": "^0.8.1", + "@backstage/core-plugin-api": "^0.3.1", "@backstage/plugin-catalog-react": "^0.6.5", "@backstage/theme": "^0.2.14", "@material-ui/core": "^4.12.2", @@ -49,8 +49,8 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.10.1", - "@backstage/core-app-api": "^0.2.0", + "@backstage/cli": "^0.10.2", + "@backstage/core-app-api": "^0.2.1", "@backstage/dev-utils": "^0.2.14", "@backstage/test-utils": "^0.1.24", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/code-coverage/package.json b/plugins/code-coverage/package.json index 951a93110a..c5cb3e3acb 100644 --- a/plugins/code-coverage/package.json +++ b/plugins/code-coverage/package.json @@ -23,8 +23,8 @@ "dependencies": { "@backstage/catalog-model": "^0.9.7", "@backstage/config": "^0.1.11", - "@backstage/core-components": "^0.8.0", - "@backstage/core-plugin-api": "^0.3.0", + "@backstage/core-components": "^0.8.1", + "@backstage/core-plugin-api": "^0.3.1", "@backstage/errors": "^0.1.4", "@backstage/plugin-catalog-react": "^0.6.5", "@backstage/theme": "^0.2.14", @@ -43,8 +43,8 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.10.1", - "@backstage/core-app-api": "^0.2.0", + "@backstage/cli": "^0.10.2", + "@backstage/core-app-api": "^0.2.1", "@backstage/dev-utils": "^0.2.14", "@backstage/test-utils": "^0.1.24", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/config-schema/package.json b/plugins/config-schema/package.json index 59d34cc00d..accf5e2d01 100644 --- a/plugins/config-schema/package.json +++ b/plugins/config-schema/package.json @@ -22,8 +22,8 @@ }, "dependencies": { "@backstage/config": "^0.1.11", - "@backstage/core-components": "^0.8.0", - "@backstage/core-plugin-api": "^0.3.0", + "@backstage/core-components": "^0.8.1", + "@backstage/core-plugin-api": "^0.3.1", "@backstage/errors": "^0.1.5", "@backstage/theme": "^0.2.14", "@backstage/types": "^0.1.1", @@ -38,8 +38,8 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.10.1", - "@backstage/core-app-api": "^0.2.0", + "@backstage/cli": "^0.10.2", + "@backstage/core-app-api": "^0.2.1", "@backstage/dev-utils": "^0.2.14", "@backstage/test-utils": "^0.1.24", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/cost-insights/package.json b/plugins/cost-insights/package.json index 45975163e4..94ec2bb5fe 100644 --- a/plugins/cost-insights/package.json +++ b/plugins/cost-insights/package.json @@ -32,8 +32,8 @@ }, "dependencies": { "@backstage/config": "^0.1.10", - "@backstage/core-components": "^0.8.0", - "@backstage/core-plugin-api": "^0.3.0", + "@backstage/core-components": "^0.8.1", + "@backstage/core-plugin-api": "^0.3.1", "@backstage/theme": "^0.2.14", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -56,8 +56,8 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.10.1", - "@backstage/core-app-api": "^0.2.0", + "@backstage/cli": "^0.10.2", + "@backstage/core-app-api": "^0.2.1", "@backstage/dev-utils": "^0.2.14", "@backstage/test-utils": "^0.1.24", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/explore/package.json b/plugins/explore/package.json index 9975992ea5..4db5aa31e2 100644 --- a/plugins/explore/package.json +++ b/plugins/explore/package.json @@ -32,8 +32,8 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.7", - "@backstage/core-components": "^0.8.0", - "@backstage/core-plugin-api": "^0.3.0", + "@backstage/core-components": "^0.8.1", + "@backstage/core-plugin-api": "^0.3.1", "@backstage/plugin-catalog-react": "^0.6.5", "@backstage/plugin-explore-react": "^0.0.8", "@backstage/theme": "^0.2.14", @@ -50,8 +50,8 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.10.1", - "@backstage/core-app-api": "^0.2.0", + "@backstage/cli": "^0.10.2", + "@backstage/core-app-api": "^0.2.1", "@backstage/dev-utils": "^0.2.14", "@backstage/test-utils": "^0.1.24", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/firehydrant/package.json b/plugins/firehydrant/package.json index 68ee004605..784346e07c 100644 --- a/plugins/firehydrant/package.json +++ b/plugins/firehydrant/package.json @@ -22,8 +22,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.8.0", - "@backstage/core-plugin-api": "^0.3.0", + "@backstage/core-components": "^0.8.1", + "@backstage/core-plugin-api": "^0.3.1", "@backstage/plugin-catalog-react": "^0.6.5", "@backstage/theme": "^0.2.14", "@material-ui/core": "^4.12.2", @@ -36,8 +36,8 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.10.1", - "@backstage/core-app-api": "^0.2.0", + "@backstage/cli": "^0.10.2", + "@backstage/core-app-api": "^0.2.1", "@backstage/dev-utils": "^0.2.14", "@backstage/test-utils": "^0.1.24", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/fossa/package.json b/plugins/fossa/package.json index b28c8bd89f..6542721b61 100644 --- a/plugins/fossa/package.json +++ b/plugins/fossa/package.json @@ -33,8 +33,8 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.7", - "@backstage/core-components": "^0.8.0", - "@backstage/core-plugin-api": "^0.3.0", + "@backstage/core-components": "^0.8.1", + "@backstage/core-plugin-api": "^0.3.1", "@backstage/errors": "^0.1.3", "@backstage/plugin-catalog-react": "^0.6.5", "@backstage/theme": "^0.2.14", @@ -50,8 +50,8 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.10.1", - "@backstage/core-app-api": "^0.2.0", + "@backstage/cli": "^0.10.2", + "@backstage/core-app-api": "^0.2.1", "@backstage/dev-utils": "^0.2.14", "@backstage/test-utils": "^0.1.24", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/gcp-projects/package.json b/plugins/gcp-projects/package.json index e73dc7736a..5580595dc8 100644 --- a/plugins/gcp-projects/package.json +++ b/plugins/gcp-projects/package.json @@ -31,8 +31,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.8.0", - "@backstage/core-plugin-api": "^0.3.0", + "@backstage/core-components": "^0.8.1", + "@backstage/core-plugin-api": "^0.3.1", "@backstage/theme": "^0.2.14", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -44,8 +44,8 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.10.1", - "@backstage/core-app-api": "^0.2.0", + "@backstage/cli": "^0.10.2", + "@backstage/core-app-api": "^0.2.1", "@backstage/dev-utils": "^0.2.14", "@backstage/test-utils": "^0.1.24", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/git-release-manager/package.json b/plugins/git-release-manager/package.json index ab11255e08..720f624fdd 100644 --- a/plugins/git-release-manager/package.json +++ b/plugins/git-release-manager/package.json @@ -21,8 +21,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.8.0", - "@backstage/core-plugin-api": "^0.3.0", + "@backstage/core-components": "^0.8.1", + "@backstage/core-plugin-api": "^0.3.1", "@backstage/integration": "^0.6.10", "@backstage/theme": "^0.2.14", "@material-ui/core": "^4.12.2", @@ -40,8 +40,8 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.10.1", - "@backstage/core-app-api": "^0.2.0", + "@backstage/cli": "^0.10.2", + "@backstage/core-app-api": "^0.2.1", "@backstage/dev-utils": "^0.2.14", "@backstage/test-utils": "^0.1.24", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/github-actions/CHANGELOG.md b/plugins/github-actions/CHANGELOG.md index 4c4a339415..15c8f61978 100644 --- a/plugins/github-actions/CHANGELOG.md +++ b/plugins/github-actions/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-github-actions +## 0.4.27 + +### Patch Changes + +- 89bd772b00: Show empty state only when workflow API call has completed +- Updated dependencies + - @backstage/core-plugin-api@0.3.1 + - @backstage/core-components@0.8.1 + - @backstage/catalog-model@0.9.8 + - @backstage/plugin-catalog-react@0.6.7 + ## 0.4.26 ### Patch Changes diff --git a/plugins/github-actions/package.json b/plugins/github-actions/package.json index 434a0b6224..22cd220e9c 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.4.26", + "version": "0.4.27", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -33,11 +33,11 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.9.7", - "@backstage/core-components": "^0.8.0", - "@backstage/core-plugin-api": "^0.3.0", + "@backstage/catalog-model": "^0.9.8", + "@backstage/core-components": "^0.8.1", + "@backstage/core-plugin-api": "^0.3.1", "@backstage/integration": "^0.6.10", - "@backstage/plugin-catalog-react": "^0.6.5", + "@backstage/plugin-catalog-react": "^0.6.7", "@backstage/theme": "^0.2.14", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -52,8 +52,8 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.10.1", - "@backstage/core-app-api": "^0.2.0", + "@backstage/cli": "^0.10.2", + "@backstage/core-app-api": "^0.2.1", "@backstage/dev-utils": "^0.2.14", "@backstage/test-utils": "^0.1.24", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/github-deployments/package.json b/plugins/github-deployments/package.json index 1965f98ef1..d593ae7ac0 100644 --- a/plugins/github-deployments/package.json +++ b/plugins/github-deployments/package.json @@ -22,8 +22,8 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.7", - "@backstage/core-components": "^0.8.0", - "@backstage/core-plugin-api": "^0.3.0", + "@backstage/core-components": "^0.8.1", + "@backstage/core-plugin-api": "^0.3.1", "@backstage/errors": "^0.1.3", "@backstage/integration": "^0.6.10", "@backstage/integration-react": "^0.1.15", @@ -40,8 +40,8 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.10.1", - "@backstage/core-app-api": "^0.2.0", + "@backstage/cli": "^0.10.2", + "@backstage/core-app-api": "^0.2.1", "@backstage/dev-utils": "^0.2.14", "@backstage/test-utils": "^0.1.24", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/gitops-profiles/package.json b/plugins/gitops-profiles/package.json index 834fd2f7cf..b6d3f2f569 100644 --- a/plugins/gitops-profiles/package.json +++ b/plugins/gitops-profiles/package.json @@ -32,8 +32,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.8.0", - "@backstage/core-plugin-api": "^0.3.0", + "@backstage/core-components": "^0.8.1", + "@backstage/core-plugin-api": "^0.3.1", "@backstage/theme": "^0.2.14", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -45,8 +45,8 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.10.1", - "@backstage/core-app-api": "^0.2.0", + "@backstage/cli": "^0.10.2", + "@backstage/core-app-api": "^0.2.1", "@backstage/dev-utils": "^0.2.14", "@backstage/test-utils": "^0.1.24", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/graphiql/package.json b/plugins/graphiql/package.json index ec5a37bf36..7f165cbef5 100644 --- a/plugins/graphiql/package.json +++ b/plugins/graphiql/package.json @@ -31,8 +31,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.8.0", - "@backstage/core-plugin-api": "^0.3.0", + "@backstage/core-components": "^0.8.1", + "@backstage/core-plugin-api": "^0.3.1", "@backstage/theme": "^0.2.14", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -45,8 +45,8 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.10.1", - "@backstage/core-app-api": "^0.2.0", + "@backstage/cli": "^0.10.2", + "@backstage/core-app-api": "^0.2.1", "@backstage/dev-utils": "^0.2.14", "@backstage/test-utils": "^0.1.24", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/home/package.json b/plugins/home/package.json index b04e4a787d..4d14388d09 100644 --- a/plugins/home/package.json +++ b/plugins/home/package.json @@ -21,8 +21,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.8.0", - "@backstage/core-plugin-api": "^0.3.0", + "@backstage/core-components": "^0.8.1", + "@backstage/core-plugin-api": "^0.3.1", "@backstage/theme": "^0.2.14", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -36,8 +36,8 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.10.1", - "@backstage/core-app-api": "^0.2.0", + "@backstage/cli": "^0.10.2", + "@backstage/core-app-api": "^0.2.1", "@backstage/dev-utils": "^0.2.14", "@backstage/test-utils": "^0.1.24", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/ilert/package.json b/plugins/ilert/package.json index 5c07bc9d21..a476abebd8 100644 --- a/plugins/ilert/package.json +++ b/plugins/ilert/package.json @@ -22,8 +22,8 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.7", - "@backstage/core-components": "^0.8.0", - "@backstage/core-plugin-api": "^0.3.0", + "@backstage/core-components": "^0.8.1", + "@backstage/core-plugin-api": "^0.3.1", "@backstage/errors": "^0.1.3", "@backstage/plugin-catalog-react": "^0.6.5", "@backstage/theme": "^0.2.14", @@ -40,8 +40,8 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.10.1", - "@backstage/core-app-api": "^0.2.0", + "@backstage/cli": "^0.10.2", + "@backstage/core-app-api": "^0.2.1", "@backstage/dev-utils": "^0.2.14", "@backstage/test-utils": "^0.1.24", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/jenkins/package.json b/plugins/jenkins/package.json index 2211bf54de..5822a262b5 100644 --- a/plugins/jenkins/package.json +++ b/plugins/jenkins/package.json @@ -33,8 +33,8 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.7", - "@backstage/core-components": "^0.8.0", - "@backstage/core-plugin-api": "^0.3.0", + "@backstage/core-components": "^0.8.1", + "@backstage/core-plugin-api": "^0.3.1", "@backstage/plugin-catalog-react": "^0.6.5", "@backstage/theme": "^0.2.14", "@material-ui/core": "^4.12.2", @@ -49,8 +49,8 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.10.1", - "@backstage/core-app-api": "^0.2.0", + "@backstage/cli": "^0.10.2", + "@backstage/core-app-api": "^0.2.1", "@backstage/dev-utils": "^0.2.14", "@backstage/test-utils": "^0.1.24", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/kafka/package.json b/plugins/kafka/package.json index 309e239a68..d3353a1f0d 100644 --- a/plugins/kafka/package.json +++ b/plugins/kafka/package.json @@ -22,8 +22,8 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.7", - "@backstage/core-components": "^0.8.0", - "@backstage/core-plugin-api": "^0.3.0", + "@backstage/core-components": "^0.8.1", + "@backstage/core-plugin-api": "^0.3.1", "@backstage/plugin-catalog-react": "^0.6.5", "@backstage/theme": "^0.2.14", "@material-ui/core": "^4.12.2", @@ -36,8 +36,8 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.10.1", - "@backstage/core-app-api": "^0.2.0", + "@backstage/cli": "^0.10.2", + "@backstage/core-app-api": "^0.2.1", "@backstage/dev-utils": "^0.2.14", "@backstage/test-utils": "^0.1.24", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/kubernetes/CHANGELOG.md b/plugins/kubernetes/CHANGELOG.md index 0329a9bc5b..94826738b3 100644 --- a/plugins/kubernetes/CHANGELOG.md +++ b/plugins/kubernetes/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-kubernetes +## 0.5.1 + +### Patch Changes + +- 6f0c850a86: Fixed styling bug for the CronJobsAccordions and updated Completed pods to display a green dot. +- Updated dependencies + - @backstage/core-plugin-api@0.3.1 + - @backstage/core-components@0.8.1 + - @backstage/catalog-model@0.9.8 + - @backstage/plugin-catalog-react@0.6.7 + ## 0.5.0 ### Minor Changes diff --git a/plugins/kubernetes/package.json b/plugins/kubernetes/package.json index 3a2ea6a9a9..02b8e85855 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.5.0", + "version": "0.5.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -31,11 +31,11 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.9.7", + "@backstage/catalog-model": "^0.9.8", "@backstage/config": "^0.1.11", - "@backstage/core-components": "^0.8.0", - "@backstage/core-plugin-api": "^0.3.0", - "@backstage/plugin-catalog-react": "^0.6.5", + "@backstage/core-components": "^0.8.1", + "@backstage/core-plugin-api": "^0.3.1", + "@backstage/plugin-catalog-react": "^0.6.7", "@backstage/plugin-kubernetes-common": "^0.2.0", "@kubernetes/client-node": "^0.16.0", "@backstage/theme": "^0.2.14", @@ -53,8 +53,8 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.10.1", - "@backstage/core-app-api": "^0.2.0", + "@backstage/cli": "^0.10.2", + "@backstage/core-app-api": "^0.2.1", "@backstage/dev-utils": "^0.2.14", "@backstage/test-utils": "^0.1.24", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/kubernetes/src/components/Cluster/Cluster.tsx b/plugins/kubernetes/src/components/Cluster/Cluster.tsx index b7a819cdbf..eb12ba20f8 100644 --- a/plugins/kubernetes/src/components/Cluster/Cluster.tsx +++ b/plugins/kubernetes/src/components/Cluster/Cluster.tsx @@ -148,9 +148,9 @@ export const Cluster = ({ clusterObjects, podsWithErrors }: ClusterProps) => { - - - + + + diff --git a/plugins/kubernetes/src/utils/pod.tsx b/plugins/kubernetes/src/utils/pod.tsx index bfb40750cd..50cc983b9d 100644 --- a/plugins/kubernetes/src/utils/pod.tsx +++ b/plugins/kubernetes/src/utils/pod.tsx @@ -63,7 +63,13 @@ export const containerStatuses = (pod: V1Pod): ReactNode => { const renderCell = (reason: string | undefined) => ( Container: {next.name}} + value={ + reason === 'Completed' ? ( + Container: {next.name} + ) : ( + Container: {next.name} + ) + } subvalue={reason} />
diff --git a/plugins/lighthouse/package.json b/plugins/lighthouse/package.json index 43aabc3e3f..3820e85fdd 100644 --- a/plugins/lighthouse/package.json +++ b/plugins/lighthouse/package.json @@ -34,8 +34,8 @@ "dependencies": { "@backstage/catalog-model": "^0.9.7", "@backstage/config": "^0.1.10", - "@backstage/core-components": "^0.8.0", - "@backstage/core-plugin-api": "^0.3.0", + "@backstage/core-components": "^0.8.1", + "@backstage/core-plugin-api": "^0.3.1", "@backstage/plugin-catalog-react": "^0.6.5", "@backstage/theme": "^0.2.14", "@material-ui/core": "^4.12.2", @@ -48,8 +48,8 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.10.1", - "@backstage/core-app-api": "^0.2.0", + "@backstage/cli": "^0.10.2", + "@backstage/core-app-api": "^0.2.1", "@backstage/dev-utils": "^0.2.14", "@backstage/test-utils": "^0.1.24", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/newrelic/package.json b/plugins/newrelic/package.json index b7be235b9e..9a32e23e18 100644 --- a/plugins/newrelic/package.json +++ b/plugins/newrelic/package.json @@ -32,8 +32,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.8.0", - "@backstage/core-plugin-api": "^0.3.0", + "@backstage/core-components": "^0.8.1", + "@backstage/core-plugin-api": "^0.3.1", "@backstage/theme": "^0.2.14", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -44,8 +44,8 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.10.1", - "@backstage/core-app-api": "^0.2.0", + "@backstage/cli": "^0.10.2", + "@backstage/core-app-api": "^0.2.1", "@backstage/dev-utils": "^0.2.14", "@backstage/test-utils": "^0.1.24", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/org/CHANGELOG.md b/plugins/org/CHANGELOG.md index 0576d644da..eb2599237c 100644 --- a/plugins/org/CHANGELOG.md +++ b/plugins/org/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-org +## 0.3.31 + +### Patch Changes + +- fe86adbcd2: Added `entityFilterKind` property for `EntityOwnershipCard` +- Updated dependencies + - @backstage/core-plugin-api@0.3.1 + - @backstage/core-components@0.8.1 + - @backstage/catalog-model@0.9.8 + - @backstage/plugin-catalog-react@0.6.7 + ## 0.3.30 ### Patch Changes diff --git a/plugins/org/api-report.md b/plugins/org/api-report.md index 88ce00fd52..60caa45134 100644 --- a/plugins/org/api-report.md +++ b/plugins/org/api-report.md @@ -33,9 +33,11 @@ export const EntityMembersListCard: (_props: { // @public (undocumented) export const EntityOwnershipCard: ({ variant, + entityFilterKind, }: { entity?: Entity | undefined; variant?: InfoCardVariants | undefined; + entityFilterKind?: string[] | undefined; }) => JSX.Element; // Warning: (ae-missing-release-tag) "EntityUserProfileCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -75,9 +77,11 @@ export { orgPlugin as plugin }; // @public (undocumented) export const OwnershipCard: ({ variant, + entityFilterKind, }: { entity?: Entity | undefined; variant?: InfoCardVariants | undefined; + entityFilterKind?: string[] | undefined; }) => JSX.Element; // Warning: (ae-missing-release-tag) "UserProfileCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) diff --git a/plugins/org/package.json b/plugins/org/package.json index 4e7f556547..8733c1c322 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.3.30", + "version": "0.3.31", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -21,10 +21,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.9.7", - "@backstage/core-components": "^0.8.0", - "@backstage/core-plugin-api": "^0.3.0", - "@backstage/plugin-catalog-react": "^0.6.5", + "@backstage/catalog-model": "^0.9.8", + "@backstage/core-components": "^0.8.1", + "@backstage/core-plugin-api": "^0.3.1", + "@backstage/plugin-catalog-react": "^0.6.7", "@backstage/theme": "^0.2.14", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -38,8 +38,9 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.10.1", - "@backstage/core-app-api": "^0.2.0", + "@backstage/cli": "^0.10.2", + "@backstage/core-app-api": "^0.2.1", + "@backstage/catalog-client": "^0.5.2", "@backstage/dev-utils": "^0.2.14", "@backstage/test-utils": "^0.1.24", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/org/src/components/Cards/OwnershipCard/OwnershipCard.test.tsx b/plugins/org/src/components/Cards/OwnershipCard/OwnershipCard.test.tsx index 8d6fc1a002..439365e583 100644 --- a/plugins/org/src/components/Cards/OwnershipCard/OwnershipCard.test.tsx +++ b/plugins/org/src/components/Cards/OwnershipCard/OwnershipCard.test.tsx @@ -14,7 +14,11 @@ * limitations under the License. */ -import { GroupEntity, UserEntity } from '@backstage/catalog-model'; +import { + CatalogEntitiesRequest, + CatalogListResponse, +} from '@backstage/catalog-client'; +import { Entity, GroupEntity, UserEntity } from '@backstage/catalog-model'; import { CatalogApi, catalogApiRef, @@ -26,6 +30,97 @@ import { queryByText } from '@testing-library/react'; import React from 'react'; import { OwnershipCard } from './OwnershipCard'; +const items = [ + { + apiVersion: 'backstage.io/v1alpha1', + kind: 'API', + metadata: { + name: 'my-api', + }, + spec: { + type: 'openapi', + }, + relations: [ + { + type: 'ownedBy', + target: { + name: 'my-team', + namespace: 'default', + kind: 'Group', + }, + }, + ], + }, + { + kind: 'Component', + metadata: { + name: 'my-service', + }, + spec: { + type: 'service', + }, + relations: [ + { + type: 'ownedBy', + target: { + name: 'my-team', + namespace: 'default', + kind: 'Group', + }, + }, + ], + }, + { + kind: 'Component', + metadata: { + name: 'my-library', + namespace: 'other-namespace', + }, + spec: { + type: 'library', + }, + relations: [ + { + type: 'ownedBy', + target: { + name: 'my-team', + namespace: 'default', + kind: 'Group', + }, + }, + ], + }, + { + apiVersion: 'backstage.io/v1alpha1', + kind: 'System', + metadata: { + name: 'my-system', + }, + relations: [ + { + type: 'ownedBy', + target: { + name: 'my-team', + namespace: 'default', + kind: 'Group', + }, + }, + ], + }, +] as Entity[]; + +const getEntitiesMock = ( + request?: CatalogEntitiesRequest, +): Promise> => { + const filterKinds = + !Array.isArray(request?.filter) && Array.isArray(request?.filter?.kind) + ? request?.filter?.kind ?? [] + : []; // we expect the request to be like { filter: { kind: ['API','System'], .... }. If changed in OwnerShipCard, let's change in also here + return Promise.resolve({ + items: items.filter(item => filterKinds.find(k => k === item.kind)), + } as CatalogListResponse); +}; + describe('OwnershipCard', () => { const groupEntity: GroupEntity = { apiVersion: 'backstage.io/v1alpha1', @@ -49,75 +144,12 @@ describe('OwnershipCard', () => { ], }; - const items = [ - { - kind: 'API', - metadata: { - name: 'my-api', - }, - spec: { - type: 'openapi', - }, - relations: [ - { - type: 'ownedBy', - target: { - name: 'my-team', - namespace: 'default', - kind: 'Group', - }, - }, - ], - }, - { - kind: 'Component', - metadata: { - name: 'my-service', - }, - spec: { - type: 'service', - }, - relations: [ - { - type: 'ownedBy', - target: { - name: 'my-team', - namespace: 'default', - kind: 'Group', - }, - }, - ], - }, - { - kind: 'Component', - metadata: { - name: 'my-library', - namespace: 'other-namespace', - }, - spec: { - type: 'library', - }, - relations: [ - { - type: 'ownedBy', - target: { - name: 'my-team', - namespace: 'default', - kind: 'Group', - }, - }, - ], - }, - ] as any; - it('displays entity counts', async () => { const catalogApi: jest.Mocked = { getEntities: jest.fn(), } as any; - catalogApi.getEntities.mockResolvedValue({ - items, - }); + catalogApi.getEntities.mockImplementation(getEntitiesMock); const { getByText } = await renderInTestApp( @@ -132,6 +164,17 @@ describe('OwnershipCard', () => { }, ); + expect(catalogApi.getEntities).toHaveBeenCalledWith({ + filter: { kind: ['Component', 'API'] }, + fields: [ + 'kind', + 'metadata.name', + 'metadata.namespace', + 'spec.type', + 'relations', + ], + }); + expect(getByText('OPENAPI')).toBeInTheDocument(); expect( queryByText(getByText('OPENAPI').parentElement!, '1'), @@ -144,6 +187,38 @@ describe('OwnershipCard', () => { expect( queryByText(getByText('LIBRARY').parentElement!, '1'), ).toBeInTheDocument(); + expect(() => getByText('SYSTEM')).toThrowError(); + }); + + it('applies CustomFilterDefinition', async () => { + const catalogApi: jest.Mocked = { + getEntities: jest.fn(), + } as any; + + catalogApi.getEntities.mockImplementation(getEntitiesMock); + + const { getByText } = await renderInTestApp( + + + + + , + { + mountedRoutes: { + '/create': catalogRouteRef, + }, + }, + ); + + expect(getByText('SYSTEM')).toBeInTheDocument(); + expect( + queryByText(getByText('SYSTEM').parentElement!, '1'), + ).toBeInTheDocument(); + expect(getByText('OPENAPI')).toBeInTheDocument(); + expect( + queryByText(getByText('OPENAPI').parentElement!, '1'), + ).toBeInTheDocument(); + expect(() => getByText('LIBRARY')).toThrowError(); }); it('links to the catalog with the group filter', async () => { @@ -151,9 +226,7 @@ describe('OwnershipCard', () => { getEntities: jest.fn(), } as any; - catalogApi.getEntities.mockResolvedValue({ - items, - }); + catalogApi.getEntities.mockImplementation(getEntitiesMock); const { getByText } = await renderInTestApp( @@ -199,9 +272,7 @@ describe('OwnershipCard', () => { getEntities: jest.fn(), } as any; - catalogApi.getEntities.mockResolvedValue({ - items, - }); + catalogApi.getEntities.mockImplementation(getEntitiesMock); const { getByText } = await renderInTestApp( diff --git a/plugins/org/src/components/Cards/OwnershipCard/OwnershipCard.tsx b/plugins/org/src/components/Cards/OwnershipCard/OwnershipCard.tsx index fe23191779..1e2d514359 100644 --- a/plugins/org/src/components/Cards/OwnershipCard/OwnershipCard.tsx +++ b/plugins/org/src/components/Cards/OwnershipCard/OwnershipCard.tsx @@ -128,10 +128,12 @@ const getQueryParams = ( export const OwnershipCard = ({ variant, + entityFilterKind, }: { /** @deprecated The entity is now grabbed from context instead */ entity?: Entity; variant?: InfoCardVariants; + entityFilterKind?: string[]; }) => { const { entity } = useEntity(); const catalogApi = useApi(catalogApiRef); @@ -142,7 +144,7 @@ export const OwnershipCard = ({ error, value: componentsWithCounters, } = useAsync(async () => { - const kinds = ['Component', 'API']; + const kinds = entityFilterKind ?? ['Component', 'API']; const entitiesList = await catalogApi.getEntities({ filter: { kind: kinds, @@ -162,17 +164,17 @@ export const OwnershipCard = ({ const counts = ownedEntitiesList.reduce( (acc: EntityTypeProps[], ownedEntity) => { - if (typeof ownedEntity.spec?.type !== 'string') return acc; - const match = acc.find( - x => x.kind === ownedEntity.kind && x.type === ownedEntity.spec?.type, + x => + x.kind === ownedEntity.kind && + x.type === (ownedEntity.spec?.type ?? ownedEntity.kind), ); if (match) { match.count += 1; } else { acc.push({ kind: ownedEntity.kind, - type: ownedEntity.spec?.type, + type: ownedEntity.spec?.type?.toString() ?? ownedEntity.kind, count: 1, }); } diff --git a/plugins/pagerduty/package.json b/plugins/pagerduty/package.json index 59e76878b5..1b6c38465f 100644 --- a/plugins/pagerduty/package.json +++ b/plugins/pagerduty/package.json @@ -32,8 +32,8 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.7", - "@backstage/core-components": "^0.8.0", - "@backstage/core-plugin-api": "^0.3.0", + "@backstage/core-components": "^0.8.1", + "@backstage/core-plugin-api": "^0.3.1", "@backstage/plugin-catalog-react": "^0.6.5", "@backstage/theme": "^0.2.14", "@material-ui/core": "^4.12.2", @@ -49,8 +49,8 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.10.1", - "@backstage/core-app-api": "^0.2.0", + "@backstage/cli": "^0.10.2", + "@backstage/core-app-api": "^0.2.1", "@backstage/dev-utils": "^0.2.14", "@backstage/test-utils": "^0.1.24", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/rollbar/package.json b/plugins/rollbar/package.json index d6a14a5b2a..012cb24490 100644 --- a/plugins/rollbar/package.json +++ b/plugins/rollbar/package.json @@ -33,8 +33,8 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.7", - "@backstage/core-components": "^0.8.0", - "@backstage/core-plugin-api": "^0.3.0", + "@backstage/core-components": "^0.8.1", + "@backstage/core-plugin-api": "^0.3.1", "@backstage/plugin-catalog-react": "^0.6.5", "@backstage/theme": "^0.2.14", "@material-ui/core": "^4.12.2", @@ -50,8 +50,8 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.10.1", - "@backstage/core-app-api": "^0.2.0", + "@backstage/cli": "^0.10.2", + "@backstage/core-app-api": "^0.2.1", "@backstage/dev-utils": "^0.2.14", "@backstage/test-utils": "^0.1.24", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/scaffolder-backend/CHANGELOG.md b/plugins/scaffolder-backend/CHANGELOG.md index 3986961f16..50d10b564c 100644 --- a/plugins/scaffolder-backend/CHANGELOG.md +++ b/plugins/scaffolder-backend/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-scaffolder-backend +## 0.15.17 + +### Patch Changes + +- eec0750d8d: Makes cookiecutter a default, but optional action based on if a containerRunner argument is passed in to createRouter or createBuiltinActions +- ed52f74ab3: Adding changes to create GitLab Merge Request using custom action +- Updated dependencies + - @backstage/plugin-catalog-backend@0.19.2 + - @backstage/backend-common@0.9.14 + - @backstage/catalog-model@0.9.8 + ## 0.15.16 ### Patch Changes diff --git a/plugins/scaffolder-backend/api-report.md b/plugins/scaffolder-backend/api-report.md index 6ed4625f66..b7c1a9146c 100644 --- a/plugins/scaffolder-backend/api-report.md +++ b/plugins/scaffolder-backend/api-report.md @@ -68,7 +68,7 @@ export const createBuiltinActions: (options: { reader: UrlReader; integrations: ScmIntegrations; catalogClient: CatalogApi; - containerRunner: ContainerRunner; + containerRunner?: ContainerRunner; config: Config; }) => TemplateAction[]; @@ -179,6 +179,13 @@ export function createPublishGitlabAction(options: { config: Config; }): TemplateAction; +// Warning: (ae-missing-release-tag) "createPublishGitlabMergeRequestAction" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const createPublishGitlabMergeRequestAction: (options: { + integrations: ScmIntegrationRegistry; +}) => TemplateAction; + // 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) @@ -289,7 +296,7 @@ export interface RouterOptions { // (undocumented) config: Config; // (undocumented) - containerRunner: ContainerRunner; + containerRunner?: ContainerRunner; // (undocumented) database: PluginDatabaseManager; // (undocumented) diff --git a/plugins/scaffolder-backend/package.json b/plugins/scaffolder-backend/package.json index 2dae658d74..3686c33c40 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": "0.15.16", + "version": "0.15.17", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -31,13 +31,13 @@ "build:assets": "node scripts/build-nunjucks.js" }, "dependencies": { - "@backstage/backend-common": "^0.9.13", + "@backstage/backend-common": "^0.9.14", "@backstage/catalog-client": "^0.5.2", - "@backstage/catalog-model": "^0.9.7", + "@backstage/catalog-model": "^0.9.8", "@backstage/config": "^0.1.11", "@backstage/errors": "^0.1.5", "@backstage/integration": "^0.6.10", - "@backstage/plugin-catalog-backend": "^0.19.1", + "@backstage/plugin-catalog-backend": "^0.19.2", "@backstage/plugin-scaffolder-common": "^0.1.1", "@backstage/plugin-scaffolder-backend-module-cookiecutter": "^0.1.5", "@backstage/types": "^0.1.1", @@ -73,7 +73,7 @@ "vm2": "^3.9.5" }, "devDependencies": { - "@backstage/cli": "^0.10.1", + "@backstage/cli": "^0.10.2", "@backstage/test-utils": "^0.1.24", "@types/command-exists": "^1.2.0", "@types/fs-extra": "^9.0.1", diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/createBuiltinActions.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/createBuiltinActions.ts index d38dfe3cdc..93ecceb76a 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/createBuiltinActions.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/createBuiltinActions.ts @@ -36,6 +36,7 @@ import { createPublishGithubAction, createPublishGithubPullRequestAction, createPublishGitlabAction, + createPublishGitlabMergeRequestAction, } from './publish'; import { createGithubActionsDispatchAction, @@ -46,22 +47,17 @@ export const createBuiltinActions = (options: { reader: UrlReader; integrations: ScmIntegrations; catalogClient: CatalogApi; - containerRunner: ContainerRunner; + containerRunner?: ContainerRunner; config: Config; }) => { const { reader, integrations, containerRunner, catalogClient, config } = options; - return [ + const actions = [ createFetchPlainAction({ reader, integrations, }), - createFetchCookiecutterAction({ - reader, - integrations, - containerRunner, - }), createFetchTemplateAction({ integrations, reader, @@ -77,6 +73,9 @@ export const createBuiltinActions = (options: { integrations, config, }), + createPublishGitlabMergeRequestAction({ + integrations, + }), createPublishBitbucketAction({ integrations, config, @@ -97,4 +96,16 @@ export const createBuiltinActions = (options: { integrations, }), ]; + + if (containerRunner) { + actions.push( + createFetchCookiecutterAction({ + reader, + integrations, + containerRunner, + }), + ); + } + + return actions; }; diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlabMergeRequest.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlabMergeRequest.ts new file mode 100644 index 0000000000..2ac369249b --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlabMergeRequest.ts @@ -0,0 +1,186 @@ +/* + * 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 { createTemplateAction } from '../../createTemplateAction'; +import { readFile } from 'fs-extra'; +import { Gitlab } from '@gitbeaker/node'; +import globby from 'globby'; +import { Types } from '@gitbeaker/core'; + +import { ScmIntegrationRegistry } from '@backstage/integration'; +import { InputError } from '@backstage/errors'; +import { parseRepoUrl } from './util'; +import { resolveSafeChildPath } from '@backstage/backend-common'; + +export type GitlabMergeRequestActionInput = { + projectid: string; + repoUrl: string; + title: string; + description: string; + branchName: string; + targetPath: string; +}; + +export const createPublishGitlabMergeRequestAction = (options: { + integrations: ScmIntegrationRegistry; +}) => { + const { integrations } = options; + + return createTemplateAction({ + id: 'publish:gitlab:merge-request', + schema: { + input: { + required: ['projectid', 'repoUrl', 'targetPath', 'branchName'], + type: 'object', + properties: { + repoUrl: { + type: 'string', + 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`, + }, + projectid: { + type: 'string', + title: 'projectid', + description: 'Project ID/Name(slug) of the Gitlab Project', + }, + title: { + type: 'string', + title: 'Merge Request Name', + description: 'The name for the merge request', + }, + description: { + type: 'string', + title: 'Merge Request Description', + description: 'The description of the merge request', + }, + branchName: { + type: 'string', + title: 'Destination Branch name', + description: 'The description of the merge request', + }, + targetPath: { + type: 'string', + title: 'Repository Subdirectory', + description: 'Subdirectory of repository to apply changes to', + }, + }, + }, + output: { + type: 'object', + properties: { + projectid: { + title: 'Gitlab Project id/Name(slug)', + type: 'string', + }, + mergeRequestURL: { + title: 'MergeRequest(MR) URL', + type: 'string', + description: 'Link to the merge request in GitLab', + }, + }, + }, + }, + async handler(ctx) { + const repoUrl = ctx.input.repoUrl; + const { host } = parseRepoUrl(repoUrl, integrations); + const integrationConfig = integrations.gitlab.byHost(host); + + const actions: Types.CommitAction[] = []; + + const destinationBranch = ctx.input.branchName; + + if (!integrationConfig) { + throw new InputError( + `No matching integration configuration for host ${host}, please check your integrations config`, + ); + } + + if (!integrationConfig.config.token) { + throw new InputError(`No token available for host ${host}`); + } + + const api = new Gitlab({ + host: integrationConfig.config.baseUrl, + token: integrationConfig.config.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 repoFilePaths = localFilePaths.map(repoFilePath => { + return repoFilePath; + }); + + 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 { default_branch: defaultBranch } = projects; + + try { + await api.Branches.create( + ctx.input.projectid, + destinationBranch, + String(defaultBranch), + ); + } catch (e) { + throw new InputError(`The branch creation failed ${e}`); + } + + try { + await api.Commits.create( + ctx.input.projectid, + destinationBranch, + ctx.input.title, + actions, + ); + } catch (e) { + throw new InputError( + `Committing the changes to ${destinationBranch} failed ${e}`, + ); + } + + try { + const mergeRequestUrl = await api.MergeRequests.create( + ctx.input.projectid, + destinationBranch, + String(defaultBranch), + ctx.input.title, + { description: ctx.input.description }, + ).then((mergeRequest: { web_url: string }) => { + return mergeRequest.web_url; + }); + ctx.output('projectid', ctx.input.projectid); + 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 a292438d3a..c42b04e55e 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/index.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/index.ts @@ -20,3 +20,4 @@ export { createPublishFileAction } from './file'; export { createPublishGithubAction } from './github'; export { createPublishGithubPullRequestAction } from './githubPullRequest'; export { createPublishGitlabAction } from './gitlab'; +export { createPublishGitlabMergeRequestAction } from './gitlabMergeRequest'; diff --git a/plugins/scaffolder-backend/src/service/router.ts b/plugins/scaffolder-backend/src/service/router.ts index b8f2968287..9d26857ab9 100644 --- a/plugins/scaffolder-backend/src/service/router.ts +++ b/plugins/scaffolder-backend/src/service/router.ts @@ -55,7 +55,7 @@ export interface RouterOptions { catalogClient: CatalogApi; actions?: TemplateAction[]; taskWorkers?: number; - containerRunner: ContainerRunner; + containerRunner?: ContainerRunner; taskBroker?: TaskBroker; } diff --git a/plugins/scaffolder/package.json b/plugins/scaffolder/package.json index e3aaa722b0..3d42126624 100644 --- a/plugins/scaffolder/package.json +++ b/plugins/scaffolder/package.json @@ -34,8 +34,8 @@ "@backstage/catalog-client": "^0.5.2", "@backstage/catalog-model": "^0.9.7", "@backstage/config": "^0.1.11", - "@backstage/core-components": "^0.8.0", - "@backstage/core-plugin-api": "^0.3.0", + "@backstage/core-components": "^0.8.1", + "@backstage/core-plugin-api": "^0.3.1", "@backstage/errors": "^0.1.5", "@backstage/integration": "^0.6.10", "@backstage/integration-react": "^0.1.15", @@ -66,10 +66,10 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/plugin-catalog": "^0.7.4", - "@backstage/cli": "^0.10.1", - "@backstage/core-app-api": "^0.2.0", + "@backstage/cli": "^0.10.2", + "@backstage/core-app-api": "^0.2.1", "@backstage/dev-utils": "^0.2.14", + "@backstage/plugin-catalog": "^0.7.4", "@backstage/test-utils": "^0.1.24", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/search-backend-module-elasticsearch/CHANGELOG.md b/plugins/search-backend-module-elasticsearch/CHANGELOG.md index f36078c503..52ca5efaff 100644 --- a/plugins/search-backend-module-elasticsearch/CHANGELOG.md +++ b/plugins/search-backend-module-elasticsearch/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-search-backend-module-elasticsearch +## 0.0.7 + +### Patch Changes + +- 68512f5178: Add `newClient()` method to re-use the configuration of the elastic search + engine with custom clients + ## 0.0.6 ### Patch Changes diff --git a/plugins/search-backend-module-elasticsearch/api-report.md b/plugins/search-backend-module-elasticsearch/api-report.md index 1e798119c3..6dc0256586 100644 --- a/plugins/search-backend-module-elasticsearch/api-report.md +++ b/plugins/search-backend-module-elasticsearch/api-report.md @@ -3,18 +3,103 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { Client } from '@elastic/elasticsearch'; +/// + import { Config } from '@backstage/config'; +import type { ConnectionOptions } from 'tls'; import { IndexableDocument } from '@backstage/search-common'; import { Logger as Logger_2 } from 'winston'; import { SearchEngine } from '@backstage/search-common'; import { SearchQuery } from '@backstage/search-common'; import { SearchResultSet } from '@backstage/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 +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) + cloud?: { + id: string; + username?: string; + password?: string; + }; + // (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) + disablePrototypePoisoningProtection?: boolean | 'proto' | 'constructor'; + // (undocumented) + enableMetaHeader?: boolean; + // (undocumented) + headers?: Record; + // (undocumented) + 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 + | string[] + | ElasticSearchNodeOptions + | ElasticSearchNodeOptions[]; + // (undocumented) + nodeFilter?: (connection: any) => boolean; + // (undocumented) + nodes?: + | string + | string[] + | ElasticSearchNodeOptions + | ElasticSearchNodeOptions[]; + // (undocumented) + nodeSelector?: ((connections: any[]) => any) | string; + // (undocumented) + opaqueIdPrefix?: string; + // (undocumented) + pingTimeout?: number; + // (undocumented) + provider?: 'aws' | 'elastic'; + // (undocumented) + proxy?: string | URL; + // (undocumented) + requestTimeout?: number; + // (undocumented) + resurrectStrategy?: 'ping' | 'optimistic' | 'none'; + // (undocumented) + sniffEndpoint?: string; + // (undocumented) + sniffInterval?: number | boolean; + // (undocumented) + sniffOnConnectionFault?: boolean; + // (undocumented) + sniffOnStart?: boolean; + // (undocumented) + 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 (undocumented) export class ElasticSearchSearchEngine implements SearchEngine { constructor( - elasticSearchClient: Client, + elasticSearchClientOptions: ElasticSearchClientOptions, aliasPostfix: string, indexPrefix: string, logger: Logger_2, @@ -22,11 +107,15 @@ export class ElasticSearchSearchEngine implements SearchEngine { // Warning: (ae-forgotten-export) The symbol "ElasticSearchOptions" needs to be exported by the entry point index.d.ts // // (undocumented) - static fromConfig( - options: ElasticSearchOptions, - ): Promise; + static fromConfig({ + logger, + config, + aliasPostfix, + indexPrefix, + }: ElasticSearchOptions): Promise; // (undocumented) index(type: string, documents: IndexableDocument[]): Promise; + 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 diff --git a/plugins/search-backend-module-elasticsearch/package.json b/plugins/search-backend-module-elasticsearch/package.json index 633af83ca6..0873fb24ac 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.0.6", + "version": "0.0.7", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -30,8 +30,8 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/backend-common": "^0.9.13", - "@backstage/cli": "^0.10.1", + "@backstage/backend-common": "^0.9.14", + "@backstage/cli": "^0.10.2", "@elastic/elasticsearch-mock": "^0.3.0" }, "files": [ diff --git a/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchClientOptions.ts b/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchClientOptions.ts new file mode 100644 index 0000000000..73cb149c0a --- /dev/null +++ b/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchClientOptions.ts @@ -0,0 +1,125 @@ +/* + * 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 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 + * + * They are drawn from the `ClientOptions` class of `@elastic/elasticsearch`, + * but are maintained separately so that this interface is not coupled to + */ +export interface ElasticSearchClientOptions { + provider?: 'aws' | 'elastic'; + node?: + | string + | string[] + | ElasticSearchNodeOptions + | ElasticSearchNodeOptions[]; + nodes?: + | string + | string[] + | ElasticSearchNodeOptions + | ElasticSearchNodeOptions[]; + Transport?: ElasticSearchTransportConstructor; + Connection?: ElasticSearchConnectionConstructor; + maxRetries?: number; + requestTimeout?: number; + pingTimeout?: number; + sniffInterval?: number | boolean; + sniffOnStart?: boolean; + sniffEndpoint?: string; + sniffOnConnectionFault?: boolean; + resurrectStrategy?: 'ping' | 'optimistic' | 'none'; + suggestCompression?: boolean; + compression?: 'gzip'; + ssl?: TLSConnectionOptions; + agent?: ElasticSearchAgentOptions | ((opts?: any) => unknown) | false; + nodeFilter?: (connection: any) => boolean; + nodeSelector?: ((connections: any[]) => any) | string; + headers?: Record; + opaqueIdPrefix?: string; + name?: string | symbol; + auth?: ElasticSearchAuth; + proxy?: string | URL; + enableMetaHeader?: boolean; + cloud?: { + id: string; + username?: string; + password?: string; + }; + disablePrototypePoisoningProtection?: boolean | 'proto' | 'constructor'; +} + +export type ElasticSearchAuth = + | { + username: string; + password: string; + } + | { + apiKey: + | string + | { + id: string; + api_key: string; + }; + }; + +export interface ElasticSearchNodeOptions { + url: URL; + id?: string; + agent?: ElasticSearchAgentOptions; + ssl?: TLSConnectionOptions; + headers?: Record; + roles?: { + master: boolean; + data: boolean; + ingest: boolean; + ml: boolean; + }; +} + +export interface ElasticSearchAgentOptions { + keepAlive?: boolean; + keepAliveMsecs?: number; + maxSockets?: number; + maxFreeSockets?: number; +} + +export interface ElasticSearchConnectionConstructor { + new (opts?: any): any; + statuses: { + ALIVE: string; + DEAD: string; + }; + roles: { + MASTER: string; + DATA: string; + INGEST: string; + ML: string; + }; +} + +export interface ElasticSearchTransportConstructor { + new (opts?: any): any; + sniffReasons: { + SNIFF_ON_START: string; + SNIFF_INTERVAL: string; + SNIFF_ON_CONNECTION_FAULT: string; + DEFAULT: string; + }; +} 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 2877632013..1566a35b57 100644 --- a/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchSearchEngine.test.ts +++ b/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchSearchEngine.test.ts @@ -15,7 +15,6 @@ */ import { getVoidLogger } from '@backstage/backend-common'; -import { SearchEngine } from '@backstage/search-common'; import { Client } from '@elastic/elasticsearch'; import Mock from '@elastic/elasticsearch-mock'; import { @@ -33,28 +32,31 @@ class ElasticSearchSearchEngineForTranslatorTests extends ElasticSearchSearchEng } const mock = new Mock(); -const client = new Client({ +const options = { node: 'http://localhost:9200', Connection: mock.getConnection(), -}); +}; describe('ElasticSearchSearchEngine', () => { - let testSearchEngine: SearchEngine; + let testSearchEngine: ElasticSearchSearchEngine; let inspectableSearchEngine: ElasticSearchSearchEngineForTranslatorTests; + let client: Client; beforeEach(() => { testSearchEngine = new ElasticSearchSearchEngine( - client, + options, 'search', '', getVoidLogger(), ); inspectableSearchEngine = new ElasticSearchSearchEngineForTranslatorTests( - client, + options, 'search', '', getVoidLogger(), ); + // eslint-disable-next-line dot-notation + client = testSearchEngine['elasticSearchClient']; }); describe('queryTranslator', () => { diff --git a/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchSearchEngine.ts b/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchSearchEngine.ts index 7912c691b3..48caf0d4f1 100644 --- a/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchSearchEngine.ts +++ b/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchSearchEngine.ts @@ -30,6 +30,10 @@ import esb from 'elastic-builder'; import { isEmpty, isNaN as nan, isNumber } from 'lodash'; import { Logger } from 'winston'; +import type { ElasticSearchClientOptions } from './ElasticSearchClientOptions'; + +export type { ElasticSearchClientOptions }; + export type ConcreteElasticSearchQuery = { documentTypes?: string[]; elasticSearchQuery: Object; @@ -68,105 +72,49 @@ function isBlank(str: string) { * @public */ export class ElasticSearchSearchEngine implements SearchEngine { + private readonly elasticSearchClient: Client; + constructor( - private readonly elasticSearchClient: Client, + private readonly elasticSearchClientOptions: ElasticSearchClientOptions, private readonly aliasPostfix: string, private readonly indexPrefix: string, private readonly logger: Logger, - ) {} + ) { + this.elasticSearchClient = this.newClient(options => new Client(options)); + } - static async fromConfig(options: ElasticSearchOptions) { - const { - logger, - config, - aliasPostfix = `search`, - indexPrefix = ``, - } = options; + static async fromConfig({ + logger, + config, + aliasPostfix = `search`, + indexPrefix = ``, + }: ElasticSearchOptions) { + const options = await createElasticSearchClientOptions( + config.getConfig('search.elasticsearch'), + ); + if (options.provider === 'elastic') { + logger.info('Initializing Elastic.co ElasticSearch search engine.'); + } else if (options.provider === 'aws') { + logger.info('Initializing AWS ElasticSearch search engine.'); + } else { + logger.info('Initializing ElasticSearch search engine.'); + } return new ElasticSearchSearchEngine( - await ElasticSearchSearchEngine.constructElasticSearchClient( - logger, - config.getConfig('search.elasticsearch'), - ), + options, aliasPostfix, indexPrefix, logger, ); } - private static async constructElasticSearchClient( - logger: Logger, - config?: Config, - ) { - if (!config) { - throw new Error('No elastic search config found'); - } - - const clientOptionsConfig = config.getOptionalConfig('clientOptions'); - const sslConfig = clientOptionsConfig?.getOptionalConfig('ssl'); - - if (config.getOptionalString('provider') === 'elastic') { - logger.info('Initializing Elastic.co ElasticSearch search engine.'); - const authConfig = config.getConfig('auth'); - return new Client({ - cloud: { - id: config.getString('cloudId'), - }, - auth: { - username: authConfig.getString('username'), - password: authConfig.getString('password'), - }, - ...(sslConfig - ? { - ssl: { - rejectUnauthorized: - sslConfig?.getOptionalBoolean('rejectUnauthorized'), - }, - } - : {}), - }); - } - if (config.getOptionalString('provider') === 'aws') { - logger.info('Initializing AWS ElasticSearch search engine.'); - const awsCredentials = await awsGetCredentials(); - const AWSConnection = createAWSConnection(awsCredentials); - return new Client({ - node: config.getString('node'), - ...AWSConnection, - ...(sslConfig - ? { - ssl: { - rejectUnauthorized: - sslConfig?.getOptionalBoolean('rejectUnauthorized'), - }, - } - : {}), - }); - } - logger.info('Initializing ElasticSearch search engine.'); - const authConfig = config.getOptionalConfig('auth'); - const auth = - authConfig && - (authConfig.has('apiKey') - ? { - apiKey: authConfig.getString('apiKey'), - } - : { - username: authConfig.getString('username'), - password: authConfig.getString('password'), - }); - return new Client({ - node: config.getString('node'), - auth, - ...(sslConfig - ? { - ssl: { - rejectUnauthorized: - sslConfig?.getOptionalBoolean('rejectUnauthorized'), - }, - } - : {}), - }); + /** + * Create a custom search client from the derived elastic search + * configuration. This need not be the same client that the engine uses + * internally. + */ + newClient(create: (options: ElasticSearchClientOptions) => T): T { + return create(this.elasticSearchClientOptions); } protected translator(query: SearchQuery): ConcreteElasticSearchQuery { @@ -349,3 +297,75 @@ export function decodePageCursor(pageCursor?: string): { page: number } { export function encodePageCursor({ page }: { page: number }): string { return Buffer.from(`${page}`, 'utf-8').toString('base64'); } + +async function createElasticSearchClientOptions( + config?: Config, +): Promise { + if (!config) { + throw new Error('No elastic search config found'); + } + const clientOptionsConfig = config.getOptionalConfig('clientOptions'); + const sslConfig = clientOptionsConfig?.getOptionalConfig('ssl'); + + if (config.getOptionalString('provider') === 'elastic') { + const authConfig = config.getConfig('auth'); + return { + provider: 'elastic', + cloud: { + id: config.getString('cloudId'), + }, + auth: { + username: authConfig.getString('username'), + password: authConfig.getString('password'), + }, + ...(sslConfig + ? { + ssl: { + rejectUnauthorized: + sslConfig?.getOptionalBoolean('rejectUnauthorized'), + }, + } + : {}), + }; + } + if (config.getOptionalString('provider') === 'aws') { + const awsCredentials = await awsGetCredentials(); + const AWSConnection = createAWSConnection(awsCredentials); + return { + provider: 'aws', + node: config.getString('node'), + ...AWSConnection, + ...(sslConfig + ? { + ssl: { + rejectUnauthorized: + sslConfig?.getOptionalBoolean('rejectUnauthorized'), + }, + } + : {}), + }; + } + const authConfig = config.getOptionalConfig('auth'); + const auth = + authConfig && + (authConfig.has('apiKey') + ? { + apiKey: authConfig.getString('apiKey'), + } + : { + username: authConfig.getString('username'), + password: authConfig.getString('password'), + }); + return { + node: config.getString('node'), + auth, + ...(sslConfig + ? { + ssl: { + rejectUnauthorized: + sslConfig?.getOptionalBoolean('rejectUnauthorized'), + }, + } + : {}), + }; +} diff --git a/plugins/search-backend-module-elasticsearch/src/engines/index.ts b/plugins/search-backend-module-elasticsearch/src/engines/index.ts index 2f3641c114..d5eee37803 100644 --- a/plugins/search-backend-module-elasticsearch/src/engines/index.ts +++ b/plugins/search-backend-module-elasticsearch/src/engines/index.ts @@ -15,4 +15,7 @@ */ export { ElasticSearchSearchEngine } from './ElasticSearchSearchEngine'; -export type { ConcreteElasticSearchQuery } from './ElasticSearchSearchEngine'; +export type { + ConcreteElasticSearchQuery, + ElasticSearchClientOptions, +} from './ElasticSearchSearchEngine'; diff --git a/plugins/search-backend-module-elasticsearch/src/index.ts b/plugins/search-backend-module-elasticsearch/src/index.ts index b4dab93b76..8cf96de858 100644 --- a/plugins/search-backend-module-elasticsearch/src/index.ts +++ b/plugins/search-backend-module-elasticsearch/src/index.ts @@ -21,3 +21,4 @@ */ export { ElasticSearchSearchEngine } from './engines'; +export type { ElasticSearchClientOptions } from './engines'; diff --git a/plugins/search/package.json b/plugins/search/package.json index dbef8e5608..85996d65c3 100644 --- a/plugins/search/package.json +++ b/plugins/search/package.json @@ -32,8 +32,8 @@ "dependencies": { "@backstage/catalog-model": "^0.9.7", "@backstage/config": "^0.1.11", - "@backstage/core-components": "^0.8.0", - "@backstage/core-plugin-api": "^0.3.0", + "@backstage/core-components": "^0.8.1", + "@backstage/core-plugin-api": "^0.3.1", "@backstage/errors": "^0.1.4", "@backstage/plugin-catalog-react": "^0.6.5", "@backstage/search-common": "^0.2.1", @@ -52,8 +52,8 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.10.1", - "@backstage/core-app-api": "^0.2.0", + "@backstage/cli": "^0.10.2", + "@backstage/core-app-api": "^0.2.1", "@backstage/dev-utils": "^0.2.14", "@backstage/test-utils": "^0.1.24", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/sentry/package.json b/plugins/sentry/package.json index 41dc522e77..0f41bfaf16 100644 --- a/plugins/sentry/package.json +++ b/plugins/sentry/package.json @@ -33,8 +33,8 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.7", - "@backstage/core-components": "^0.8.0", - "@backstage/core-plugin-api": "^0.3.0", + "@backstage/core-components": "^0.8.1", + "@backstage/core-plugin-api": "^0.3.1", "@backstage/plugin-catalog-react": "^0.6.5", "@backstage/theme": "^0.2.14", "@material-ui/core": "^4.12.2", @@ -49,8 +49,8 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.10.1", - "@backstage/core-app-api": "^0.2.0", + "@backstage/cli": "^0.10.2", + "@backstage/core-app-api": "^0.2.1", "@backstage/dev-utils": "^0.2.14", "@backstage/test-utils": "^0.1.24", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/shortcuts/package.json b/plugins/shortcuts/package.json index a706d43e0e..a03dde616b 100644 --- a/plugins/shortcuts/package.json +++ b/plugins/shortcuts/package.json @@ -21,8 +21,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.8.0", - "@backstage/core-plugin-api": "^0.3.0", + "@backstage/core-components": "^0.8.1", + "@backstage/core-plugin-api": "^0.3.1", "@backstage/theme": "^0.2.14", "@backstage/types": "^0.1.1", "@material-ui/core": "^4.12.2", @@ -39,8 +39,8 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.10.1", - "@backstage/core-app-api": "^0.2.0", + "@backstage/cli": "^0.10.2", + "@backstage/core-app-api": "^0.2.1", "@backstage/dev-utils": "^0.2.14", "@backstage/test-utils": "^0.1.24", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/sonarqube/package.json b/plugins/sonarqube/package.json index 451ec86659..d1daf47b67 100644 --- a/plugins/sonarqube/package.json +++ b/plugins/sonarqube/package.json @@ -34,8 +34,8 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.7", - "@backstage/core-components": "^0.8.0", - "@backstage/core-plugin-api": "^0.3.0", + "@backstage/core-components": "^0.8.1", + "@backstage/core-plugin-api": "^0.3.1", "@backstage/plugin-catalog-react": "^0.6.5", "@backstage/theme": "^0.2.14", "@material-ui/core": "^4.12.2", @@ -50,8 +50,8 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.10.1", - "@backstage/core-app-api": "^0.2.0", + "@backstage/cli": "^0.10.2", + "@backstage/core-app-api": "^0.2.1", "@backstage/dev-utils": "^0.2.14", "@backstage/test-utils": "^0.1.24", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/splunk-on-call/package.json b/plugins/splunk-on-call/package.json index e5b55836a4..faa49a962e 100644 --- a/plugins/splunk-on-call/package.json +++ b/plugins/splunk-on-call/package.json @@ -32,8 +32,8 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.7", - "@backstage/core-components": "^0.8.0", - "@backstage/core-plugin-api": "^0.3.0", + "@backstage/core-components": "^0.8.1", + "@backstage/core-plugin-api": "^0.3.1", "@backstage/plugin-catalog-react": "^0.6.5", "@backstage/theme": "^0.2.14", "@material-ui/core": "^4.12.2", @@ -48,8 +48,8 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.10.1", - "@backstage/core-app-api": "^0.2.0", + "@backstage/cli": "^0.10.2", + "@backstage/core-app-api": "^0.2.1", "@backstage/dev-utils": "^0.2.14", "@backstage/test-utils": "^0.1.24", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/tech-insights/package.json b/plugins/tech-insights/package.json index 16e1ec6784..9466433b8e 100644 --- a/plugins/tech-insights/package.json +++ b/plugins/tech-insights/package.json @@ -20,26 +20,26 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.8.0", - "@backstage/core-plugin-api": "^0.3.0", + "@backstage/catalog-model": "^0.9.7", + "@backstage/core-components": "^0.8.1", + "@backstage/core-plugin-api": "^0.3.1", + "@backstage/errors": "^0.1.4", + "@backstage/plugin-catalog-react": "^0.6.5", + "@backstage/plugin-tech-insights-common": "^0.2.0", "@backstage/theme": "^0.2.14", + "@backstage/types": "^0.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", "react-router-dom": "6.0.0-beta.0", - "@backstage/plugin-catalog-react": "^0.6.5", - "@backstage/plugin-tech-insights-common": "^0.2.0", - "@backstage/catalog-model": "^0.9.7", - "@backstage/errors": "^0.1.4", - "@backstage/types": "^0.1.1" + "react-use": "^17.2.4" }, "peerDependencies": { "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.10.1", - "@backstage/core-app-api": "^0.2.0", + "@backstage/cli": "^0.10.2", + "@backstage/core-app-api": "^0.2.1", "@backstage/dev-utils": "^0.2.14", "@backstage/test-utils": "^0.1.24", "@testing-library/jest-dom": "^5.10.1", @@ -47,8 +47,8 @@ "@testing-library/user-event": "^13.1.8", "@types/jest": "^26.0.7", "@types/node": "^14.14.32", - "msw": "^0.35.0", - "cross-fetch": "^3.0.6" + "cross-fetch": "^3.0.6", + "msw": "^0.35.0" }, "files": [ "dist" diff --git a/plugins/tech-radar/package.json b/plugins/tech-radar/package.json index 7d6fdb4afe..b68a05c546 100644 --- a/plugins/tech-radar/package.json +++ b/plugins/tech-radar/package.json @@ -31,8 +31,8 @@ "start": "backstage-cli plugin:serve" }, "dependencies": { - "@backstage/core-components": "^0.8.0", - "@backstage/core-plugin-api": "^0.3.0", + "@backstage/core-components": "^0.8.1", + "@backstage/core-plugin-api": "^0.3.1", "@backstage/theme": "^0.2.14", "@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.10.1", - "@backstage/core-app-api": "^0.2.0", + "@backstage/cli": "^0.10.2", + "@backstage/core-app-api": "^0.2.1", "@backstage/dev-utils": "^0.2.14", "@backstage/test-utils": "^0.1.24", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/techdocs/CHANGELOG.md b/plugins/techdocs/CHANGELOG.md index 5cab32a73f..d12e2df366 100644 --- a/plugins/techdocs/CHANGELOG.md +++ b/plugins/techdocs/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-techdocs +## 0.12.10 + +### Patch Changes + +- e7cce2b603: Fix issue where assets weren't being fetched from the correct URL path for doc URLs without trailing slashes +- Updated dependencies + - @backstage/core-plugin-api@0.3.1 + - @backstage/core-components@0.8.1 + - @backstage/catalog-model@0.9.8 + - @backstage/plugin-catalog-react@0.6.7 + ## 0.12.9 ### Patch Changes diff --git a/plugins/techdocs/package.json b/plugins/techdocs/package.json index 4ac62a2b56..f9e3db913b 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": "0.12.9", + "version": "0.12.10", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -32,15 +32,15 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.9.7", + "@backstage/catalog-model": "^0.9.8", "@backstage/config": "^0.1.11", - "@backstage/core-components": "^0.8.0", - "@backstage/core-plugin-api": "^0.3.0", + "@backstage/core-components": "^0.8.1", + "@backstage/core-plugin-api": "^0.3.1", "@backstage/errors": "^0.1.5", "@backstage/integration": "^0.6.10", "@backstage/integration-react": "^0.1.15", "@backstage/plugin-catalog": "^0.7.4", - "@backstage/plugin-catalog-react": "^0.6.5", + "@backstage/plugin-catalog-react": "^0.6.7", "@backstage/plugin-search": "^0.5.1", "@backstage/theme": "^0.2.14", "@material-ui/core": "^4.12.2", @@ -62,8 +62,8 @@ "react-dom": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.10.1", - "@backstage/core-app-api": "^0.2.0", + "@backstage/cli": "^0.10.2", + "@backstage/core-app-api": "^0.2.1", "@backstage/dev-utils": "^0.2.14", "@backstage/test-utils": "^0.1.24", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/techdocs/src/client.test.ts b/plugins/techdocs/src/client.test.ts index bdb2f74772..d1a421c69f 100644 --- a/plugins/techdocs/src/client.test.ts +++ b/plugins/techdocs/src/client.test.ts @@ -62,6 +62,12 @@ describe('TechDocsStorageClient', () => { ).resolves.toEqual( `${mockBaseUrl}/static/docs/${mockEntity.namespace}/${mockEntity.kind}/${mockEntity.name}/test.js`, ); + + await expect( + storageApi.getBaseUrl('../test.js', mockEntity, 'some-docs-path'), + ).resolves.toEqual( + `${mockBaseUrl}/static/docs/${mockEntity.namespace}/${mockEntity.kind}/${mockEntity.name}/test.js`, + ); }); it('should return base url with correct entity structure', async () => { diff --git a/plugins/techdocs/src/client.ts b/plugins/techdocs/src/client.ts index 8a3ad59c03..f965cd7ced 100644 --- a/plugins/techdocs/src/client.ts +++ b/plugins/techdocs/src/client.ts @@ -263,9 +263,11 @@ export class TechDocsStorageClient implements TechDocsStorageApi { const { kind, namespace, name } = entityId; const apiOrigin = await this.getApiOrigin(); + const newBaseUrl = `${apiOrigin}/static/docs/${namespace}/${kind}/${name}/${path}`; + return new URL( oldBaseUrl, - `${apiOrigin}/static/docs/${namespace}/${kind}/${name}/${path}`, + newBaseUrl.endsWith('/') ? newBaseUrl : `${newBaseUrl}/`, ).toString(); } } diff --git a/plugins/todo/package.json b/plugins/todo/package.json index 10f2b311ca..deef4ac7f2 100644 --- a/plugins/todo/package.json +++ b/plugins/todo/package.json @@ -28,8 +28,8 @@ }, "dependencies": { "@backstage/catalog-model": "^0.9.7", - "@backstage/core-components": "^0.8.0", - "@backstage/core-plugin-api": "^0.3.0", + "@backstage/core-components": "^0.8.1", + "@backstage/core-plugin-api": "^0.3.1", "@backstage/errors": "^0.1.3", "@backstage/plugin-catalog-react": "^0.6.5", "@backstage/theme": "^0.2.14", @@ -42,8 +42,8 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.10.1", - "@backstage/core-app-api": "^0.2.0", + "@backstage/cli": "^0.10.2", + "@backstage/core-app-api": "^0.2.1", "@backstage/dev-utils": "^0.2.14", "@backstage/test-utils": "^0.1.24", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/user-settings/package.json b/plugins/user-settings/package.json index aaef5eb0a3..6d9f4bac21 100644 --- a/plugins/user-settings/package.json +++ b/plugins/user-settings/package.json @@ -31,8 +31,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.8.0", - "@backstage/core-plugin-api": "^0.3.0", + "@backstage/core-components": "^0.8.1", + "@backstage/core-plugin-api": "^0.3.1", "@backstage/theme": "^0.2.14", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", @@ -44,8 +44,8 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.10.1", - "@backstage/core-app-api": "^0.2.0", + "@backstage/cli": "^0.10.2", + "@backstage/core-app-api": "^0.2.1", "@backstage/dev-utils": "^0.2.14", "@backstage/test-utils": "^0.1.24", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/xcmetrics/package.json b/plugins/xcmetrics/package.json index 55488aa8f4..26fcc28f9f 100644 --- a/plugins/xcmetrics/package.json +++ b/plugins/xcmetrics/package.json @@ -21,8 +21,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.8.0", - "@backstage/core-plugin-api": "^0.3.0", + "@backstage/core-components": "^0.8.1", + "@backstage/core-plugin-api": "^0.3.1", "@backstage/errors": "^0.1.3", "@backstage/theme": "^0.2.14", "@material-ui/core": "^4.12.2", @@ -37,8 +37,8 @@ "react": "^16.13.1 || ^17.0.0" }, "devDependencies": { - "@backstage/cli": "^0.10.1", - "@backstage/core-app-api": "^0.2.0", + "@backstage/cli": "^0.10.2", + "@backstage/core-app-api": "^0.2.1", "@backstage/dev-utils": "^0.2.14", "@backstage/test-utils": "^0.1.24", "@testing-library/jest-dom": "^5.10.1", diff --git a/scripts/api-extractor.ts b/scripts/api-extractor.ts index 8ca7743cf2..22eae267ee 100644 --- a/scripts/api-extractor.ts +++ b/scripts/api-extractor.ts @@ -35,7 +35,6 @@ import { import { Program } from 'typescript'; import { DocNode, - DocSection, IDocNodeContainerParameters, TSDocTagSyntaxKind, } from '@microsoft/tsdoc'; @@ -51,7 +50,6 @@ import { DocHeading } from '@microsoft/api-documenter/lib/nodes/DocHeading'; import { CustomMarkdownEmitter } from '@microsoft/api-documenter/lib/markdown/CustomMarkdownEmitter'; import { IMarkdownEmitterContext } from '@microsoft/api-documenter/lib/markdown/MarkdownEmitter'; import { AstDeclaration } from '@microsoft/api-extractor/lib/analyzer/AstDeclaration'; -import { DocTableCell } from '@microsoft/api-documenter/lib/nodes/DocTableCell'; const tmpDir = resolvePath(__dirname, '../node_modules/.cache/api-extractor'); @@ -605,18 +603,9 @@ async function buildDocs({ }); for (const apiMember of apiModel.members) { - // This is a workaround for this check failing: https://github.com/microsoft/rushstack/blob/915aca8d8847b65981892f44f0544ccb00752792/apps/api-documenter/src/documenters/MarkdownDocumenter.ts#L991 - const description = new DocSection({ configuration }); - if (apiMember.tsdocComment !== undefined) { - this._appendAndMergeSection( - description, - apiMember.tsdocComment.summarySection, - ); - } - const row = new DocTableRow({ configuration }, [ this._createTitleCell(apiMember), - new DocTableCell({ configuration }, description.nodes), + this._createDescriptionCell(apiMember), ]); if (apiMember.kind === 'Package') { diff --git a/scripts/list-deprecations.js b/scripts/list-deprecations.js index 920b01c7b5..9a159927fc 100755 --- a/scripts/list-deprecations.js +++ b/scripts/list-deprecations.js @@ -129,7 +129,7 @@ async function main() { const srcDir = resolvePath(rootPath, packageDir, 'src'); if (await fs.pathExists(srcDir)) { - const files = await globby(['**/*.{js,jsx,ts,tsx}'], { + const files = await globby(['**/*.{js,jsx,ts,tsx,mjs,cjs}'], { cwd: srcDir, }); fileQueue.push( diff --git a/yarn.lock b/yarn.lock index 7a599498d0..cca08863df 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4846,54 +4846,45 @@ resolved "https://registry.npmjs.org/@mdx-js/util/-/util-1.6.22.tgz#219dfd89ae5b97a8801f015323ffa4b62f45718b" integrity sha512-H1rQc1ZOHANWBvPcW+JpGwr+juXSxM8Q8YCkm3GhZd8REu1fHR3z99CErO1p9pkcfcxZnMdIZdIsXkOHY0NilA== -"@microsoft/api-documenter@^7.13.68": - version "7.13.68" - resolved "https://registry.npmjs.org/@microsoft/api-documenter/-/api-documenter-7.13.68.tgz#c1e144764cac0684adefe78fd848d78c3f374681" - integrity sha512-cRjwK1TDyGxFGgCsRG8G0Yi3Z4akvfWgw1pWAxKFbm7ajlQQGZcHPnb+n4lKlSeQ5g/cxc7hcdw54Mvisne9Bg== +"@microsoft/api-documenter@^7.13.77": + version "7.13.77" + resolved "https://registry.npmjs.org/@microsoft/api-documenter/-/api-documenter-7.13.77.tgz#9760b42553679695620b578436ce14c357454e88" + integrity sha512-kot4VJlQUd7i0mFSM7IjyOhF0WWyVrmN9x3sXmd72imgCg9w795QHSkphJ7Qc6ZjhHcolaJvBByEPTKG4exZmA== dependencies: - "@microsoft/api-extractor-model" "7.13.16" + "@microsoft/api-extractor-model" "7.15.1" "@microsoft/tsdoc" "0.13.2" - "@rushstack/node-core-library" "3.43.2" - "@rushstack/ts-command-line" "4.10.4" + "@rushstack/node-core-library" "3.44.2" + "@rushstack/ts-command-line" "4.10.5" colors "~1.2.1" js-yaml "~3.13.1" resolve "~1.17.0" -"@microsoft/api-extractor-model@7.13.16": - version "7.13.16" - resolved "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.13.16.tgz#1d67541ebbcea32672c5fdd9392dc1579b2fc23a" - integrity sha512-ttdxVXsTWL5dd26W1YNLe3LgDsE0EE273aZlcLe58W0opymBybCYU1Mn+OHQM8BuErrdvdN8LdpWAAbkiOEN/Q== +"@microsoft/api-extractor-model@7.15.1", "@microsoft/api-extractor-model@^7.15.1": + version "7.15.1" + resolved "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.15.1.tgz#e52d68676e846d8b86e3b18e2654af39fba6e4a1" + integrity sha512-DWfS1o3oMY0mzdO3OuQbD/9vzn80jwM6tFd7XbiYnkpxwhD83LMGXz7NZWwSh+IaA+9w3LF4w62fT31Qq+dAMw== dependencies: "@microsoft/tsdoc" "0.13.2" "@microsoft/tsdoc-config" "~0.15.2" - "@rushstack/node-core-library" "3.43.2" + "@rushstack/node-core-library" "3.44.2" -"@microsoft/api-extractor-model@7.13.5", "@microsoft/api-extractor-model@^7.13.5": - version "7.13.5" - resolved "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.13.5.tgz#7836a81ba47b9a654062ed0361e4eee69afae51e" - integrity sha512-il6AebNltYo5hEtqXZw4DMvrwBPn6+F58TxwqmsLY+U+sSJNxaYn2jYksArrjErXVPR3gUgRMqD6zsdIkg+WEQ== +"@microsoft/api-extractor@^7.19.2": + version "7.19.2" + resolved "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.19.2.tgz#8c546003523163c1432f6e19506065f790d6182c" + integrity sha512-LxSa9lwp7eYtM4i5y/1n79QpotPKlmpCrVQbkb0LAHE1sCRHpZDTb6p3cMJthDhYPMjAYKOLfq639GwtZrg23Q== dependencies: + "@microsoft/api-extractor-model" "7.15.1" "@microsoft/tsdoc" "0.13.2" "@microsoft/tsdoc-config" "~0.15.2" - "@rushstack/node-core-library" "3.40.0" - -"@microsoft/api-extractor@^7.18.7": - version "7.18.7" - resolved "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.18.7.tgz#851d2413a3c5d696f7cc914eb59de7a7882b2e8b" - integrity sha512-JhtV8LoyLuIecbgCPyZQg08G1kngIRWpai2UzwNil9mGVGYiDZVeeKx8c2phmlPcogmMDm4oQROxyuiYt5sJiw== - dependencies: - "@microsoft/api-extractor-model" "7.13.5" - "@microsoft/tsdoc" "0.13.2" - "@microsoft/tsdoc-config" "~0.15.2" - "@rushstack/node-core-library" "3.40.0" - "@rushstack/rig-package" "0.3.0" - "@rushstack/ts-command-line" "4.9.0" + "@rushstack/node-core-library" "3.44.2" + "@rushstack/rig-package" "0.3.6" + "@rushstack/ts-command-line" "4.10.5" colors "~1.2.1" lodash "~4.17.15" resolve "~1.17.0" semver "~7.3.0" source-map "~0.6.1" - typescript "~4.3.5" + typescript "~4.5.2" "@microsoft/fetch-event-source@2.0.1": version "2.0.1" @@ -5649,25 +5640,10 @@ estree-walker "^2.0.1" picomatch "^2.2.2" -"@rushstack/node-core-library@3.40.0": - version "3.40.0" - resolved "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-3.40.0.tgz#2551915ea34e34ec2abb7172b9d7f4546144d9d4" - integrity sha512-P6uMPI7cqTdawLSPAG5BQrBu1MHlGRPqecp7ruIRgyukIEzkmh0QAnje4jAL/l1r3hw0qe4e+Dz5ZSnukT/Egg== - dependencies: - "@types/node" "10.17.13" - colors "~1.2.1" - fs-extra "~7.0.1" - import-lazy "~4.0.0" - jju "~1.4.0" - resolve "~1.17.0" - semver "~7.3.0" - timsort "~0.3.0" - z-schema "~3.18.3" - -"@rushstack/node-core-library@3.43.2": - version "3.43.2" - resolved "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-3.43.2.tgz#f067371a94fd92ed8f9d9aa8201c5e9e17a19f0f" - integrity sha512-b7AEhSf6CvZgvuDcWMFDeKx2mQSn9AVnMQVyxNxFeHCtLz3gJicqCOlw2GOXM8HKh6PInLdil/NVCDcstwSrIw== +"@rushstack/node-core-library@3.44.2": + version "3.44.2" + resolved "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-3.44.2.tgz#4fcd8f76887ae5968f2796f633a7e48f5ebd2271" + integrity sha512-lQ8Ct267UKkNSJSDxpBWn7SyyITWQ9l3Xqww0V+YY0rMt02r9eiGvwwPaU1ugJW7IMVo6r/HXvgbmpOSPyzGyg== dependencies: "@types/node" "12.20.24" colors "~1.2.1" @@ -5677,30 +5653,20 @@ resolve "~1.17.0" semver "~7.3.0" timsort "~0.3.0" - z-schema "~3.18.3" + z-schema "~5.0.2" -"@rushstack/rig-package@0.3.0": - version "0.3.0" - resolved "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.3.0.tgz#334ad2846797861361b3445d4cc9ae9164b1885c" - integrity sha512-Lj6noF7Q4BBm1hKiBDw94e6uZvq1xlBwM/d2cBFaPqXeGdV+G6r3qaCWfRiSXK0pcHpGGpV5Tb2MdfhVcO6G/g== +"@rushstack/rig-package@0.3.6": + version "0.3.6" + resolved "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.3.6.tgz#a57b53db59106fb93bcda36cad4f8602f508ebc6" + integrity sha512-H/uFsAT6cD4JCYrlQXYMZg+wPVECByFoJLGqfGRiTwSS5ngQw9QxnFV2mPG2LrxFUsMjLQ2lsrYr523700XzfA== dependencies: resolve "~1.17.0" strip-json-comments "~3.1.1" -"@rushstack/ts-command-line@4.10.4": - version "4.10.4" - resolved "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.10.4.tgz#05142b74e5cb207d3dd9b935c82f80d7fcb68042" - integrity sha512-4T5ao4UgDb6LmiRj4GumvG3VT/p6RSMgl7TN7S58ifaAGN2GeTNBajFCDdJs9QQP0d/4tA5p0SFzT7Ps5Byirg== - dependencies: - "@types/argparse" "1.0.38" - argparse "~1.0.9" - colors "~1.2.1" - string-argv "~0.3.1" - -"@rushstack/ts-command-line@4.9.0": - version "4.9.0" - resolved "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.9.0.tgz#781ba42cff73cae097b6d5241b6441e7cc2fe6e0" - integrity sha512-kmT8t+JfnvphISF1C5WwY56RefjwgajhSjs9J4ckvAFXZDXR6F5cvF5/RTh7fGCzIomg8esy2PHO/b52zFoZvA== +"@rushstack/ts-command-line@4.10.5": + version "4.10.5" + resolved "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.10.5.tgz#a31a44ddd24fe3a594e4ad91c22f3ea7668b43a9" + integrity sha512-5fVlTDbKsJ5WyT6L7NrnOlLG3uoITKxoqTPP2j0QZEi95kPbVT4+VPZaXXDJtkrao9qrIyig8pLK9WABY1bb3w== dependencies: "@types/argparse" "1.0.38" argparse "~1.0.9" @@ -7327,9 +7293,9 @@ "@types/d3-selection" "*" "@types/dagre@^0.7.44": - version "0.7.44" - resolved "https://registry.npmjs.org/@types/dagre/-/dagre-0.7.44.tgz#8f4b796b118ca29c132da7068fbc0d0351ee5851" - integrity sha512-N6HD+79w77ZVAaVO7JJDW5yJ9LAxM62FpgNGO9xEde+KVYjDRyhIMzfiErXpr1g0JPon9kwlBzoBK6s4fOww9Q== + version "0.7.46" + resolved "https://registry.npmjs.org/@types/dagre/-/dagre-0.7.46.tgz#c51ffcc62ca8a9affa07f5e06842acd840d3d442" + integrity sha512-ku3y+F8sPqmiB5Ugl22NpukI2dLAViJiWwdtueXLeuF4fxZozl+bytPSFVlLu/gDgaKiwobu3LBXXRWktIMiIA== "@types/debug@^4.0.0": version "4.1.7" @@ -7582,14 +7548,6 @@ resolved "https://registry.npmjs.org/@types/humanize-duration/-/humanize-duration-3.25.1.tgz#b6140d5fc00ff3917b3f521784abef4bc0387ccc" integrity sha512-WZU/4bb+lvzyDmZzjJtp++9mfKy6B3lH6gGISgkcz6SU8hMILKRM0vi08TxIsb0dQB4Gzo68MWLmctu6xqUi9g== -"@types/inquirer@^7.3.1": - version "7.3.1" - resolved "https://registry.npmjs.org/@types/inquirer/-/inquirer-7.3.1.tgz#1f231224e7df11ccfaf4cf9acbcc3b935fea292d" - integrity sha512-osD38QVIfcdgsPCT0V3lD7eH0OFurX71Jft18bZrsVQWVRt6TuxRzlr0GJLrxoHZR2V5ph7/qP8se/dcnI7o0g== - dependencies: - "@types/through" "*" - rxjs "^6.4.0" - "@types/inquirer@^7.3.3": version "7.3.3" resolved "https://registry.npmjs.org/@types/inquirer/-/inquirer-7.3.3.tgz#92e6676efb67fa6925c69a2ee638f67a822952ac" @@ -7598,6 +7556,14 @@ "@types/through" "*" rxjs "^6.4.0" +"@types/inquirer@^8.1.3": + version "8.1.3" + resolved "https://registry.npmjs.org/@types/inquirer/-/inquirer-8.1.3.tgz#dfda4c97cdbe304e4dceb378a80f79448ea5c8fe" + integrity sha512-AayK4ZL5ssPzR1OtnOLGAwpT0Dda3Xi/h1G0l1oJDNrowp7T1423q4Zb8/emr7tzRlCy4ssEri0LWVexAqHyKQ== + dependencies: + "@types/through" "*" + rxjs "^7.2.0" + "@types/is-function@^1.0.0": version "1.0.0" resolved "https://registry.npmjs.org/@types/is-function/-/is-function-1.0.0.tgz#1b0b819b1636c7baf0d6785d030d12edf70c3e83" @@ -7756,9 +7722,9 @@ "@types/node" "*" "@types/ldapjs@^2.2.0": - version "2.2.0" - resolved "https://registry.npmjs.org/@types/ldapjs/-/ldapjs-2.2.0.tgz#43ff78668c420b69a61b93e0acde71ddc0c7766d" - integrity sha512-L6ObSryRGOw0qYmN9B4w4s8UkoyRbxoWh7C1vc/Z6O/VXJQJqGBSyZPXRDmgKF+TZVGq/F7H37t0Cxrm+hLiRA== + version "2.2.2" + resolved "https://registry.npmjs.org/@types/ldapjs/-/ldapjs-2.2.2.tgz#cf79510d8dc34e5579442c2743f8a228427eb99c" + integrity sha512-U5HdnwIZ5uZa+f3usxdqgyfNmOROxOxXvQdQtsu6sKo8fte5vej9br2csHxPvXreAbAO1bs8/rdEzvCLpi67nQ== dependencies: "@types/node" "*" @@ -7897,36 +7863,31 @@ "@types/node" "*" form-data "^3.0.0" -"@types/node@*", "@types/node@>= 8", "@types/node@>=12.12.47", "@types/node@>=13.7.0", "@types/node@^14.0.10", "@types/node@^14.14.31", "@types/node@^14.14.32": - version "14.17.8" - resolved "https://registry.npmjs.org/@types/node/-/node-14.17.8.tgz#813b73ab7d82ac06ddfd2458b13c88459a3b319f" - integrity sha512-0CHLt50GbUmH/6MrlBIKNdWCglvlyQKkorRf08/0DIi0ryuTPP+ijWLSI19SbDTHSKaagGDELiImY4BSikt61w== +"@types/node@*", "@types/node@>= 8", "@types/node@>=12.12.47", "@types/node@>=13.7.0", "@types/node@^16.9.2": + version "16.11.6" + resolved "https://registry.npmjs.org/@types/node/-/node-16.11.6.tgz#6bef7a2a0ad684cf6e90fcfe31cecabd9ce0a3ae" + integrity sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w== -"@types/node@10.17.13", "@types/node@^10.1.0", "@types/node@^10.12.0": - version "10.17.13" - resolved "https://registry.npmjs.org/@types/node/-/node-10.17.13.tgz#ccebcdb990bd6139cd16e84c39dc2fb1023ca90c" - integrity sha512-pMCcqU2zT4TjqYFrWtYHKal7Sl30Ims6ulZ4UFXxI4xbtQqK/qqKwkDoBFCfooRqqmRu9vY3xaJRwxSh673aYg== - -"@types/node@12.20.24": +"@types/node@12.20.24", "@types/node@^12.7.1": version "12.20.24" resolved "https://registry.npmjs.org/@types/node/-/node-12.20.24.tgz#c37ac69cb2948afb4cef95f424fa0037971a9a5c" integrity sha512-yxDeaQIAJlMav7fH5AQqPH1u8YIuhYJXYBzxaQ4PifsU0GDO38MSdmEDeRlIxrKbC6NbEaaEHDanWb+y30U8SQ== -"@types/node@^12.7.1": - version "12.12.58" - resolved "https://registry.npmjs.org/@types/node/-/node-12.12.58.tgz#46dae9b2b9ee5992818c8f7cee01ff4ce03ab44c" - integrity sha512-Be46CNIHWAagEfINOjmriSxuv7IVcqbGe+sDSg2SYCEz/0CRBy7LRASGfRbD8KZkqoePU73Wsx3UvOSFcq/9hA== +"@types/node@^10.1.0", "@types/node@^10.12.0": + version "10.17.13" + resolved "https://registry.npmjs.org/@types/node/-/node-10.17.13.tgz#ccebcdb990bd6139cd16e84c39dc2fb1023ca90c" + integrity sha512-pMCcqU2zT4TjqYFrWtYHKal7Sl30Ims6ulZ4UFXxI4xbtQqK/qqKwkDoBFCfooRqqmRu9vY3xaJRwxSh673aYg== + +"@types/node@^14.0.10", "@types/node@^14.14.31", "@types/node@^14.14.32": + version "14.17.8" + resolved "https://registry.npmjs.org/@types/node/-/node-14.17.8.tgz#813b73ab7d82ac06ddfd2458b13c88459a3b319f" + integrity sha512-0CHLt50GbUmH/6MrlBIKNdWCglvlyQKkorRf08/0DIi0ryuTPP+ijWLSI19SbDTHSKaagGDELiImY4BSikt61w== "@types/node@^15.6.1": version "15.14.9" resolved "https://registry.npmjs.org/@types/node/-/node-15.14.9.tgz#bc43c990c3c9be7281868bbc7b8fdd6e2b57adfa" integrity sha512-qjd88DrCxupx/kJD5yQgZdcYKZKSIGBVDIBE1/LTGcNm3d2Np/jxojkdePDdfnBHJc5W7vSMpbJ1aB7p/Py69A== -"@types/node@^16.9.2": - version "16.11.6" - resolved "https://registry.npmjs.org/@types/node/-/node-16.11.6.tgz#6bef7a2a0ad684cf6e90fcfe31cecabd9ce0a3ae" - integrity sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w== - "@types/normalize-package-data@^2.4.0": version "2.4.0" resolved "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e" @@ -8382,6 +8343,14 @@ "@types/minipass" "*" "@types/node" "*" +"@types/tar@^6.1.1": + version "6.1.1" + resolved "https://registry.npmjs.org/@types/tar/-/tar-6.1.1.tgz#ab341ec1f149d7eb2a4f4ded56ff85f0d4fe7cb5" + integrity sha512-8mto3YZfVpqB1CHMaYz1TUYIQfZFbh/QbEq5Hsn6D0ilCfqRVCdalmc89B7vi3jhl9UYIk+dWDABShNfOkv5HA== + dependencies: + "@types/minipass" "*" + "@types/node" "*" + "@types/tern@*": version "0.23.3" resolved "https://registry.npmjs.org/@types/tern/-/tern-0.23.3.tgz#4b54538f04a88c9ff79de1f6f94f575a7f339460" @@ -9749,13 +9718,14 @@ 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.1, array.prototype.flat@^1.2.3: - version "1.2.3" - resolved "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz#0de82b426b0318dbfdb940089e38b043d37f6c7b" - integrity sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ== +array.prototype.flat@^1.2.1, 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" + integrity sha512-KaYU+S+ndVqyUnignHftkwc58o3uVU1jzczILJ1tN2YaIZpFIKBiP/x/j97E5MVPsaCloPbqWLB/8qCTVvT2qg== dependencies: + call-bind "^1.0.2" define-properties "^1.1.3" - es-abstract "^1.17.0-next.1" + es-abstract "^1.19.0" array.prototype.flatmap@^1.2.1, array.prototype.flatmap@^1.2.4, array.prototype.flatmap@^1.2.5: version "1.2.5" @@ -11931,11 +11901,6 @@ constants-browserify@^1.0.0: resolved "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" integrity sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U= -contains-path@^0.1.0: - version "0.1.0" - resolved "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a" - integrity sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo= - content-disposition@0.5.2: version "0.5.2" resolved "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" @@ -13034,10 +12999,10 @@ debug@4.3.1: dependencies: ms "2.1.2" -debug@^3.0.0, debug@^3.1.0, debug@^3.1.1, debug@^3.2.6: - version "3.2.6" - resolved "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" - integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== +debug@^3.0.0, debug@^3.1.0, debug@^3.1.1, debug@^3.2.6, debug@^3.2.7: + version "3.2.7" + resolved "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== dependencies: ms "^2.1.1" @@ -13428,14 +13393,6 @@ dockerode@^3.3.1: docker-modem "^3.0.0" tar-fs "~2.0.1" -doctrine@1.5.0: - version "1.5.0" - resolved "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" - integrity sha1-N53Ocw9hZvds76TmcHoVmwLFpvo= - dependencies: - esutils "^2.0.2" - isarray "^1.0.0" - doctrine@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" @@ -14226,20 +14183,21 @@ eslint-formatter-friendly@^7.0.0: strip-ansi "5.2.0" text-table "0.2.0" -eslint-import-resolver-node@^0.3.4: - version "0.3.4" - resolved "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz#85ffa81942c25012d8231096ddf679c03042c717" - integrity sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA== +eslint-import-resolver-node@^0.3.6: + version "0.3.6" + resolved "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz#4048b958395da89668252001dbd9eca6b83bacbd" + integrity sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw== dependencies: - debug "^2.6.9" - resolve "^1.13.1" + debug "^3.2.7" + resolve "^1.20.0" -eslint-module-utils@^2.1.1, eslint-module-utils@^2.6.0: - version "2.6.0" - resolved "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz#579ebd094f56af7797d19c9866c9c9486629bfa6" - integrity sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA== +eslint-module-utils@^2.1.1, eslint-module-utils@^2.7.1: + version "2.7.1" + resolved "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.1.tgz#b435001c9f8dd4ab7f6d0efcae4b9696d4c24b7c" + integrity sha512-fjoetBXQZq2tSTWZ9yWVl2KuFrTZZH3V+9iD1V1RfpDgxzJR+mPd/KZmMiA8gbPqdBzpNiEHOuT7IYEWxrH0zQ== dependencies: - debug "^2.6.9" + debug "^3.2.7" + find-up "^2.1.0" pkg-dir "^2.0.0" eslint-plugin-cypress@^2.10.3: @@ -14260,23 +14218,23 @@ eslint-plugin-graphql@^4.0.0: lodash.without "^4.4.0" eslint-plugin-import@^2.20.2: - version "2.22.1" - resolved "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.22.1.tgz#0896c7e6a0cf44109a2d97b95903c2bb689d7702" - integrity sha512-8K7JjINHOpH64ozkAhpT3sd+FswIZTfMZTjdx052pnWrgRCVfp8op9tbjpAk3DdUeI/Ba4C8OjdC0r90erHEOw== + version "2.25.3" + resolved "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.25.3.tgz#a554b5f66e08fb4f6dc99221866e57cfff824766" + integrity sha512-RzAVbby+72IB3iOEL8clzPLzL3wpDrlwjsTBAQXgyp5SeTqqY+0bFubwuo+y/HLhNZcXV4XqTBO4LGsfyHIDXg== dependencies: - array-includes "^3.1.1" - array.prototype.flat "^1.2.3" - contains-path "^0.1.0" + array-includes "^3.1.4" + array.prototype.flat "^1.2.5" debug "^2.6.9" - doctrine "1.5.0" - eslint-import-resolver-node "^0.3.4" - eslint-module-utils "^2.6.0" + doctrine "^2.1.0" + eslint-import-resolver-node "^0.3.6" + eslint-module-utils "^2.7.1" has "^1.0.3" + is-core-module "^2.8.0" + is-glob "^4.0.3" minimatch "^3.0.4" - object.values "^1.1.1" - read-pkg-up "^2.0.0" - resolve "^1.17.0" - tsconfig-paths "^3.9.0" + object.values "^1.1.5" + resolve "^1.20.0" + tsconfig-paths "^3.11.0" eslint-plugin-jest@^24.1.0: version "24.3.6" @@ -16991,9 +16949,9 @@ human-signals@^2.1.0: integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== humanize-duration@^3.25.1, humanize-duration@^3.26.0, humanize-duration@^3.27.0: - version "3.27.0" - resolved "https://registry.npmjs.org/humanize-duration/-/humanize-duration-3.27.0.tgz#3f781b7cf8022ad587f76b9839b60bc2b29636b2" - integrity sha512-qLo/08cNc3Tb0uD7jK0jAcU5cnqCM0n568918E7R2XhMr/+7F37p4EY062W/stg7tmzvknNn9b/1+UhVRzsYrQ== + version "3.27.1" + resolved "https://registry.npmjs.org/humanize-duration/-/humanize-duration-3.27.1.tgz#2cd4ea4b03bd92184aee6d90d77a8f3d7628df69" + integrity sha512-jCVkMl+EaM80rrMrAPl96SGG4NRac53UyI1o/yAzebDntEY6K6/Fj2HOjdPg8omTqIe5Y0wPBai2q5xXrIbarA== humanize-ms@^1.2.1: version "1.2.1" @@ -17516,10 +17474,10 @@ is-ci@^3.0.0: dependencies: ci-info "^3.1.1" -is-core-module@^2.1.0, is-core-module@^2.2.0: - version "2.4.0" - resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.4.0.tgz#8e9fc8e15027b011418026e98f0e6f4d86305cc1" - integrity sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A== +is-core-module@^2.1.0, is-core-module@^2.2.0, is-core-module@^2.8.0: + version "2.8.0" + resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz#0321336c3d0925e497fd97f5d95cb114a5ccd548" + integrity sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw== dependencies: has "^1.0.3" @@ -17642,7 +17600,7 @@ is-generator-function@^1.0.7: resolved "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.8.tgz#dfb5c2b120e02b0a8d9d2c6806cd5621aa922f7b" integrity sha512-2Omr/twNtufVZFr1GhxjOMFPAj2sjc/dKaIqBhvo4qciXfJmITGH6ZGd8eZYNHza8t1y0e01AuqRhJwfWp26WQ== -is-glob@4.0.1, is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: +is-glob@4.0.1: version "4.0.1" resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== @@ -17663,6 +17621,13 @@ is-glob@^3.0.0, is-glob@^3.1.0: dependencies: is-extglob "^2.1.0" +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + is-hexadecimal@^1.0.0: version "1.0.4" resolved "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz#cc35c97588da4bd49a8eedd6bc4082d44dcb23a7" @@ -19660,16 +19625,6 @@ load-json-file@^1.0.0: pinkie-promise "^2.0.0" strip-bom "^2.0.0" -load-json-file@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8" - integrity sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg= - dependencies: - graceful-fs "^4.1.2" - parse-json "^2.2.0" - pify "^2.0.0" - strip-bom "^3.0.0" - load-json-file@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" @@ -19808,7 +19763,7 @@ lodash.flattendeep@^4.0.0: resolved "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2" integrity sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI= -lodash.get@^4, lodash.get@^4.0.0, lodash.get@^4.4.2: +lodash.get@^4, lodash.get@^4.4.2: version "4.4.2" resolved "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk= @@ -19843,7 +19798,7 @@ lodash.isempty@^4.4.0: resolved "https://registry.npmjs.org/lodash.isempty/-/lodash.isempty-4.4.0.tgz#6f86cbedd8be4ec987be9aaf33c9684db1b31e7e" integrity sha1-b4bL7di+TsmHvpqvM8loTbGzHn4= -lodash.isequal@^4.0.0: +lodash.isequal@^4.5.0: version "4.5.0" resolved "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA= @@ -21327,12 +21282,7 @@ mkdirp@^1.0.3, mkdirp@^1.0.4: resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== -mock-fs@^5.1.0: - version "5.1.0" - resolved "https://registry.npmjs.org/mock-fs/-/mock-fs-5.1.0.tgz#a9aebd4e6d74a626f84b86eae8a372bd061754e8" - integrity sha512-wXdQ2nIk81TYIGLphUnbXl8akQpjb9ItfZefMcTxZcoe+djMkd5POU8fQdSEErxVAeT4CgDHWveYquys4H6Cmw== - -mock-fs@^5.1.1: +mock-fs@^5.1.0, mock-fs@^5.1.1: version "5.1.2" resolved "https://registry.npmjs.org/mock-fs/-/mock-fs-5.1.2.tgz#6fa486e06d00f8793a8d2228de980eff93ce6db7" integrity sha512-YkjQkdLulFrz0vD4BfNQdQRVmgycXTV7ykuHMlyv+C8WCHazpkiQRDthwa02kSyo8wKnY9wRptHfQLgmf0eR+A== @@ -22223,7 +22173,7 @@ object.pick@^1.3.0: dependencies: isobject "^3.0.1" -object.values@^1.1.0, object.values@^1.1.1, object.values@^1.1.5: +object.values@^1.1.0, object.values@^1.1.5: version "1.1.5" resolved "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz#959f63e3ce9ef108720333082131e4a459b716ac" integrity sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg== @@ -23058,13 +23008,6 @@ path-type@^1.0.0: pify "^2.0.0" pinkie-promise "^2.0.0" -path-type@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73" - integrity sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM= - dependencies: - pify "^2.0.0" - path-type@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" @@ -24922,14 +24865,6 @@ read-pkg-up@^1.0.1: find-up "^1.0.0" read-pkg "^1.0.0" -read-pkg-up@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be" - integrity sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4= - dependencies: - find-up "^2.0.0" - read-pkg "^2.0.0" - read-pkg-up@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz#3ed496685dba0f8fe118d0691dc51f4a1ff96f07" @@ -24956,15 +24891,6 @@ read-pkg@^1.0.0: normalize-package-data "^2.3.2" path-type "^1.0.0" -read-pkg@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8" - integrity sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg= - dependencies: - load-json-file "^2.0.0" - normalize-package-data "^2.3.2" - path-type "^2.0.0" - read-pkg@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" @@ -25604,7 +25530,7 @@ resolve-url@^0.2.1: resolved "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= -resolve@^1.1.6, resolve@^1.10.0, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.18.1, resolve@^1.19.0, resolve@^1.3.2, resolve@^1.9.0: +resolve@^1.1.6, resolve@^1.10.0, resolve@^1.12.0, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.18.1, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.3.2, resolve@^1.9.0: version "1.20.0" resolved "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== @@ -25856,21 +25782,7 @@ run-script-webpack-plugin@^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== -rxjs@^6.3.3, rxjs@^6.4.0, rxjs@^6.6.0: - version "6.6.2" - resolved "https://registry.npmjs.org/rxjs/-/rxjs-6.6.2.tgz#8096a7ac03f2cc4fe5860ef6e572810d9e01c0d2" - integrity sha512-BHdBMVoWC2sL26w//BCu3YzKT4s2jip/WhwsGEDmeKYBhKDZeYezVUnHatYB7L85v5xs0BAQmg6BEYJEKxBabg== - dependencies: - tslib "^1.9.0" - -rxjs@^6.6.3: - version "6.6.6" - resolved "https://registry.npmjs.org/rxjs/-/rxjs-6.6.6.tgz#14d8417aa5a07c5e633995b525e1e3c0dec03b70" - integrity sha512-/oTwee4N4iWzAMAL9xdGKjkEHmIwupR3oXbQjCKywF1BeFohswF3vZdogbmEF6pZkOsXTzWkrZszrWpQTByYVg== - dependencies: - tslib "^1.9.0" - -rxjs@^6.6.7: +rxjs@^6.3.3, rxjs@^6.4.0, rxjs@^6.6.0, rxjs@^6.6.3, rxjs@^6.6.7: version "6.6.7" resolved "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== @@ -28269,10 +28181,10 @@ 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== -tsconfig-paths@^3.9.0: - version "3.9.0" - resolved "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz#098547a6c4448807e8fcb8eae081064ee9a3c90b" - integrity sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw== +tsconfig-paths@^3.11.0: + version "3.12.0" + resolved "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.12.0.tgz#19769aca6ee8f6a1a341e38c8fa45dd9fb18899b" + integrity sha512-e5adrnOYT6zqVnWqZu7i/BQ3BnhzvGbjEjejFXO20lKIKpwTaupkCPgEfv4GZK1IBciJUEhYs3J3p75FdaTFVg== dependencies: "@types/json5" "^0.0.29" json5 "^1.0.1" @@ -28476,6 +28388,11 @@ typescript@~4.3.5: resolved "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz#4d1c37cc16e893973c45a06886b7113234f119f4" integrity sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA== +typescript@~4.5.2: + version "4.5.4" + resolved "https://registry.npmjs.org/typescript/-/typescript-4.5.4.tgz#a17d3a0263bf5c8723b9c52f43c5084edf13c2e8" + integrity sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg== + 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" @@ -29163,10 +29080,10 @@ validate.io-number@^1.0.3: resolved "https://registry.npmjs.org/validate.io-number/-/validate.io-number-1.0.3.tgz#f63ffeda248bf28a67a8d48e0e3b461a1665baf8" integrity sha1-9j/+2iSL8opnqNSODjtGGhZluvg= -validator@^8.0.0: - version "8.2.0" - resolved "https://registry.npmjs.org/validator/-/validator-8.2.0.tgz#3c1237290e37092355344fef78c231249dab77b9" - integrity sha512-Yw5wW34fSv5spzTXNkokD6S6/Oq92d8q/t14TqsS3fAiA1RYnxSFSIZ+CY3n6PGGRCq5HhJTSepQvFUS2QUDxA== +validator@^13.7.0: + version "13.7.0" + resolved "https://registry.npmjs.org/validator/-/validator-13.7.0.tgz#4f9658ba13ba8f3d82ee881d3516489ea85c0857" + integrity sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw== value-or-promise@1.0.11: version "1.0.11" @@ -30205,14 +30122,14 @@ yup@^0.32.9: property-expr "^2.0.4" toposort "^2.0.2" -z-schema@~3.18.3: - version "3.18.4" - resolved "https://registry.npmjs.org/z-schema/-/z-schema-3.18.4.tgz#ea8132b279533ee60be2485a02f7e3e42541a9a2" - integrity sha512-DUOKC/IhbkdLKKiV89gw9DUauTV8U/8yJl1sjf6MtDmzevLKOF2duNJ495S3MFVjqZarr+qNGCPbkg4mu4PpLw== +z-schema@~5.0.2: + version "5.0.2" + resolved "https://registry.npmjs.org/z-schema/-/z-schema-5.0.2.tgz#f410394b2c9fcb9edaf6a7511491c0bb4e89a504" + integrity sha512-40TH47ukMHq5HrzkeVE40Ad7eIDKaRV2b+Qpi2prLc9X9eFJFzV7tMe5aH12e6avaSS/u5l653EQOv+J9PirPw== dependencies: - lodash.get "^4.0.0" - lodash.isequal "^4.0.0" - validator "^8.0.0" + lodash.get "^4.4.2" + lodash.isequal "^4.5.0" + validator "^13.7.0" optionalDependencies: commander "^2.7.1"