Skip to content

Commit

Permalink
feat(NodeManager): Promote get() from PrivateGatewayManager
Browse files Browse the repository at this point in the history
  • Loading branch information
gnarea committed Jun 15, 2022
1 parent fd6934c commit e64fea5
Show file tree
Hide file tree
Showing 9 changed files with 157 additions and 103 deletions.
19 changes: 19 additions & 0 deletions src/lib/nodes/managers/EndpointManager.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { MockKeyStoreSet } from '../../keyStores/testMocks';
import { Endpoint } from '../Endpoint';
import { EndpointManager } from './EndpointManager';

const KEY_STORES = new MockKeyStoreSet();
afterEach(() => {
KEY_STORES.clear();
});

describe('get', () => {
test('Endpoint instances should be returned', async () => {
const { privateAddress } = await KEY_STORES.privateKeyStore.generateIdentityKeyPair();
const manager = new EndpointManager(KEY_STORES);

const endpoint = await manager.get(privateAddress);

expect(endpoint).toBeInstanceOf(Endpoint);
});
});
5 changes: 4 additions & 1 deletion src/lib/nodes/managers/EndpointManager.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import { Endpoint } from '../Endpoint';
import { NodeManager } from './NodeManager';

export class EndpointManager extends NodeManager {}
export class EndpointManager extends NodeManager<Endpoint> {
protected readonly defaultNodeConstructor = Endpoint;
}
3 changes: 2 additions & 1 deletion src/lib/nodes/managers/GatewayManager.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Gateway } from '../Gateway';
import { NodeManager } from './NodeManager';

export abstract class GatewayManager extends NodeManager {}
export abstract class GatewayManager<G extends Gateway> extends NodeManager<G> {}
10 changes: 10 additions & 0 deletions src/lib/nodes/managers/NodeConstructor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { KeyStoreSet } from '../../keyStores/KeyStoreSet';
import { NodeCryptoOptions } from '../NodeCryptoOptions';
import { Node } from '../Node';

export type NodeConstructor<N extends Node<any>> = new (
privateAddress: string,
identityPrivateKey: CryptoKey,
keyStores: KeyStoreSet,
cryptoOptions: Partial<NodeCryptoOptions>,
) => N;
77 changes: 77 additions & 0 deletions src/lib/nodes/managers/NodeManager.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { mockSpy } from '../../_test_utils';
import { MockKeyStoreSet } from '../../keyStores/testMocks';
import * as nodeTestUtils from '../_test_utils';
import { StubNodeManager } from './_test_utils';

const MOCK_NODE_CLASS = mockSpy(jest.spyOn(nodeTestUtils, 'StubNode'));

const KEY_STORES = new MockKeyStoreSet();
afterEach(() => {
KEY_STORES.clear();
});

