From e6e764320b1b01f49fc7e318721f25bd8f381be5 Mon Sep 17 00:00:00 2001 From: Christopher Allford Date: Wed, 1 Jul 2020 11:07:57 -0700 Subject: [PATCH] Added a registry to hold all of the factories and adapters --- tests/e2e/factories/src/framework/adapter.ts | 3 + .../src/framework/factory-registry.spec.ts | 42 +++++++++ .../src/framework/factory-registry.ts | 87 +++++++++++++++++++ 3 files changed, 132 insertions(+) create mode 100644 tests/e2e/factories/src/framework/factory-registry.spec.ts create mode 100644 tests/e2e/factories/src/framework/factory-registry.ts diff --git a/tests/e2e/factories/src/framework/adapter.ts b/tests/e2e/factories/src/framework/adapter.ts index 85e90dc30ee..e04e45ad714 100644 --- a/tests/e2e/factories/src/framework/adapter.ts +++ b/tests/e2e/factories/src/framework/adapter.ts @@ -1,5 +1,8 @@ import { Model } from '../models/model'; +/** + * An interface for implementing adapters to create models. + */ export interface Adapter { /** * Creates a model or array of models using a service.. diff --git a/tests/e2e/factories/src/framework/factory-registry.spec.ts b/tests/e2e/factories/src/framework/factory-registry.spec.ts new file mode 100644 index 00000000000..df727595ac6 --- /dev/null +++ b/tests/e2e/factories/src/framework/factory-registry.spec.ts @@ -0,0 +1,42 @@ +import { AdapterTypes, FactoryRegistry } from './factory-registry'; +import { ModelFactory } from './model-factory'; +import { Product } from '../models/product'; +import { APIAdapter } from './api-adapter'; + +describe( 'FactoryRegistry', () => { + let factoryRegistry: FactoryRegistry; + + beforeEach( () => { + factoryRegistry = new FactoryRegistry(); + } ); + + it( 'should register factories once', () => { + const factory = ModelFactory.define>( ( { params } ) => { + return new Product( params ); + } ); + + expect( factoryRegistry.getFactory( Product ) ).toBeNull(); + + factoryRegistry.registerFactory( Product, factory ); + + expect( () => factoryRegistry.registerFactory( Product, factory ) ).toThrowError( /already been registered/ ); + + const loaded = factoryRegistry.getFactory( Product ); + + expect( loaded ).toBe( factory ); + } ); + + it( 'should register adapters once', () => { + const adapter = new APIAdapter( '', ( model ) => model ); + + expect( factoryRegistry.getAdapter( Product, AdapterTypes.API ) ).toBeNull(); + + factoryRegistry.registerAdapter( Product, AdapterTypes.API, adapter ); + + expect( () => factoryRegistry.registerAdapter( Product, AdapterTypes.API, adapter ) ).toThrowError( /already been registered/ ); + + const loaded = factoryRegistry.getAdapter( Product, AdapterTypes.API ); + + expect( loaded ).toBe( adapter ); + } ); +} ); diff --git a/tests/e2e/factories/src/framework/factory-registry.ts b/tests/e2e/factories/src/framework/factory-registry.ts new file mode 100644 index 00000000000..615e1541213 --- /dev/null +++ b/tests/e2e/factories/src/framework/factory-registry.ts @@ -0,0 +1,87 @@ +import { Adapter } from './adapter'; +import { Model } from '../models/model'; +import { ModelFactory } from './model-factory'; + +type Registry = { [key: string ]: T }; + +/** + * The types of adapters that can be stored in the registry. + * + * @typedef AdapterTypes + * @property {string} API "api" + * @property {string} Database "database" + * @property {string} Custom "custom" + */ +export enum AdapterTypes { + API = 'api', + Database = 'database', + Custom = 'custom' +} + +/** + * A registry that allows for us to easily manage all of our factories and related state. + */ +export class FactoryRegistry { + private readonly registry: Registry> = {}; + private readonly adapters: { [key in AdapterTypes]: Registry> } = { + api: {}, + database: {}, + custom: {}, + }; + + /** + * Registers a factory for the class. + * + * @param {Function} modelClass The class of model we're registering the factory for. + * @param {ModelFactory} factory The factory that we're registering. + */ + public registerFactory( modelClass: new () => T, factory: ModelFactory ): void { + if ( this.registry.hasOwnProperty( modelClass.name ) ) { + throw new Error( 'A factory of this type has already been registered for the model class.' ); + } + + this.registry[ modelClass.name ] = factory; + } + + /** + * Fetches a factory that was registered for the class. + * + * @param {Function} modelClass The class of model for the factory we're fetching. + */ + public getFactory( modelClass: new () => T ): ModelFactory | null { + if ( this.registry.hasOwnProperty( modelClass.name ) ) { + return this.registry[ modelClass.name ]; + } + + return null; + } + + /** + * Registers an adapter for the class. + * + * @param {Function} modelClass The class of model that we're registering the adapter for. + * @param {AdapterTypes} type The type of adapter that we're registering. + * @param {Adapter} adapter The adapter that we're registering. + */ + public registerAdapter( modelClass: new () => T, type: AdapterTypes, adapter: Adapter ): void { + if ( this.adapters[ type ].hasOwnProperty( modelClass.name ) ) { + throw new Error( 'An adapter of this type has already been registered for the model class.' ); + } + + this.adapters[ type ][ modelClass.name ] = adapter; + } + + /** + * Fetches an adapter registered for the class. + * + * @param {Function} modelClass The class of the model for the adapter we're fetching. + * @param {AdapterTypes} type The type of adapter we're fetching. + */ + public getAdapter( modelClass: new () => T, type: AdapterTypes ): Adapter | null { + if ( this.adapters[ type ].hasOwnProperty( modelClass.name ) ) { + return this.adapters[ type ][ modelClass.name ]; + } + + return null; + } +}