Merge branch 'master' into mobile-sidebar
Signed-off-by: Philipp Hugenroth <philipph@spotify.com>
This commit is contained in:
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@backstage/plugin-github-actions': patch
|
||||
---
|
||||
|
||||
Show empty state only when workflow API call has completed
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
<Route
|
||||
path="/settings"
|
||||
element={
|
||||
<UserSettingsPage
|
||||
providerSettings={
|
||||
<ProviderSettingsItem
|
||||
title="ACME"
|
||||
description="Provides sign-in via ACME"
|
||||
apiRef={acmeAuthApiRef}
|
||||
icon={Star}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
```
|
||||
|
||||
@@ -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
|
||||
|
||||
+5
-4
@@ -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"
|
||||
],
|
||||
|
||||
@@ -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
|
||||
|
||||
+13
-13
@@ -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",
|
||||
|
||||
@@ -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 = (
|
||||
<EntityUserProfileCard variant="gridItem" />
|
||||
</Grid>
|
||||
<Grid item xs={12} md={6}>
|
||||
<EntityOwnershipCard variant="gridItem" />
|
||||
<EntityOwnershipCard
|
||||
variant="gridItem"
|
||||
entityFilterKind={customEntityFilterKind}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</EntityLayout.Route>
|
||||
@@ -539,7 +544,10 @@ const groupPage = (
|
||||
<EntityGroupProfileCard variant="gridItem" />
|
||||
</Grid>
|
||||
<Grid item xs={12} md={6}>
|
||||
<EntityOwnershipCard variant="gridItem" />
|
||||
<EntityOwnershipCard
|
||||
variant="gridItem"
|
||||
entityFilterKind={customEntityFilterKind}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<EntityMembersListCard />
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:'), {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -325,6 +325,7 @@ interface LocationEntityV1alpha1 extends Entity {
|
||||
type?: string;
|
||||
target?: string;
|
||||
targets?: string[];
|
||||
presence?: 'required' | 'optional';
|
||||
};
|
||||
}
|
||||
export { LocationEntityV1alpha1 as LocationEntity };
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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/);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -30,6 +30,7 @@ export interface LocationEntityV1alpha1 extends Entity {
|
||||
type?: string;
|
||||
target?: string;
|
||||
targets?: string[];
|
||||
presence?: 'required' | 'optional';
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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: {}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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})/`],
|
||||
};
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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]),
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<PendingAuthRequest[]>;
|
||||
authRequest$(): Observable<PendingOAuthRequest[]>;
|
||||
// (undocumented)
|
||||
createAuthRequester<T>(options: AuthRequesterOptions<T>): AuthRequester<T>;
|
||||
createAuthRequester<T>(options: OAuthRequesterOptions<T>): OAuthRequester<T>;
|
||||
}
|
||||
|
||||
// @public
|
||||
@@ -539,9 +537,7 @@ export type OneLoginAuthCreateOptions = {
|
||||
discoveryApi: DiscoveryApi;
|
||||
oauthRequestApi: OAuthRequestApi;
|
||||
environment?: string;
|
||||
provider?: AuthProvider & {
|
||||
id: string;
|
||||
};
|
||||
provider?: AuthProviderInfo;
|
||||
};
|
||||
|
||||
// @public
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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<T>(options: AuthRequesterOptions<T>) {
|
||||
createAuthRequester<T>(options: OAuthRequesterOptions<T>) {
|
||||
return this.real.createAuthRequester(options);
|
||||
}
|
||||
|
||||
|
||||
+15
-9
@@ -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<PendingAuthRequest[]>([]);
|
||||
private currentRequests: PendingAuthRequest[] = [];
|
||||
private readonly subject = new BehaviorSubject<PendingOAuthRequest[]>([]);
|
||||
private currentRequests: PendingOAuthRequest[] = [];
|
||||
private handlerCount = 0;
|
||||
|
||||
createAuthRequester<T>(options: AuthRequesterOptions<T>): AuthRequester<T> {
|
||||
createAuthRequester<T>(options: OAuthRequesterOptions<T>): OAuthRequester<T> {
|
||||
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<T>();
|
||||
|
||||
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<any>,
|
||||
options: AuthRequesterOptions<any>,
|
||||
): PendingAuthRequest | undefined {
|
||||
options: OAuthRequesterOptions<any>,
|
||||
): PendingOAuthRequest | undefined {
|
||||
const { scopes } = request;
|
||||
if (!scopes) {
|
||||
return undefined;
|
||||
@@ -88,7 +94,7 @@ export class OAuthRequestManager implements OAuthRequestApi {
|
||||
};
|
||||
}
|
||||
|
||||
authRequest$(): Observable<PendingAuthRequest[]> {
|
||||
authRequest$(): Observable<PendingOAuthRequest[]> {
|
||||
return this.subject;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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<AuthSession> = {
|
||||
* 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<AuthSession>
|
||||
{
|
||||
private readonly discoveryApi: DiscoveryApi;
|
||||
private readonly environment: string;
|
||||
private readonly provider: AuthProvider & { id: string };
|
||||
private readonly provider: AuthProviderInfo;
|
||||
private readonly joinScopesFunc: (scopes: Set<string>) => string;
|
||||
private readonly authRequester: AuthRequester<AuthSession>;
|
||||
private readonly authRequester: OAuthRequester<AuthSession>;
|
||||
private readonly sessionTransform: (response: any) => Promise<AuthSession>;
|
||||
|
||||
constructor(options: Options<AuthSession>) {
|
||||
|
||||
@@ -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<DirectAuthResponse> {
|
||||
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;
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
# @backstage/core-components
|
||||
|
||||
## 0.8.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 2c17e5b073: Items in `<SidebarSubmenu>` 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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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<Theme>(
|
||||
);
|
||||
|
||||
type RowProps = {
|
||||
request: PendingAuthRequest;
|
||||
request: PendingOAuthRequest;
|
||||
busy: boolean;
|
||||
setBusy: (busy: boolean) => void;
|
||||
};
|
||||
|
||||
@@ -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<SidebarSubmenuItemProps>;
|
||||
}) => {
|
||||
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<SidebarItemWithSubmenuProps>) => {
|
||||
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 = (
|
||||
<Badge
|
||||
color="secondary"
|
||||
variant="dot"
|
||||
overlap="circular"
|
||||
className={isOpen ? '' : classes.closedItemIcon}
|
||||
invisible={!hasNotifications}
|
||||
>
|
||||
<Icon fontSize="small" />
|
||||
</Badge>
|
||||
);
|
||||
const openContent = (
|
||||
<>
|
||||
<div data-testid="login-button" className={classes.iconContainer}>
|
||||
{itemIcon}
|
||||
</div>
|
||||
{text && (
|
||||
<Typography variant="subtitle2" className={classes.label}>
|
||||
{text}
|
||||
</Typography>
|
||||
)}
|
||||
<div className={classes.secondaryAction}>{}</div>
|
||||
</>
|
||||
);*/
|
||||
|
||||
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,
|
||||
|
||||
@@ -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 = () => (
|
||||
<SidebarSubmenu title="Catalog">
|
||||
<SidebarSubmenuItem title="Tools" to="/1" icon={BuildRoundedIcon} />
|
||||
<SidebarSubmenuItem title="APIs" to="/2" icon={CloudQueueIcon} />
|
||||
<SidebarSubmenuItem
|
||||
title="Services"
|
||||
to="/3"
|
||||
icon={SettingsApplications}
|
||||
/>
|
||||
<SidebarSubmenuItem
|
||||
title="Libraries"
|
||||
to="/4"
|
||||
icon={LibraryBooksOutlinedIcon}
|
||||
/>
|
||||
<SidebarSubmenuItem title="Websites" to="/5" icon={WebOutlinedIcon} />
|
||||
<SidebarSubmenuItem title="Components" to="/3" icon={AppsIcon} />
|
||||
<SidebarSubmenuItem
|
||||
title="Misc"
|
||||
to="/6"
|
||||
|
||||
@@ -30,6 +30,7 @@ import ArrowDropDownIcon from '@material-ui/icons/ArrowDropDown';
|
||||
import ArrowDropUpIcon from '@material-ui/icons/ArrowDropUp';
|
||||
import { SidebarItemWithSubmenuContext } from './config';
|
||||
import { SidebarContext } from '../..';
|
||||
import { isLocationMatch } from './utils';
|
||||
|
||||
const useStyles = makeStyles<BackstageTheme>(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 (
|
||||
<div className={classes.itemContainer}>
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<AnalyticsApi>;
|
||||
|
||||
// @public
|
||||
// @alpha
|
||||
export const AnalyticsContext: (options: {
|
||||
attributes: Partial<AnalyticsContextValue>;
|
||||
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<AppThemeApi>;
|
||||
|
||||
// @public
|
||||
// @alpha
|
||||
export const atlassianAuthApiRef: ApiRef<
|
||||
OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi
|
||||
>;
|
||||
@@ -206,27 +207,26 @@ export function attachComponentData<P>(
|
||||
data: unknown,
|
||||
): void;
|
||||
|
||||
// @public
|
||||
// @public @deprecated
|
||||
export const auth0AuthApiRef: ApiRef<
|
||||
OpenIdConnectApi & ProfileInfoApi & BackstageIdentityApi & SessionApi
|
||||
>;
|
||||
|
||||
// @public @deprecated (undocumented)
|
||||
export type AuthProvider = Omit<AuthProviderInfo, 'id'>;
|
||||
|
||||
// @public
|
||||
export type AuthProvider = {
|
||||
export type AuthProviderInfo = {
|
||||
id: string;
|
||||
title: string;
|
||||
icon: IconComponent;
|
||||
};
|
||||
|
||||
// @public
|
||||
export type AuthRequester<AuthResponse> = (
|
||||
scopes: Set<string>,
|
||||
) => Promise<AuthResponse>;
|
||||
// @public @deprecated (undocumented)
|
||||
export type AuthRequester<T> = OAuthRequester<T>;
|
||||
|
||||
// @public
|
||||
export type AuthRequesterOptions<AuthResponse> = {
|
||||
provider: AuthProvider;
|
||||
onAuthRequest(scopes: Set<string>): Promise<AuthResponse>;
|
||||
};
|
||||
// @public @deprecated (undocumented)
|
||||
export type AuthRequesterOptions<T> = OAuthRequesterOptions<T>;
|
||||
|
||||
// @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<T>(
|
||||
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<AuthResponse>(
|
||||
options: AuthRequesterOptions<AuthResponse>,
|
||||
): AuthRequester<AuthResponse>;
|
||||
authRequest$(): Observable_2<PendingAuthRequest[]>;
|
||||
createAuthRequester<OAuthResponse>(
|
||||
options: OAuthRequesterOptions<OAuthResponse>,
|
||||
): OAuthRequester<OAuthResponse>;
|
||||
authRequest$(): Observable_2<PendingOAuthRequest[]>;
|
||||
};
|
||||
|
||||
// @public
|
||||
export const oauthRequestApiRef: ApiRef<OAuthRequestApi>;
|
||||
|
||||
// @public
|
||||
export type OAuthRequester<TAuthResponse> = (
|
||||
scopes: Set<string>,
|
||||
) => Promise<TAuthResponse>;
|
||||
|
||||
// @public
|
||||
export type OAuthRequesterOptions<TOAuthResponse> = {
|
||||
provider: Omit<AuthProviderInfo, 'id'> & {
|
||||
id?: string;
|
||||
};
|
||||
onAuthRequest(scopes: Set<string>): Promise<TOAuthResponse>;
|
||||
};
|
||||
|
||||
// @public
|
||||
export type OAuthScope = string | string[];
|
||||
|
||||
@@ -616,7 +629,7 @@ export type Observable<T> = Observable_2<T>;
|
||||
// @public @deprecated
|
||||
export type Observer<T> = Observer_2<T>;
|
||||
|
||||
// @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<SvgIconProps>;
|
||||
|
||||
// @public
|
||||
// @alpha
|
||||
export const oneloginAuthApiRef: ApiRef<
|
||||
OAuthApi &
|
||||
OpenIdConnectApi &
|
||||
@@ -679,10 +692,15 @@ export type PathParams<S extends string> = {
|
||||
[name in ParamNames<S>]: string;
|
||||
};
|
||||
|
||||
// @public @deprecated (undocumented)
|
||||
export type PendingAuthRequest = PendingOAuthRequest;
|
||||
|
||||
// @public
|
||||
export type PendingAuthRequest = {
|
||||
provider: AuthProvider;
|
||||
reject: () => void;
|
||||
export type PendingOAuthRequest = {
|
||||
provider: Omit<AuthProviderInfo, 'id'> & {
|
||||
id?: string;
|
||||
};
|
||||
reject(): void;
|
||||
trigger(): Promise<void>;
|
||||
};
|
||||
|
||||
@@ -738,7 +756,7 @@ export type RouteRef<Params extends AnyParams = any> = {
|
||||
title?: string;
|
||||
};
|
||||
|
||||
// @public
|
||||
// @public @deprecated
|
||||
export const samlAuthApiRef: ApiRef<
|
||||
ProfileInfoApi & BackstageIdentityApi & SessionApi
|
||||
>;
|
||||
@@ -803,7 +821,7 @@ export type TypesToApiRefs<T> = {
|
||||
[key in keyof T]: ApiRef<T[key]>;
|
||||
};
|
||||
|
||||
// @public
|
||||
// @alpha
|
||||
export function useAnalytics(): AnalyticsTracker;
|
||||
|
||||
// @public
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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<AnalyticsContextValue>;
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -35,7 +35,7 @@ function useAnalyticsApi(): AnalyticsApi {
|
||||
/**
|
||||
* Gets a pre-configured analytics tracker.
|
||||
*
|
||||
* @public
|
||||
* @alpha
|
||||
*/
|
||||
export function useAnalytics(): AnalyticsTracker {
|
||||
const trackerRef = useRef<Tracker | null>(null);
|
||||
|
||||
@@ -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<AnalyticsApi> = createApiRef({
|
||||
id: 'core.analytics',
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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<AuthProviderInfo, 'id'>;
|
||||
|
||||
/**
|
||||
* 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<AuthResponse> = {
|
||||
export type OAuthRequesterOptions<TOAuthResponse> = {
|
||||
/**
|
||||
* 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<AuthProviderInfo, 'id'> & { id?: string };
|
||||
|
||||
/**
|
||||
* Implementation of the auth flow, which will be called synchronously when
|
||||
* trigger() is called on an auth requests.
|
||||
*/
|
||||
onAuthRequest(scopes: Set<string>): Promise<AuthResponse>;
|
||||
onAuthRequest(scopes: Set<string>): Promise<TOAuthResponse>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @deprecated Use OAuthRequesterOptions instead
|
||||
*/
|
||||
export type AuthRequesterOptions<T> = OAuthRequesterOptions<T>;
|
||||
|
||||
/**
|
||||
* 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<AuthResponse> = {
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type AuthRequester<AuthResponse> = (
|
||||
export type OAuthRequester<TAuthResponse> = (
|
||||
scopes: Set<string>,
|
||||
) => Promise<AuthResponse>;
|
||||
) => Promise<TAuthResponse>;
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @deprecated Use OAuthRequester instead
|
||||
*/
|
||||
export type AuthRequester<T> = OAuthRequester<T>;
|
||||
|
||||
/**
|
||||
* An pending auth request for a single auth provider. The request will remain in this pending
|
||||
@@ -88,16 +86,18 @@ export type AuthRequester<AuthResponse> = (
|
||||
*
|
||||
* @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<AuthProviderInfo, 'id'> & { 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<void>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @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<AuthResponse>(
|
||||
options: AuthRequesterOptions<AuthResponse>,
|
||||
): AuthRequester<AuthResponse>;
|
||||
createAuthRequester<OAuthResponse>(
|
||||
options: OAuthRequesterOptions<OAuthResponse>,
|
||||
): OAuthRequester<OAuthResponse>;
|
||||
|
||||
/**
|
||||
* 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<PendingAuthRequest[]>;
|
||||
authRequest$(): Observable<PendingOAuthRequest[]>;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -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<OAuthApi & OpenIDConnectApi>({ ... })
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
},
|
||||
"prettier": "@spotify/prettier-config",
|
||||
"lint-staged": {
|
||||
"*.{js,jsx,ts,tsx}": [
|
||||
"*.{js,jsx,ts,tsx,mjs,cjs}": [
|
||||
"eslint --fix",
|
||||
"prettier --write"
|
||||
],
|
||||
|
||||
@@ -59,7 +59,7 @@ module.exports = ({ args }) => {
|
||||
},
|
||||
},
|
||||
{
|
||||
test: /\.(jsx?|mjs)$/,
|
||||
test: /\.(jsx?|mjs|cjs)$/,
|
||||
exclude: /node_modules/,
|
||||
loader: require.resolve('@sucrase/webpack-loader'),
|
||||
options: {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -47,6 +47,16 @@ export type AtlassianProviderOptions = {
|
||||
};
|
||||
};
|
||||
|
||||
// @public
|
||||
export type AuthHandler<AuthResult> = (
|
||||
input: AuthResult,
|
||||
) => Promise<AuthHandlerResult>;
|
||||
|
||||
// @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<OidcAuthResult>;
|
||||
signIn?: {
|
||||
resolver?: SignInResolver<OidcAuthResult>;
|
||||
};
|
||||
};
|
||||
|
||||
// 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<AuthResult> = {
|
||||
profile: ProfileInfo;
|
||||
result: AuthResult;
|
||||
};
|
||||
|
||||
// @public
|
||||
export type SignInResolver<AuthResult> = (
|
||||
info: SignInInfo<AuthResult>,
|
||||
context: {
|
||||
tokenIssuer: TokenIssuer;
|
||||
catalogIdentityClient: CatalogIdentityClient;
|
||||
logger: Logger_2;
|
||||
},
|
||||
) => Promise<BackstageSignInResult>;
|
||||
|
||||
// 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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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<AuthResult>;
|
||||
authHandler: AuthHandler<AuthResult>;
|
||||
signInResolver?: SignInResolver<OidcAuthResult>;
|
||||
authHandler: AuthHandler<OidcAuthResult>;
|
||||
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<AuthResult>;
|
||||
private readonly authHandler: AuthHandler<AuthResult>;
|
||||
private readonly signInResolver?: SignInResolver<OidcAuthResult>;
|
||||
private readonly authHandler: AuthHandler<OidcAuthResult>;
|
||||
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<AuthResult, PrivateInfo>,
|
||||
done: PassportDoneCallback<OidcAuthResult, PrivateInfo>,
|
||||
) => {
|
||||
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<OAuthResponse> {
|
||||
private async handleResult(result: OidcAuthResult): Promise<OAuthResponse> {
|
||||
const { profile } = await this.authHandler(result);
|
||||
const response: OAuthResponse = {
|
||||
providerInfo: {
|
||||
@@ -210,27 +214,37 @@ export class OidcAuthProvider implements OAuthHandlers {
|
||||
}
|
||||
}
|
||||
|
||||
export const oAuth2DefaultSignInResolver: SignInResolver<AuthResult> = async (
|
||||
info,
|
||||
ctx,
|
||||
) => {
|
||||
const { profile } = info;
|
||||
export const oAuth2DefaultSignInResolver: SignInResolver<OidcAuthResult> =
|
||||
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<AuthResult>;
|
||||
authHandler?: AuthHandler<OidcAuthResult>;
|
||||
|
||||
signIn?: {
|
||||
resolver?: SignInResolver<AuthResult>;
|
||||
resolver?: SignInResolver<OidcAuthResult>;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -260,7 +274,7 @@ export const createOidcProvider = (
|
||||
tokenIssuer,
|
||||
});
|
||||
|
||||
const authHandler: AuthHandler<AuthResult> = options?.authHandler
|
||||
const authHandler: AuthHandler<OidcAuthResult> = options?.authHandler
|
||||
? options.authHandler
|
||||
: async ({ userinfo }) => ({
|
||||
profile: {
|
||||
@@ -271,7 +285,7 @@ export const createOidcProvider = (
|
||||
});
|
||||
const signInResolverFn =
|
||||
options?.signIn?.resolver ?? oAuth2DefaultSignInResolver;
|
||||
const signInResolver: SignInResolver<AuthResult> = info =>
|
||||
const signInResolver: SignInResolver<OidcAuthResult> = info =>
|
||||
signInResolverFn(info, {
|
||||
catalogIdentityClient,
|
||||
tokenIssuer,
|
||||
|
||||
@@ -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<AuthResult> = {
|
||||
/**
|
||||
* The simple profile passed down for use in the frontend.
|
||||
@@ -252,6 +256,11 @@ export type SignInInfo<AuthResult> = {
|
||||
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<AuthResult> = (
|
||||
info: SignInInfo<AuthResult>,
|
||||
context: {
|
||||
@@ -261,6 +270,10 @@ export type SignInResolver<AuthResult> = (
|
||||
},
|
||||
) => Promise<BackstageSignInResult>;
|
||||
|
||||
/**
|
||||
* 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<AuthResult> = (
|
||||
input: AuthResult,
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
];
|
||||
|
||||
<Route
|
||||
path="/azure-pull-requests"
|
||||
element={
|
||||
<AzurePullRequestsPage
|
||||
projectName="{PROJECT_NAME}"
|
||||
defaultColumnConfigs={COLUMN_CONFIGS}
|
||||
/>
|
||||
}
|
||||
/>;
|
||||
```
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-common@0.9.14
|
||||
- @backstage/plugin-azure-devops-common@0.1.3
|
||||
|
||||
## 0.2.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
];
|
||||
|
||||
<Route
|
||||
path="/azure-pull-requests"
|
||||
element={
|
||||
<AzurePullRequestsPage
|
||||
projectName="{PROJECT_NAME}"
|
||||
defaultColumnConfigs={COLUMN_CONFIGS}
|
||||
/>
|
||||
}
|
||||
/>;
|
||||
```
|
||||
|
||||
## 0.1.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
];
|
||||
|
||||
<Route
|
||||
path="/azure-pull-requests"
|
||||
element={
|
||||
<AzurePullRequestsPage
|
||||
projectName="{PROJECT_NAME}"
|
||||
defaultColumnConfigs={COLUMN_CONFIGS}
|
||||
/>
|
||||
}
|
||||
/>;
|
||||
```
|
||||
|
||||
- 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
|
||||
|
||||
@@ -6,9 +6,55 @@
|
||||
/// <reference types="react" />
|
||||
|
||||
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)
|
||||
```
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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 <Progress />;
|
||||
}
|
||||
|
||||
@@ -88,25 +49,50 @@ const PullRequestsPageContent = ({
|
||||
return <ResponseErrorPanel error={error} />;
|
||||
}
|
||||
|
||||
return <PullRequestGrid pullRequestGroups={pullRequestGroups} />;
|
||||
return <PullRequestGrid pullRequestGroups={pullRequestGroups ?? []} />;
|
||||
};
|
||||
|
||||
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,
|
||||
);
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
+39
@@ -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);
|
||||
};
|
||||
}
|
||||
+51
@@ -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);
|
||||
});
|
||||
};
|
||||
}
|
||||
+50
@@ -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);
|
||||
};
|
||||
}
|
||||
+150
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
}
|
||||
+42
@@ -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);
|
||||
};
|
||||
}
|
||||
+65
@@ -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);
|
||||
});
|
||||
};
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user