Refactor to use context argument

This commit is contained in:
Erik Larsson
2021-02-03 02:00:32 +01:00
parent 987fa8b24f
commit 124d21c20a
15 changed files with 149 additions and 156 deletions
@@ -14,34 +14,38 @@
* limitations under the License.
*/
import { CatalogClient } from '@backstage/catalog-client';
import { CatalogApi } from '@backstage/catalog-client';
import { UserEntity } from '@backstage/catalog-model';
import { CatalogIdentityClient } from './CatalogIdentityClient';
jest.mock('@backstage/catalog-client');
const MockedCatalogClient = CatalogClient as jest.Mock<CatalogClient>;
describe('CatalogIdentityClient', () => {
const token = 'fake-id-token';
const catalogApi: jest.Mocked<CatalogApi> = {
getLocationById: jest.fn(),
getEntityByName: jest.fn(),
getEntities: jest.fn(),
addLocation: jest.fn(),
getLocationByEntity: jest.fn(),
removeEntityByUid: jest.fn(),
};
afterEach(() => jest.resetAllMocks());
it('passes through the correct search params', async () => {
catalogApi.getEntities.mockResolvedValueOnce({ items: [{} as UserEntity] });
const client = new CatalogIdentityClient({
catalogClient: new MockedCatalogClient(),
catalogApi: catalogApi as CatalogApi,
});
client.findUser({ annotations: { key: 'value' } }, { token });
client.findUser({ annotations: { key: 'value' } });
const getEntities = MockedCatalogClient.mock.instances[0].getEntities;
expect(getEntities).toHaveBeenCalledWith(
expect(catalogApi.getEntities).toBeCalledWith(
{
filter: {
kind: 'user',
'metadata.annotations.key': 'value',
},
},
{ token },
undefined,
);
expect(getEntities).toHaveBeenCalledTimes(1);
});
});
@@ -15,9 +15,8 @@
*/
import { ConflictError, NotFoundError } from '@backstage/backend-common';
import { CatalogClient } from '@backstage/catalog-client';
import { ApiContext, CatalogApi } from '@backstage/catalog-client';
import { UserEntity } from '@backstage/catalog-model';
import { CatalogIdentityRequestOptions } from './types';
type UserQuery = {
annotations: Record<string, string>;
@@ -27,10 +26,10 @@ type UserQuery = {
* A catalog client tailored for reading out identity data from the catalog.
*/
export class CatalogIdentityClient {
private readonly catalogClient: CatalogClient;
private readonly catalogApi: CatalogApi;
constructor(options: { catalogClient: CatalogClient }) {
this.catalogClient = options.catalogClient;
constructor(options: { catalogApi: CatalogApi }) {
this.catalogApi = options.catalogApi;
}
/**
@@ -38,11 +37,7 @@ export class CatalogIdentityClient {
*
* Throws a NotFoundError or ConflictError if 0 or multiple users are found.
*/
async findUser(
query: UserQuery,
options?: CatalogIdentityRequestOptions,
): Promise<UserEntity> {
const token = options?.token;
async findUser(query: UserQuery, context?: ApiContext): Promise<UserEntity> {
const filter: Record<string, string> = {
kind: 'user',
};
@@ -50,10 +45,7 @@ export class CatalogIdentityClient {
filter[`metadata.annotations.${key}`] = value;
}
const { items } = await this.catalogClient.getEntities(
{ filter },
{ token },
);
const { items } = await this.catalogApi.getEntities({ filter }, context);
if (items.length !== 1) {
if (items.length > 1) {
@@ -1,19 +0,0 @@
/*
* Copyright 2020 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export type CatalogIdentityRequestOptions = {
token: string | undefined;
};
@@ -14,7 +14,6 @@
* limitations under the License.
*/
import { getVoidLogger } from '@backstage/backend-common';
import { CatalogClient } from '@backstage/catalog-client';
import express from 'express';
import { JWT } from 'jose';
@@ -44,9 +43,6 @@ jest.mock('cross-fetch', () => ({
},
}));
jest.mock('@backstage/catalog-client');
const MockedCatalogClient = CatalogClient as jest.Mock<CatalogClient>;
const identityResolutionCallbackMock = async (): Promise<AuthResponse<any>> => {
return {
backstageIdentity: {
@@ -71,7 +67,15 @@ beforeEach(() => {
});
describe('AwsALBAuthProvider', () => {
const catalogClient = new MockedCatalogClient();
const catalogApi = {
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
addLocation: jest.fn(),
getEntities: jest.fn(),
getLocationByEntity: jest.fn(),
getLocationById: jest.fn(),
removeEntityByUid: jest.fn(),
getEntityByName: jest.fn(),
};
const mockResponseSend = jest.fn();
const mockRequest = ({
@@ -91,7 +95,7 @@ describe('AwsALBAuthProvider', () => {
describe('should transform to type OAuthResponse', () => {
it('when JWT is valid and identity is resolved successfully', async () => {
const provider = new AwsAlbAuthProvider(getVoidLogger(), catalogClient, {
const provider = new AwsAlbAuthProvider(getVoidLogger(), catalogApi, {
region: 'us-west-2',
identityResolutionCallback: identityResolutionCallbackMock,
issuer: 'foo',
@@ -117,7 +121,7 @@ describe('AwsALBAuthProvider', () => {
});
describe('should fail when', () => {
it('JWT is missing', async () => {
const provider = new AwsAlbAuthProvider(getVoidLogger(), catalogClient, {
const provider = new AwsAlbAuthProvider(getVoidLogger(), catalogApi, {
region: 'us-west-2',
identityResolutionCallback: identityResolutionCallbackMock,
issuer: 'foo',
@@ -129,7 +133,7 @@ describe('AwsALBAuthProvider', () => {
});
it('JWT is invalid', async () => {
const provider = new AwsAlbAuthProvider(getVoidLogger(), catalogClient, {
const provider = new AwsAlbAuthProvider(getVoidLogger(), catalogApi, {
region: 'us-west-2',
identityResolutionCallback: identityResolutionCallbackMock,
issuer: 'foo',
@@ -145,7 +149,7 @@ describe('AwsALBAuthProvider', () => {
});
it('issuer is invalid', async () => {
const provider = new AwsAlbAuthProvider(getVoidLogger(), catalogClient, {
const provider = new AwsAlbAuthProvider(getVoidLogger(), catalogApi, {
region: 'us-west-2',
identityResolutionCallback: identityResolutionCallbackMock,
issuer: 'foobar',
@@ -159,7 +163,7 @@ describe('AwsALBAuthProvider', () => {
});
it('identity resolution callback rejects', async () => {
const provider = new AwsAlbAuthProvider(getVoidLogger(), catalogClient, {
const provider = new AwsAlbAuthProvider(getVoidLogger(), catalogApi, {
region: 'us-west-2',
identityResolutionCallback: identityResolutionCallbackRejectedMock,
issuer: 'foo',
@@ -25,7 +25,7 @@ import { KeyObject } from 'crypto';
import { Logger } from 'winston';
import NodeCache from 'node-cache';
import { JWT } from 'jose';
import { CatalogClient } from '@backstage/catalog-client';
import { CatalogApi } from '@backstage/catalog-client';
const ALB_JWT_HEADER = 'x-amzn-oidc-data';
/**
@@ -44,13 +44,13 @@ export const getJWTHeaders = (input: string) => {
export class AwsAlbAuthProvider implements AuthProviderRouteHandlers {
private logger: Logger;
private readonly catalogClient: CatalogClient;
private readonly catalogClient: CatalogApi;
private options: AwsAlbAuthProviderOptions;
private readonly keyCache: NodeCache;
constructor(
logger: Logger,
catalogClient: CatalogClient,
catalogClient: CatalogApi,
options: AwsAlbAuthProviderOptions,
) {
this.logger = logger;
@@ -108,14 +108,14 @@ export class AwsAlbAuthProvider implements AuthProviderRouteHandlers {
export const createAwsAlbProvider = ({
logger,
catalogClient,
catalogApi,
config,
identityResolver,
}: AuthProviderFactoryOptions) => {
const region = config.getString('region');
const issuer = config.getOptionalString('iss');
if (identityResolver !== undefined) {
return new AwsAlbAuthProvider(logger, catalogClient, {
return new AwsAlbAuthProvider(logger, catalogApi, {
region,
issuer,
identityResolutionCallback: identityResolver,
@@ -190,7 +190,7 @@ export const createGoogleProvider: AuthProviderFactory = ({
config,
logger,
tokenIssuer,
catalogClient,
catalogApi,
}) =>
OAuthEnvironmentHandler.mapConfig(config, envConfig => {
const clientId = envConfig.getString('clientId');
@@ -203,7 +203,7 @@ export const createGoogleProvider: AuthProviderFactory = ({
callbackUrl,
logger,
tokenIssuer,
identityClient: new CatalogIdentityClient({ catalogClient }),
identityClient: new CatalogIdentityClient({ catalogApi }),
});
return OAuthAdapter.fromConfig(globalConfig, provider, {
+3 -3
View File
@@ -15,7 +15,7 @@
*/
import { PluginEndpointDiscovery } from '@backstage/backend-common';
import { CatalogClient } from '@backstage/catalog-client';
import { CatalogApi } from '@backstage/catalog-client';
import { Config } from '@backstage/config';
import express from 'express';
import { Logger } from 'winston';
@@ -122,7 +122,7 @@ export type ExperimentalIdentityResolver = (
* An object containing information specific to the auth provider.
*/
payload: object,
catalogClient: CatalogClient,
catalogApi: CatalogApi,
) => Promise<AuthResponse<any>>;
export type AuthProviderFactoryOptions = {
@@ -132,8 +132,8 @@ export type AuthProviderFactoryOptions = {
logger: Logger;
tokenIssuer: TokenIssuer;
discovery: PluginEndpointDiscovery;
catalogApi: CatalogApi;
identityResolver?: ExperimentalIdentityResolver;
catalogClient: CatalogClient;
};
export type AuthProviderFactory = (
+2 -2
View File
@@ -66,7 +66,7 @@ export async function createRouter({
keyDurationSeconds,
logger: logger.child({ component: 'token-factory' }),
});
const catalogClient = new CatalogClient({ discoveryApi: discovery });
const catalogApi = new CatalogClient({ discoveryApi: discovery });
const secret = config.getOptionalString('auth.session.secret');
if (secret) {
@@ -103,7 +103,7 @@ export async function createRouter({
logger,
tokenIssuer,
discovery,
catalogClient,
catalogApi,
});
const r = Router();