describe('get', () => {
test('Null should be returned if the private key does not exist', async () => {
const manager = new StubNodeManager(KEY_STORES);

await expect(manager.get('non-existing')).resolves.toBeNull();
});

test('Node should be returned if private key exists', async () => {
const { privateKey, privateAddress } =
await KEY_STORES.privateKeyStore.generateIdentityKeyPair();
const manager = new StubNodeManager(KEY_STORES);

const gateway = await manager.get(privateAddress);

expect(MOCK_NODE_CLASS).toBeCalledWith(privateAddress, privateKey, KEY_STORES, {});
expect(gateway).toEqual(MOCK_NODE_CLASS.mock.instances[0]);
});

test('Key stores should be passed on', async () => {
const { privateAddress } = await KEY_STORES.privateKeyStore.generateIdentityKeyPair();
const manager = new StubNodeManager(KEY_STORES);

await manager.get(privateAddress);

expect(MOCK_NODE_CLASS).toBeCalledWith(
expect.anything(),
expect.anything(),
KEY_STORES,
expect.anything(),
);
});

test('Crypto options should be honoured if passed', async () => {
const { privateAddress } = await KEY_STORES.privateKeyStore.generateIdentityKeyPair();
const cryptoOptions = { encryption: { aesKeySize: 256 } };
const manager = new StubNodeManager(KEY_STORES, cryptoOptions);

await manager.get(privateAddress);

expect(MOCK_NODE_CLASS).toBeCalledWith(
expect.anything(),
expect.anything(),
expect.anything(),
cryptoOptions,
);
});

test('Custom PrivateGateway subclass should be used if applicable', async () => {
const customPrivateGateway = {};
const customPrivateGatewayConstructor = jest.fn().mockReturnValue(customPrivateGateway);
const manager = new StubNodeManager(KEY_STORES);
const { privateKey, privateAddress } =
await KEY_STORES.privateKeyStore.generateIdentityKeyPair();

const gateway = await manager.get(privateAddress, customPrivateGatewayConstructor);

expect(gateway).toBe(customPrivateGateway);
expect(customPrivateGatewayConstructor).toBeCalledWith(
privateAddress,
privateKey,
KEY_STORES,
{},
);
});
});
34 changes: 33 additions & 1 deletion src/lib/nodes/managers/NodeManager.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,41 @@
import { KeyStoreSet } from '../../keyStores/KeyStoreSet';
import { Node } from '../Node';
import { NodeCryptoOptions } from '../NodeCryptoOptions';
import { NodeConstructor } from './NodeConstructor';

