Merge branch 'master' of https://github.com/backstage/backstage into marley/7678-pull-request-custom-filters

This commit is contained in:
Marley Powell
2021-12-14 14:34:14 +00:00
60 changed files with 781 additions and 375 deletions
+40 -3
View File
@@ -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
@@ -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,
+6
View File
@@ -1,5 +1,11 @@
# @backstage/plugin-catalog-react
## 0.6.6
### Patch Changes
- 4c0f0b2003: Removed dependency on `@backstage/core-app-api`.
## 0.6.5
### Patch Changes
+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'
+2 -2
View File
@@ -1,7 +1,7 @@
{
"name": "@backstage/plugin-catalog-react",
"description": "A frontend library that helps other Backstage plugins interact with the catalog",
"version": "0.6.5",
"version": "0.6.6",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
@@ -31,7 +31,6 @@
"dependencies": {
"@backstage/catalog-client": "^0.5.2",
"@backstage/catalog-model": "^0.9.7",
"@backstage/core-app-api": "^0.2.0",
"@backstage/core-components": "^0.8.0",
"@backstage/core-plugin-api": "^0.3.0",
"@backstage/errors": "^0.1.4",
@@ -54,6 +53,7 @@
},
"devDependencies": {
"@backstage/cli": "^0.10.1",
"@backstage/core-app-api": "^0.2.0",
"@backstage/test-utils": "^0.1.24",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^11.2.5",
@@ -16,6 +16,7 @@
import { useEffect, useMemo, useRef, useState } from 'react';
import { useAsync } from 'react-use';
import isEqual from 'lodash/isEqual';
import { useApi } from '@backstage/core-plugin-api';
import { catalogApiRef } from '../api';
import { useEntityListProvider } from './useEntityListProvider';
@@ -107,7 +108,9 @@ export function useEntityTypeFilter(): EntityTypeReturn {
const stillValidTypes = selectedTypes.filter(value =>
newTypes.includes(value),
);
setSelectedTypes(stillValidTypes);
if (!isEqual(selectedTypes, stillValidTypes)) {
setSelectedTypes(stillValidTypes);
}
}, [loading, kind, selectedTypes, setSelectedTypes, entities]);
useEffect(() => {
@@ -172,8 +172,9 @@ export const WorkflowRunsTable = ({
});
const githubHost = hostname || 'github.com';
const hasNoRuns = !loading && !tableProps.loading && !runs;
return !runs ? (
return hasNoRuns ? (
<EmptyState
missing="data"
title="No Workflow Data"
+2 -2
View File
@@ -68,7 +68,7 @@ export const createBuiltinActions: (options: {
reader: UrlReader;
integrations: ScmIntegrations;
catalogClient: CatalogApi;
containerRunner: ContainerRunner;
containerRunner?: ContainerRunner;
config: Config;
}) => TemplateAction<any>[];
@@ -289,7 +289,7 @@ export interface RouterOptions {
// (undocumented)
config: Config;
// (undocumented)
containerRunner: ContainerRunner;
containerRunner?: ContainerRunner;
// (undocumented)
database: PluginDatabaseManager;
// (undocumented)
@@ -46,22 +46,17 @@ export const createBuiltinActions = (options: {
reader: UrlReader;
integrations: ScmIntegrations;
catalogClient: CatalogApi;
containerRunner: ContainerRunner;
containerRunner?: ContainerRunner;
config: Config;
}) => {
const { reader, integrations, containerRunner, catalogClient, config } =
options;
return [
const actions = [
createFetchPlainAction({
reader,
integrations,
}),
createFetchCookiecutterAction({
reader,
integrations,
containerRunner,
}),
createFetchTemplateAction({
integrations,
reader,
@@ -97,4 +92,16 @@ export const createBuiltinActions = (options: {
integrations,
}),
];
if (containerRunner) {
actions.push(
createFetchCookiecutterAction({
reader,
integrations,
containerRunner,
}),
);
}
return actions;
};
@@ -55,7 +55,7 @@ export interface RouterOptions {
catalogClient: CatalogApi;
actions?: TemplateAction<any>[];
taskWorkers?: number;
containerRunner: ContainerRunner;
containerRunner?: ContainerRunner;
taskBroker?: TaskBroker;
}
+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();
}
}