Merge branch 'master' of https://github.com/backstage/backstage into bazaar-workflow-changes

This commit is contained in:
Lykke Axlin
2021-12-14 09:29:51 +01:00
27 changed files with 326 additions and 158 deletions
+6
View File
@@ -0,0 +1,6 @@
---
'@backstage/backend-common': patch
---
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.
+6
View File
@@ -0,0 +1,6 @@
---
'@backstage/core-app-api': patch
'@backstage/core-components': patch
---
Switched out usage of deprecated `OAuthRequestApi` types from `@backstage/core-plugin-api`.
@@ -0,0 +1,5 @@
---
'@backstage/plugin-techdocs': patch
---
Fix issue where assets weren't being fetched from the correct URL path for doc URLs without trailing slashes
+11
View File
@@ -0,0 +1,11 @@
---
'@backstage/core-plugin-api': patch
---
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`
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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"
+1 -1
View File
@@ -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 -1
View File
@@ -134,7 +134,7 @@
"@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",
+8 -12
View File
@@ -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';
@@ -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
@@ -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);
}
@@ -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;
}
}
@@ -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;
@@ -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;
};
+34 -17
View File
@@ -211,22 +211,21 @@ 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 = {
@@ -598,15 +597,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[];
@@ -679,10 +691,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>;
};
@@ -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.
+2 -2
View File
@@ -271,14 +271,13 @@ export const EntityRefLink: React_2.ForwardRefExoticComponent<
| 'children'
| 'key'
| 'id'
| 'className'
| 'classes'
| 'innerRef'
| 'defaultChecked'
| 'defaultValue'
| 'suppressContentEditableWarning'
| 'suppressHydrationWarning'
| 'accessKey'
| 'className'
| 'contentEditable'
| 'contextMenu'
| 'draggable'
@@ -519,6 +518,7 @@ export const EntityRefLink: React_2.ForwardRefExoticComponent<
| 'onTransitionEndCapture'
| 'component'
| 'variant'
| 'innerRef'
| 'download'
| 'href'
| 'hrefLang'
+6
View File
@@ -62,6 +62,12 @@ describe('TechDocsStorageClient', () => {
).resolves.toEqual(
`${mockBaseUrl}/static/docs/${mockEntity.namespace}/${mockEntity.kind}/${mockEntity.name}/test.js`,
);
await expect(
storageApi.getBaseUrl('../test.js', mockEntity, 'some-docs-path'),
).resolves.toEqual(
`${mockBaseUrl}/static/docs/${mockEntity.namespace}/${mockEntity.kind}/${mockEntity.name}/test.js`,
);
});
it('should return base url with correct entity structure', async () => {
+3 -1
View File
@@ -263,9 +263,11 @@ export class TechDocsStorageClient implements TechDocsStorageApi {
const { kind, namespace, name } = entityId;
const apiOrigin = await this.getApiOrigin();
const newBaseUrl = `${apiOrigin}/static/docs/${namespace}/${kind}/${name}/${path}`;
return new URL(
oldBaseUrl,
`${apiOrigin}/static/docs/${namespace}/${kind}/${name}/${path}`,
newBaseUrl.endsWith('/') ? newBaseUrl : `${newBaseUrl}/`,
).toString();
}
}
+11 -3
View File
@@ -8382,6 +8382,14 @@
"@types/minipass" "*"
"@types/node" "*"
"@types/tar@^6.1.1":
version "6.1.1"
resolved "https://registry.npmjs.org/@types/tar/-/tar-6.1.1.tgz#ab341ec1f149d7eb2a4f4ded56ff85f0d4fe7cb5"
integrity sha512-8mto3YZfVpqB1CHMaYz1TUYIQfZFbh/QbEq5Hsn6D0ilCfqRVCdalmc89B7vi3jhl9UYIk+dWDABShNfOkv5HA==
dependencies:
"@types/minipass" "*"
"@types/node" "*"
"@types/tern@*":
version "0.23.3"
resolved "https://registry.npmjs.org/@types/tern/-/tern-0.23.3.tgz#4b54538f04a88c9ff79de1f6f94f575a7f339460"
@@ -16991,9 +16999,9 @@ human-signals@^2.1.0:
integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==
humanize-duration@^3.25.1, humanize-duration@^3.26.0, humanize-duration@^3.27.0:
version "3.27.0"
resolved "https://registry.npmjs.org/humanize-duration/-/humanize-duration-3.27.0.tgz#3f781b7cf8022ad587f76b9839b60bc2b29636b2"
integrity sha512-qLo/08cNc3Tb0uD7jK0jAcU5cnqCM0n568918E7R2XhMr/+7F37p4EY062W/stg7tmzvknNn9b/1+UhVRzsYrQ==
version "3.27.1"
resolved "https://registry.npmjs.org/humanize-duration/-/humanize-duration-3.27.1.tgz#2cd4ea4b03bd92184aee6d90d77a8f3d7628df69"
integrity sha512-jCVkMl+EaM80rrMrAPl96SGG4NRac53UyI1o/yAzebDntEY6K6/Fj2HOjdPg8omTqIe5Y0wPBai2q5xXrIbarA==
humanize-ms@^1.2.1:
version "1.2.1"