export abstract class NodeManager<N extends Node<any>> {
protected abstract readonly defaultNodeConstructor: NodeConstructor<N>;

export abstract class NodeManager {
constructor(
protected keyStores: KeyStoreSet,
protected cryptoOptions: Partial<NodeCryptoOptions> = {},
) {}

/**
* Get node by `privateAddress`.
*
* @param privateAddress
*/
public async get(privateAddress: string): Promise<N | null>;
/**
* Get node by `privateAddress` but return instance of custom `customNodeClass`.
*
* @param privateAddress
* @param customNodeClass
*/
public async get<C extends N>(
privateAddress: string,
customNodeClass: NodeConstructor<C>,
): Promise<C | null>;
public async get(
privateAddress: string,
nodeConstructor?: NodeConstructor<N>,
): Promise<N | null> {
const nodePrivateKey = await this.keyStores.privateKeyStore.retrieveIdentityKey(privateAddress);
if (!nodePrivateKey) {
return null;
}
const constructor = nodeConstructor ?? this.defaultNodeConstructor;
return new constructor(privateAddress, nodePrivateKey, this.keyStores, this.cryptoOptions);
}
}
66 changes: 4 additions & 62 deletions src/lib/nodes/managers/PrivateGatewayManager.spec.ts
Original file line number Diff line number Diff line change
@@ -1,77 +1,19 @@
import { mockSpy } from '../../_test_utils';
import { MockKeyStoreSet } from '../../keyStores/testMocks';
import * as privateGatewayModule from '../PrivateGateway';
import { PrivateGateway } from '../PrivateGateway';
import { PrivateGatewayManager } from './PrivateGatewayManager';

const MOCK_PRIVATE_GATEWAY_CLASS = mockSpy(jest.spyOn(privateGatewayModule, 'PrivateGateway'));

const KEY_STORES = new MockKeyStoreSet();
afterEach(() => {
KEY_STORES.clear();
});

describe('get', () => {
test('Null should be returned if the private key does not exist', async () => {
const manager = new PrivateGatewayManager(KEY_STORES);

await expect(manager.get('non-existing')).resolves.toBeNull();
});

test('Node should be returned if private key exists', async () => {
const { privateKey, privateAddress } =
await KEY_STORES.privateKeyStore.generateIdentityKeyPair();
const manager = new PrivateGatewayManager(KEY_STORES);

const gateway = await manager.get(privateAddress);

expect(MOCK_PRIVATE_GATEWAY_CLASS).toBeCalledWith(privateAddress, privateKey, KEY_STORES, {});
expect(gateway).toEqual(MOCK_PRIVATE_GATEWAY_CLASS.mock.instances[0]);
});

test('Key stores should be passed on', async () => {
test('PrivateGateway instances should be returned', async () => {
const { privateAddress } = await KEY_STORES.privateKeyStore.generateIdentityKeyPair();
const manager = new PrivateGatewayManager(KEY_STORES);

await manager.get(privateAddress);

expect(MOCK_PRIVATE_GATEWAY_CLASS).toBeCalledWith(
expect.anything(),
expect.anything(),
KEY_STORES,
expect.anything(),
);
});

test('Crypto options should be honoured if passed', async () => {
const { privateAddress } = await KEY_STORES.privateKeyStore.generateIdentityKeyPair();
const cryptoOptions = { encryption: { aesKeySize: 256 } };
const manager = new PrivateGatewayManager(KEY_STORES, cryptoOptions);

await manager.get(privateAddress);

expect(MOCK_PRIVATE_GATEWAY_CLASS).toBeCalledWith(
expect.anything(),
expect.anything(),
expect.anything(),
cryptoOptions,
);
});

test('Custom PrivateGateway subclass should be used if applicable', async () => {
const customPrivateGateway = {};
const customPrivateGatewayConstructor = jest.fn().mockReturnValue(customPrivateGateway);
const manager = new PrivateGatewayManager(KEY_STORES);
const { privateKey, privateAddress } =
await KEY_STORES.privateKeyStore.generateIdentityKeyPair();

const gateway = await manager.get(privateAddress, customPrivateGatewayConstructor);
const gateway = await manager.get(privateAddress);

expect(gateway).toBe(customPrivateGateway);
expect(customPrivateGatewayConstructor).toBeCalledWith(
privateAddress,
privateKey,
KEY_STORES,
{},
);
expect(gateway).toBeInstanceOf(PrivateGateway);
});
});
40 changes: 2 additions & 38 deletions src/lib/nodes/managers/PrivateGatewayManager.ts
Original file line number Diff line number Diff line change
@@ -1,42 +1,6 @@
import { KeyStoreSet } from '../../keyStores/KeyStoreSet';
import { NodeCryptoOptions } from '../NodeCryptoOptions';
import { PrivateGateway } from '../PrivateGateway';
import { GatewayManager } from './GatewayManager';

type PrivateGatewayConstructor<G extends PrivateGateway> = new (
privateAddress: string,
identityPrivateKey: CryptoKey,
keyStores: KeyStoreSet,
cryptoOptions: Partial<NodeCryptoOptions>,
) => G;

export class PrivateGatewayManager extends GatewayManager {
/**
* Get private gateway by `privateAddress`.
*
* @param privateAddress
*/
public async get(privateAddress: string): Promise<PrivateGateway | null>;
/**
* Get private gateway by `privateAddress` but return instance of custom
* `customPrivateGatewayClass`.
*
* @param privateAddress
* @param customPrivateGatewayClass
*/
public async get<G extends PrivateGateway>(
privateAddress: string,
customPrivateGatewayClass: PrivateGatewayConstructor<G>,
): Promise<G | null>;
public async get<G extends PrivateGateway>(
privateAddress: string,
privateGatewayConstructor?: PrivateGatewayConstructor<G>,
): Promise<G | PrivateGateway | null> {
const nodePrivateKey = await this.keyStores.privateKeyStore.retrieveIdentityKey(privateAddress);
if (!nodePrivateKey) {
return null;
}
const constructor = privateGatewayConstructor ?? PrivateGateway;
return new constructor(privateAddress, nodePrivateKey, this.keyStores, this.cryptoOptions);
}
export class PrivateGatewayManager extends GatewayManager<PrivateGateway> {
protected readonly defaultNodeConstructor = PrivateGateway;
}
6 changes: 6 additions & 0 deletions src/lib/nodes/managers/_test_utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { StubNode } from '../_test_utils';
import { NodeManager } from './NodeManager';

export class StubNodeManager extends NodeManager<StubNode> {
protected readonly defaultNodeConstructor = StubNode;
}

0 comments on commit e64fea5

Please sign in to comment.