Centralize configure-able factory classes
This commit is contained in:
parent
5557aae543
commit
c0595f3ef9
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@extollo/lib",
|
||||
"version": "0.14.4",
|
||||
"version": "0.14.5",
|
||||
"description": "The framework library that lifts up your code.",
|
||||
"main": "lib/index.js",
|
||||
"types": "lib/index.d.ts",
|
||||
|
@ -1,73 +1,23 @@
|
||||
import {
|
||||
AbstractFactory,
|
||||
Container,
|
||||
DependencyRequirement,
|
||||
PropertyDependency,
|
||||
isInstantiable,
|
||||
DEPENDENCY_KEYS_METADATA_KEY,
|
||||
Instantiable, FactoryProducer, getPropertyInjectionMetadata,
|
||||
} from '../../di'
|
||||
import {Collection, ErrorWithContext} from '../../util'
|
||||
import {Config} from '../../service/Config'
|
||||
import {Instantiable, FactoryProducer} from '../../di'
|
||||
import {AuthenticatableRepository} from '../types'
|
||||
import {ORMUserRepository} from './orm/ORMUserRepository'
|
||||
import {ConfiguredSingletonFactory} from '../../di/factory/ConfiguredSingletonFactory'
|
||||
|
||||
/**
|
||||
* A dependency injection factory that matches the abstract ClientRepository class
|
||||
* and produces an instance of the configured repository driver implementation.
|
||||
*/
|
||||
@FactoryProducer()
|
||||
export class AuthenticatableRepositoryFactory extends AbstractFactory<AuthenticatableRepository> {
|
||||
protected get config(): Config {
|
||||
return Container.getContainer().make<Config>(Config)
|
||||
export class AuthenticatableRepositoryFactory extends ConfiguredSingletonFactory<AuthenticatableRepository> {
|
||||
protected getConfigKey(): string {
|
||||
return 'auth.storage'
|
||||
}
|
||||
|
||||
produce(): AuthenticatableRepository {
|
||||
return new (this.getAuthenticatableRepositoryClass())()
|
||||
protected getDefaultImplementation(): Instantiable<AuthenticatableRepository> {
|
||||
return ORMUserRepository
|
||||
}
|
||||
|
||||
match(something: unknown): boolean {
|
||||
return something === AuthenticatableRepository
|
||||
}
|
||||
|
||||
getDependencyKeys(): Collection<DependencyRequirement> {
|
||||
const meta = Reflect.getMetadata(DEPENDENCY_KEYS_METADATA_KEY, this.getAuthenticatableRepositoryClass())
|
||||
if ( meta ) {
|
||||
return meta
|
||||
}
|
||||
return new Collection<DependencyRequirement>()
|
||||
}
|
||||
|
||||
getInjectedProperties(): Collection<PropertyDependency> {
|
||||
const meta = new Collection<PropertyDependency>()
|
||||
let currentToken = this.getAuthenticatableRepositoryClass()
|
||||
|
||||
do {
|
||||
const loadedMeta = getPropertyInjectionMetadata(currentToken)
|
||||
if ( loadedMeta ) {
|
||||
meta.concat(loadedMeta)
|
||||
}
|
||||
currentToken = Object.getPrototypeOf(currentToken)
|
||||
} while (Object.getPrototypeOf(currentToken) !== Function.prototype && Object.getPrototypeOf(currentToken) !== Object.prototype)
|
||||
|
||||
return meta
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the instantiable class of the configured user repository backend.
|
||||
* @protected
|
||||
*/
|
||||
protected getAuthenticatableRepositoryClass(): Instantiable<AuthenticatableRepository> {
|
||||
const AuthenticatableRepositoryClass = this.config.get('auth.storage', ORMUserRepository)
|
||||
|
||||
if ( !isInstantiable(AuthenticatableRepositoryClass) || !(AuthenticatableRepositoryClass.prototype instanceof AuthenticatableRepository) ) {
|
||||
const e = new ErrorWithContext('Provided client repository class does not extend from @extollo/lib.AuthenticatableRepository')
|
||||
e.context = {
|
||||
configKey: 'auth.storage',
|
||||
class: AuthenticatableRepositoryClass.toString(),
|
||||
}
|
||||
}
|
||||
|
||||
return AuthenticatableRepositoryClass
|
||||
protected getAbstractImplementation(): any {
|
||||
return AuthenticatableRepository
|
||||
}
|
||||
}
|
||||
|
@ -1,74 +1,23 @@
|
||||
import {
|
||||
AbstractFactory,
|
||||
Container,
|
||||
DependencyRequirement,
|
||||
PropertyDependency,
|
||||
isInstantiable,
|
||||
DEPENDENCY_KEYS_METADATA_KEY,
|
||||
Instantiable, FactoryProducer, getPropertyInjectionMetadata,
|
||||
} from '../../../di'
|
||||
import {Collection, ErrorWithContext} from '../../../util'
|
||||
import {Config} from '../../../service/Config'
|
||||
import {Instantiable, FactoryProducer} from '../../../di'
|
||||
import {ClientRepository} from '../types'
|
||||
import {ConfigClientRepository} from './ConfigClientRepository'
|
||||
import {ConfiguredSingletonFactory} from '../../../di/factory/ConfiguredSingletonFactory'
|
||||
|
||||
/**
|
||||
* A dependency injection factory that matches the abstract ClientRepository class
|
||||
* and produces an instance of the configured repository driver implementation.
|
||||
*/
|
||||
@FactoryProducer()
|
||||
export class ClientRepositoryFactory extends AbstractFactory<ClientRepository> {
|
||||
protected get config(): Config {
|
||||
return Container.getContainer().make<Config>(Config)
|
||||
export class ClientRepositoryFactory extends ConfiguredSingletonFactory<ClientRepository> {
|
||||
protected getConfigKey(): string {
|
||||
return 'oauth2.repository.client'
|
||||
}
|
||||
|
||||
produce(): ClientRepository {
|
||||
return new (this.getClientRepositoryClass())()
|
||||
protected getDefaultImplementation(): Instantiable<ClientRepository> {
|
||||
return ConfigClientRepository
|
||||
}
|
||||
|
||||
match(something: unknown): boolean {
|
||||
return something === ClientRepository
|
||||
}
|
||||
|
||||
getDependencyKeys(): Collection<DependencyRequirement> {
|
||||
const meta = Reflect.getMetadata(DEPENDENCY_KEYS_METADATA_KEY, this.getClientRepositoryClass())
|
||||
if ( meta ) {
|
||||
return meta
|
||||
}
|
||||
return new Collection<DependencyRequirement>()
|
||||
}
|
||||
|
||||
getInjectedProperties(): Collection<PropertyDependency> {
|
||||
const meta = new Collection<PropertyDependency>()
|
||||
let currentToken = this.getClientRepositoryClass()
|
||||
|
||||
do {
|
||||
const loadedMeta = getPropertyInjectionMetadata(currentToken)
|
||||
if ( loadedMeta ) {
|
||||
meta.concat(loadedMeta)
|
||||
}
|
||||
currentToken = Object.getPrototypeOf(currentToken)
|
||||
} while (Object.getPrototypeOf(currentToken) !== Function.prototype && Object.getPrototypeOf(currentToken) !== Object.prototype)
|
||||
|
||||
return meta
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the instantiable class of the configured client repository backend.
|
||||
* @protected
|
||||
* @return Instantiable<ClientRepository>
|
||||
*/
|
||||
protected getClientRepositoryClass(): Instantiable<ClientRepository> {
|
||||
const ClientRepositoryClass = this.config.get('oauth2.repository.client', ConfigClientRepository)
|
||||
|
||||
if ( !isInstantiable(ClientRepositoryClass) || !(ClientRepositoryClass.prototype instanceof ClientRepository) ) {
|
||||
const e = new ErrorWithContext('Provided client repository class does not extend from @extollo/lib.ClientRepository')
|
||||
e.context = {
|
||||
configKey: 'oauth2.repository.client',
|
||||
class: ClientRepositoryClass.toString(),
|
||||
}
|
||||
}
|
||||
|
||||
return ClientRepositoryClass
|
||||
protected getAbstractImplementation(): any {
|
||||
return ClientRepository
|
||||
}
|
||||
}
|
||||
|
@ -1,74 +1,23 @@
|
||||
import {
|
||||
AbstractFactory,
|
||||
Container,
|
||||
DependencyRequirement,
|
||||
PropertyDependency,
|
||||
isInstantiable,
|
||||
DEPENDENCY_KEYS_METADATA_KEY,
|
||||
Instantiable, FactoryProducer, getPropertyInjectionMetadata,
|
||||
} from '../../../di'
|
||||
import {Collection, ErrorWithContext} from '../../../util'
|
||||
import {Config} from '../../../service/Config'
|
||||
import {Instantiable, FactoryProducer} from '../../../di'
|
||||
import {RedemptionCodeRepository} from '../types'
|
||||
import {CacheRedemptionCodeRepository} from './CacheRedemptionCodeRepository'
|
||||
import {ConfiguredSingletonFactory} from '../../../di/factory/ConfiguredSingletonFactory'
|
||||
|
||||
/**
|
||||
* A dependency injection factory that matches the abstract RedemptionCodeRepository class
|
||||
* and produces an instance of the configured repository driver implementation.
|
||||
*/
|
||||
@FactoryProducer()
|
||||
export class RedemptionCodeRepositoryFactory extends AbstractFactory<RedemptionCodeRepository> {
|
||||
protected get config(): Config {
|
||||
return Container.getContainer().make<Config>(Config)
|
||||
export class RedemptionCodeRepositoryFactory extends ConfiguredSingletonFactory<RedemptionCodeRepository> {
|
||||
protected getConfigKey(): string {
|
||||
return 'oauth2.repository.client'
|
||||
}
|
||||
|
||||
produce(): RedemptionCodeRepository {
|
||||
return new (this.getRedemptionCodeRepositoryClass())()
|
||||
protected getDefaultImplementation(): Instantiable<RedemptionCodeRepository> {
|
||||
return CacheRedemptionCodeRepository
|
||||
}
|
||||
|
||||
match(something: unknown): boolean {
|
||||
return something === RedemptionCodeRepository
|
||||
}
|
||||
|
||||
getDependencyKeys(): Collection<DependencyRequirement> {
|
||||
const meta = Reflect.getMetadata(DEPENDENCY_KEYS_METADATA_KEY, this.getRedemptionCodeRepositoryClass())
|
||||
if ( meta ) {
|
||||
return meta
|
||||
}
|
||||
return new Collection<DependencyRequirement>()
|
||||
}
|
||||
|
||||
getInjectedProperties(): Collection<PropertyDependency> {
|
||||
const meta = new Collection<PropertyDependency>()
|
||||
let currentToken = this.getRedemptionCodeRepositoryClass()
|
||||
|
||||
do {
|
||||
const loadedMeta = getPropertyInjectionMetadata(currentToken)
|
||||
if ( loadedMeta ) {
|
||||
meta.concat(loadedMeta)
|
||||
}
|
||||
currentToken = Object.getPrototypeOf(currentToken)
|
||||
} while (Object.getPrototypeOf(currentToken) !== Function.prototype && Object.getPrototypeOf(currentToken) !== Object.prototype)
|
||||
|
||||
return meta
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the instantiable class of the configured client repository backend.
|
||||
* @protected
|
||||
* @return Instantiable<RedemptionCodeRepository>
|
||||
*/
|
||||
protected getRedemptionCodeRepositoryClass(): Instantiable<RedemptionCodeRepository> {
|
||||
const RedemptionCodeRepositoryClass = this.config.get('oauth2.repository.client', CacheRedemptionCodeRepository)
|
||||
|
||||
if ( !isInstantiable(RedemptionCodeRepositoryClass) || !(RedemptionCodeRepositoryClass.prototype instanceof RedemptionCodeRepository) ) {
|
||||
const e = new ErrorWithContext('Provided client repository class does not extend from @extollo/lib.RedemptionCodeRepository')
|
||||
e.context = {
|
||||
configKey: 'oauth2.repository.client',
|
||||
class: RedemptionCodeRepositoryClass.toString(),
|
||||
}
|
||||
}
|
||||
|
||||
return RedemptionCodeRepositoryClass
|
||||
protected getAbstractImplementation(): any {
|
||||
return RedemptionCodeRepository
|
||||
}
|
||||
}
|
||||
|
@ -1,74 +1,23 @@
|
||||
import {
|
||||
AbstractFactory,
|
||||
Container,
|
||||
DependencyRequirement,
|
||||
PropertyDependency,
|
||||
isInstantiable,
|
||||
DEPENDENCY_KEYS_METADATA_KEY,
|
||||
Instantiable, FactoryProducer, getPropertyInjectionMetadata,
|
||||
} from '../../../di'
|
||||
import {Collection, ErrorWithContext} from '../../../util'
|
||||
import {Config} from '../../../service/Config'
|
||||
import {Instantiable, FactoryProducer} from '../../../di'
|
||||
import {ScopeRepository} from '../types'
|
||||
import {ConfigScopeRepository} from './ConfigScopeRepository'
|
||||
import {ConfiguredSingletonFactory} from '../../../di/factory/ConfiguredSingletonFactory'
|
||||
|
||||
/**
|
||||
* A dependency injection factory that matches the abstract ScopeRepository class
|
||||
* and produces an instance of the configured repository driver implementation.
|
||||
*/
|
||||
@FactoryProducer()
|
||||
export class ScopeRepositoryFactory extends AbstractFactory<ScopeRepository> {
|
||||
protected get config(): Config {
|
||||
return Container.getContainer().make<Config>(Config)
|
||||
export class ScopeRepositoryFactory extends ConfiguredSingletonFactory<ScopeRepository> {
|
||||
protected getConfigKey(): string {
|
||||
return 'oauth2.repository.scope'
|
||||
}
|
||||
|
||||
produce(): ScopeRepository {
|
||||
return new (this.getScopeRepositoryClass())()
|
||||
protected getDefaultImplementation(): Instantiable<ScopeRepository> {
|
||||
return ConfigScopeRepository
|
||||
}
|
||||
|
||||
match(something: unknown): boolean {
|
||||
return something === ScopeRepository
|
||||
}
|
||||
|
||||
getDependencyKeys(): Collection<DependencyRequirement> {
|
||||
const meta = Reflect.getMetadata(DEPENDENCY_KEYS_METADATA_KEY, this.getScopeRepositoryClass())
|
||||
if ( meta ) {
|
||||
return meta
|
||||
}
|
||||
return new Collection<DependencyRequirement>()
|
||||
}
|
||||
|
||||
getInjectedProperties(): Collection<PropertyDependency> {
|
||||
const meta = new Collection<PropertyDependency>()
|
||||
let currentToken = this.getScopeRepositoryClass()
|
||||
|
||||
do {
|
||||
const loadedMeta = getPropertyInjectionMetadata(currentToken)
|
||||
if ( loadedMeta ) {
|
||||
meta.concat(loadedMeta)
|
||||
}
|
||||
currentToken = Object.getPrototypeOf(currentToken)
|
||||
} while (Object.getPrototypeOf(currentToken) !== Function.prototype && Object.getPrototypeOf(currentToken) !== Object.prototype)
|
||||
|
||||
return meta
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the instantiable class of the configured scope repository backend.
|
||||
* @protected
|
||||
* @return Instantiable<ScopeRepository>
|
||||
*/
|
||||
protected getScopeRepositoryClass(): Instantiable<ScopeRepository> {
|
||||
const ScopeRepositoryClass = this.config.get('oauth2.repository.scope', ConfigScopeRepository)
|
||||
|
||||
if ( !isInstantiable(ScopeRepositoryClass) || !(ScopeRepositoryClass.prototype instanceof ScopeRepository) ) {
|
||||
const e = new ErrorWithContext('Provided client repository class does not extend from @extollo/lib.ScopeRepository')
|
||||
e.context = {
|
||||
configKey: 'oauth2.repository.client',
|
||||
class: ScopeRepositoryClass.toString(),
|
||||
}
|
||||
}
|
||||
|
||||
return ScopeRepositoryClass
|
||||
protected getAbstractImplementation(): any {
|
||||
return ScopeRepository
|
||||
}
|
||||
}
|
||||
|
@ -1,74 +1,23 @@
|
||||
import {
|
||||
AbstractFactory,
|
||||
Container,
|
||||
DependencyRequirement,
|
||||
PropertyDependency,
|
||||
isInstantiable,
|
||||
DEPENDENCY_KEYS_METADATA_KEY,
|
||||
Instantiable, FactoryProducer, getPropertyInjectionMetadata,
|
||||
} from '../../../di'
|
||||
import {Collection, ErrorWithContext} from '../../../util'
|
||||
import {Config} from '../../../service/Config'
|
||||
import {Instantiable, FactoryProducer} from '../../../di'
|
||||
import {TokenRepository} from '../types'
|
||||
import {ORMTokenRepository} from './ORMTokenRepository'
|
||||
import {ConfiguredSingletonFactory} from '../../../di/factory/ConfiguredSingletonFactory'
|
||||
|
||||
/**
|
||||
* A dependency injection factory that matches the abstract TokenRepository class
|
||||
* and produces an instance of the configured repository driver implementation.
|
||||
*/
|
||||
@FactoryProducer()
|
||||
export class TokenRepositoryFactory extends AbstractFactory<TokenRepository> {
|
||||
protected get config(): Config {
|
||||
return Container.getContainer().make<Config>(Config)
|
||||
export class TokenRepositoryFactory extends ConfiguredSingletonFactory<TokenRepository> {
|
||||
protected getConfigKey(): string {
|
||||
return 'oauth2.repository.token'
|
||||
}
|
||||
|
||||
produce(): TokenRepository {
|
||||
return new (this.getTokenRepositoryClass())()
|
||||
protected getDefaultImplementation(): Instantiable<TokenRepository> {
|
||||
return ORMTokenRepository
|
||||
}
|
||||
|
||||
match(something: unknown): boolean {
|
||||
return something === TokenRepository
|
||||
}
|
||||
|
||||
getDependencyKeys(): Collection<DependencyRequirement> {
|
||||
const meta = Reflect.getMetadata(DEPENDENCY_KEYS_METADATA_KEY, this.getTokenRepositoryClass())
|
||||
if ( meta ) {
|
||||
return meta
|
||||
}
|
||||
return new Collection<DependencyRequirement>()
|
||||
}
|
||||
|
||||
getInjectedProperties(): Collection<PropertyDependency> {
|
||||
const meta = new Collection<PropertyDependency>()
|
||||
let currentToken = this.getTokenRepositoryClass()
|
||||
|
||||
do {
|
||||
const loadedMeta = getPropertyInjectionMetadata(currentToken)
|
||||
if ( loadedMeta ) {
|
||||
meta.concat(loadedMeta)
|
||||
}
|
||||
currentToken = Object.getPrototypeOf(currentToken)
|
||||
} while (Object.getPrototypeOf(currentToken) !== Function.prototype && Object.getPrototypeOf(currentToken) !== Object.prototype)
|
||||
|
||||
return meta
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the instantiable class of the configured token repository backend.
|
||||
* @protected
|
||||
* @return Instantiable<TokenRepository>
|
||||
*/
|
||||
protected getTokenRepositoryClass(): Instantiable<TokenRepository> {
|
||||
const TokenRepositoryClass = this.config.get('oauth2.repository.token', ORMTokenRepository)
|
||||
|
||||
if ( !isInstantiable(TokenRepositoryClass) || !(TokenRepositoryClass.prototype instanceof TokenRepository) ) {
|
||||
const e = new ErrorWithContext('Provided token repository class does not extend from @extollo/lib.TokenRepository')
|
||||
e.context = {
|
||||
configKey: 'oauth2.repository.client',
|
||||
class: TokenRepositoryClass.toString(),
|
||||
}
|
||||
}
|
||||
|
||||
return TokenRepositoryClass
|
||||
protected getAbstractImplementation(): any {
|
||||
return TokenRepository
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import 'reflect-metadata'
|
||||
import {collect, Collection} from '../../util'
|
||||
import {collect, Collection} from '../../util/collection/Collection'
|
||||
import {logIfDebugging} from '../../util/support/debug'
|
||||
import {
|
||||
DEPENDENCY_KEYS_METADATA_KEY,
|
||||
|
@ -1,5 +1,6 @@
|
||||
import {DependencyKey, DependencyRequirement, PropertyDependency} from '../types'
|
||||
import { Collection } from '../../util'
|
||||
import {DependencyKey, DependencyRequirement, Instantiable, PropertyDependency} from '../types'
|
||||
import {Collection, logIfDebugging} from '../../util'
|
||||
import {getPropertyInjectionMetadata} from '../decorator/getPropertyInjectionMetadata'
|
||||
|
||||
/**
|
||||
* Abstract base class for dependency container factories.
|
||||
@ -42,6 +43,22 @@ export abstract class AbstractFactory<T> {
|
||||
*/
|
||||
abstract getInjectedProperties(): Collection<PropertyDependency>
|
||||
|
||||
/** Helper method that returns all `@Inject()`'ed properties for a token and its prototypical ancestors. */
|
||||
protected getInjectedPropertiesForPrototypeChain(token: Instantiable<any>): Collection<PropertyDependency> {
|
||||
const meta = new Collection<PropertyDependency>()
|
||||
|
||||
do {
|
||||
const loadedMeta = getPropertyInjectionMetadata(token)
|
||||
if ( loadedMeta ) {
|
||||
meta.concat(loadedMeta)
|
||||
}
|
||||
token = Object.getPrototypeOf(token)
|
||||
logIfDebugging('extollo.di.injection', 'next currentToken:', token)
|
||||
} while (token !== Function.prototype && token !== Object.prototype)
|
||||
|
||||
return meta
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a human-readable name of the token this factory produces.
|
||||
* This is meant for debugging output only.
|
||||
|
84
src/di/factory/ConfiguredSingletonFactory.ts
Normal file
84
src/di/factory/ConfiguredSingletonFactory.ts
Normal file
@ -0,0 +1,84 @@
|
||||
import {AbstractFactory} from './AbstractFactory'
|
||||
import {Inject, Injectable} from '../decorator/injection'
|
||||
import {Logging} from '../../service/Logging'
|
||||
import {Config} from '../../service/Config'
|
||||
import {
|
||||
DEPENDENCY_KEYS_METADATA_KEY,
|
||||
DependencyRequirement,
|
||||
Instantiable,
|
||||
isInstantiable,
|
||||
PropertyDependency,
|
||||
} from '../types'
|
||||
import {Collection, ErrorWithContext, Maybe} from '../../util'
|
||||
import 'reflect-metadata'
|
||||
|
||||
@Injectable()
|
||||
export abstract class ConfiguredSingletonFactory<T> extends AbstractFactory<T> {
|
||||
protected static loggedDefaultImplementationWarningOnce = false
|
||||
|
||||
@Inject()
|
||||
protected readonly logging!: Logging
|
||||
|
||||
@Inject()
|
||||
protected readonly config!: Config
|
||||
|
||||
constructor() {
|
||||
super({})
|
||||
}
|
||||
|
||||
protected abstract getConfigKey(): string
|
||||
|
||||
protected abstract getDefaultImplementation(): Instantiable<T>
|
||||
|
||||
protected abstract getAbstractImplementation(): any
|
||||
|
||||
protected getDefaultImplementationWarning(): Maybe<string> {
|
||||
return undefined
|
||||
}
|
||||
|
||||
produce(dependencies: any[], parameters: any[]): T {
|
||||
return new (this.getImplementation())(...dependencies, ...parameters)
|
||||
}
|
||||
|
||||
match(something: unknown): boolean {
|
||||
return something === this.getAbstractImplementation()
|
||||
}
|
||||
|
||||
getDependencyKeys(): Collection<DependencyRequirement> {
|
||||
const meta = Reflect.getMetadata(DEPENDENCY_KEYS_METADATA_KEY, this.getImplementation())
|
||||
if ( meta ) {
|
||||
return meta
|
||||
}
|
||||
|
||||
return new Collection<DependencyRequirement>()
|
||||
}
|
||||
|
||||
getInjectedProperties(): Collection<PropertyDependency> {
|
||||
return this.getInjectedPropertiesForPrototypeChain(this.getImplementation())
|
||||
}
|
||||
|
||||
protected getImplementation(): Instantiable<T> {
|
||||
const ctor = this.constructor as typeof ConfiguredSingletonFactory
|
||||
const ImplementationClass = this.config.get(this.getConfigKey(), this.getDefaultImplementation())
|
||||
if ( ImplementationClass === this.getDefaultImplementation() ) {
|
||||
const warning = this.getDefaultImplementationWarning()
|
||||
if ( warning && !ctor.loggedDefaultImplementationWarningOnce ) {
|
||||
this.logging.warn(warning)
|
||||
ctor.loggedDefaultImplementationWarningOnce = true
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
!isInstantiable(ImplementationClass)
|
||||
|| !(ImplementationClass.prototype instanceof this.getAbstractImplementation())
|
||||
) {
|
||||
throw new ErrorWithContext('Configured service clas does not properly extend from implementation base class.', {
|
||||
configKey: this.getConfigKey(),
|
||||
class: `${ImplementationClass}`,
|
||||
mustExtendBase: `${this.getAbstractImplementation()}`,
|
||||
})
|
||||
}
|
||||
|
||||
return ImplementationClass as Instantiable<T>
|
||||
}
|
||||
}
|
@ -5,9 +5,8 @@ import {
|
||||
Instantiable,
|
||||
PropertyDependency,
|
||||
} from '../types'
|
||||
import {Collection, logIfDebugging} from '../../util'
|
||||
import {Collection} from '../../util'
|
||||
import 'reflect-metadata'
|
||||
import {getPropertyInjectionMetadata} from '../decorator/getPropertyInjectionMetadata'
|
||||
|
||||
/**
|
||||
* Standard static-class factory. The token of this factory is a reference to a
|
||||
@ -53,18 +52,6 @@ export class Factory<T> extends AbstractFactory<T> {
|
||||
}
|
||||
|
||||
getInjectedProperties(): Collection<PropertyDependency> {
|
||||
const meta = new Collection<PropertyDependency>()
|
||||
let currentToken = this.token
|
||||
|
||||
do {
|
||||
const loadedMeta = getPropertyInjectionMetadata(currentToken)
|
||||
logIfDebugging('extollo.di.injection', 'Factory.getInjectedProperties() target:', currentToken, 'loaded:', loadedMeta)
|
||||
if ( loadedMeta ) {
|
||||
meta.concat(loadedMeta)
|
||||
}
|
||||
currentToken = Object.getPrototypeOf(currentToken)
|
||||
} while (Object.getPrototypeOf(currentToken) !== Function.prototype && Object.getPrototypeOf(currentToken) !== Object.prototype)
|
||||
|
||||
return meta
|
||||
return this.getInjectedPropertiesForPrototypeChain(this.token)
|
||||
}
|
||||
}
|
||||
|
@ -7,12 +7,13 @@ export * from './factory/Factory'
|
||||
export * from './factory/NamedFactory'
|
||||
export * from './factory/SingletonFactory'
|
||||
|
||||
export * from './types'
|
||||
export * from './ContainerBlueprint'
|
||||
export * from './decorator/getPropertyInjectionMetadata'
|
||||
export * from './decorator/injection'
|
||||
|
||||
export * from './Container'
|
||||
export * from './ScopedContainer'
|
||||
export * from './types'
|
||||
|
||||
export * from './decorator/injection'
|
||||
export * from './decorator/getPropertyInjectionMetadata'
|
||||
export * from './InjectionAware'
|
||||
export * from './constructable'
|
||||
|
@ -20,7 +20,7 @@ export class InjectSessionHTTPModule extends HTTPKernelModule {
|
||||
}
|
||||
|
||||
public async apply(request: Request): Promise<Request> {
|
||||
request.registerFactory(new SessionFactory())
|
||||
request.registerFactory(request.make(SessionFactory))
|
||||
|
||||
const session = <Session> request.make(Session)
|
||||
const id = request.cookies.get('extollo.session')
|
||||
|
@ -1,87 +1,23 @@
|
||||
import {
|
||||
AbstractFactory,
|
||||
Container,
|
||||
DependencyRequirement,
|
||||
PropertyDependency,
|
||||
isInstantiable,
|
||||
DEPENDENCY_KEYS_METADATA_KEY,
|
||||
Instantiable, getPropertyInjectionMetadata,
|
||||
} from '../../di'
|
||||
import {Collection, ErrorWithContext} from '../../util'
|
||||
import {Instantiable} from '../../di'
|
||||
import {Maybe} from '../../util'
|
||||
import {MemorySession} from './MemorySession'
|
||||
import {Session} from './Session'
|
||||
import {Logging} from '../../service/Logging'
|
||||
import {Config} from '../../service/Config'
|
||||
import {ConfiguredSingletonFactory} from '../../di/factory/ConfiguredSingletonFactory'
|
||||
|
||||
/**
|
||||
* A dependency injection factory that matches the abstract Session class
|
||||
* and produces an instance of the configured session driver implementation.
|
||||
*/
|
||||
export class SessionFactory extends AbstractFactory<Session> {
|
||||
protected readonly logging: Logging
|
||||
|
||||
protected readonly config: Config
|
||||
|
||||
/** True if we have printed the memory session warning at least once. */
|
||||
private static loggedMemorySessionWarningOnce = false
|
||||
|
||||
constructor() {
|
||||
super({})
|
||||
this.logging = Container.getContainer().make<Logging>(Logging)
|
||||
this.config = Container.getContainer().make<Config>(Config)
|
||||
export class SessionFactory extends ConfiguredSingletonFactory<Session> {
|
||||
protected getConfigKey(): string {
|
||||
return 'server.session.driver'
|
||||
}
|
||||
|
||||
produce(): Session {
|
||||
return new (this.getSessionClass())()
|
||||
protected getDefaultImplementation(): Instantiable<Session> {
|
||||
return MemorySession
|
||||
}
|
||||
|
||||
match(something: unknown): boolean {
|
||||
return something === Session
|
||||
protected getAbstractImplementation(): any {
|
||||
return Session
|
||||
}
|
||||
|
||||
getDependencyKeys(): Collection<DependencyRequirement> {
|
||||
const meta = Reflect.getMetadata(DEPENDENCY_KEYS_METADATA_KEY, this.getSessionClass())
|
||||
if ( meta ) {
|
||||
return meta
|
||||
}
|
||||
return new Collection<DependencyRequirement>()
|
||||
}
|
||||
|
||||
getInjectedProperties(): Collection<PropertyDependency> {
|
||||
const meta = new Collection<PropertyDependency>()
|
||||
let currentToken = this.getSessionClass()
|
||||
|
||||
do {
|
||||
const loadedMeta = getPropertyInjectionMetadata(currentToken)
|
||||
if ( loadedMeta ) {
|
||||
meta.concat(loadedMeta)
|
||||
}
|
||||
currentToken = Object.getPrototypeOf(currentToken)
|
||||
} while (Object.getPrototypeOf(currentToken) !== Function.prototype && Object.getPrototypeOf(currentToken) !== Object.prototype)
|
||||
|
||||
return meta
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the instantiable class of the configured session backend.
|
||||
* @protected
|
||||
* @return Instantiable<Session>
|
||||
*/
|
||||
protected getSessionClass(): Instantiable<Session> {
|
||||
const SessionClass = this.config.get('server.session.driver', MemorySession)
|
||||
if ( SessionClass === MemorySession && !SessionFactory.loggedMemorySessionWarningOnce ) {
|
||||
this.logging.warn(`You are using the default memory-based session driver. It is recommended you configure a persistent session driver instead.`)
|
||||
SessionFactory.loggedMemorySessionWarningOnce = true
|
||||
}
|
||||
|
||||
if ( !isInstantiable(SessionClass) || !(SessionClass.prototype instanceof Session) ) {
|
||||
const e = new ErrorWithContext('Provided session class does not extend from @extollo/lib.Session')
|
||||
e.context = {
|
||||
configKey: 'server.session.driver',
|
||||
class: SessionClass.toString(),
|
||||
}
|
||||
}
|
||||
|
||||
return SessionClass
|
||||
protected getDefaultImplementationWarning(): Maybe<string> {
|
||||
return 'You are using the default memory-based session driver. It is recommended you configure a persistent session driver instead.'
|
||||
}
|
||||
}
|
||||
|
@ -1,86 +1,23 @@
|
||||
import {
|
||||
AbstractFactory,
|
||||
DependencyRequirement,
|
||||
PropertyDependency,
|
||||
isInstantiable,
|
||||
DEPENDENCY_KEYS_METADATA_KEY,
|
||||
Instantiable,
|
||||
Injectable,
|
||||
Inject,
|
||||
FactoryProducer,
|
||||
getPropertyInjectionMetadata,
|
||||
} from '../../di'
|
||||
import {Collection, ErrorWithContext} from '../../util'
|
||||
import {Logging} from '../../service/Logging'
|
||||
import {Config} from '../../service/Config'
|
||||
import {Instantiable, FactoryProducer} from '../../di'
|
||||
import {Migrator} from './Migrator'
|
||||
import {DatabaseMigrator} from './DatabaseMigrator'
|
||||
import {ConfiguredSingletonFactory} from '../../di/factory/ConfiguredSingletonFactory'
|
||||
|
||||
/**
|
||||
* A dependency injection factory that matches the abstract Migrator class
|
||||
* and produces an instance of the configured session driver implementation.
|
||||
*/
|
||||
@Injectable()
|
||||
@FactoryProducer()
|
||||
export class MigratorFactory extends AbstractFactory<Migrator> {
|
||||
@Inject()
|
||||
protected readonly logging!: Logging
|
||||
|
||||
@Inject()
|
||||
protected readonly config!: Config
|
||||
|
||||
constructor() {
|
||||
super({})
|
||||
export class MigratorFactory extends ConfiguredSingletonFactory<Migrator> {
|
||||
protected getConfigKey(): string {
|
||||
return 'database.migrations.driver'
|
||||
}
|
||||
|
||||
produce(): Migrator {
|
||||
return new (this.getMigratorClass())()
|
||||
protected getDefaultImplementation(): Instantiable<Migrator> {
|
||||
return DatabaseMigrator
|
||||
}
|
||||
|
||||
match(something: unknown): boolean {
|
||||
return something === Migrator
|
||||
}
|
||||
|
||||
getDependencyKeys(): Collection<DependencyRequirement> {
|
||||
const meta = Reflect.getMetadata(DEPENDENCY_KEYS_METADATA_KEY, this.getMigratorClass())
|
||||
if ( meta ) {
|
||||
return meta
|
||||
}
|
||||
|
||||
return new Collection<DependencyRequirement>()
|
||||
}
|
||||
|
||||
getInjectedProperties(): Collection<PropertyDependency> {
|
||||
const meta = new Collection<PropertyDependency>()
|
||||
let currentToken = this.getMigratorClass()
|
||||
|
||||
do {
|
||||
const loadedMeta = getPropertyInjectionMetadata(currentToken)
|
||||
if ( loadedMeta ) {
|
||||
meta.concat(loadedMeta)
|
||||
}
|
||||
currentToken = Object.getPrototypeOf(currentToken)
|
||||
} while (Object.getPrototypeOf(currentToken) !== Function.prototype && Object.getPrototypeOf(currentToken) !== Object.prototype)
|
||||
|
||||
return meta
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the instantiable class of the configured migrator backend.
|
||||
* @protected
|
||||
* @return Instantiable<Migrator>
|
||||
*/
|
||||
protected getMigratorClass(): Instantiable<Migrator> {
|
||||
const MigratorClass = this.config.get('database.migrations.driver', DatabaseMigrator)
|
||||
|
||||
if ( !isInstantiable(MigratorClass) || !(MigratorClass.prototype instanceof Migrator) ) {
|
||||
const e = new ErrorWithContext('Provided migration driver class does not extend from @extollo/lib.Migrator')
|
||||
e.context = {
|
||||
configKey: 'database.migrations.driver',
|
||||
class: MigratorClass.toString(),
|
||||
}
|
||||
}
|
||||
|
||||
return MigratorClass
|
||||
protected getAbstractImplementation(): any {
|
||||
return Migrator
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ import {collect, Collection, ErrorWithContext, Maybe} from '../../util'
|
||||
import {HasSubtree} from './relation/HasSubtree'
|
||||
import {Related} from './relation/decorators'
|
||||
import {HasTreeParent} from './relation/HasTreeParent'
|
||||
import {ModelBuilder} from './ModelBuilder'
|
||||
|
||||
/**
|
||||
* Model implementation with helpers for querying tree-structured data.
|
||||
@ -44,6 +45,12 @@ export abstract class TreeModel<T extends TreeModel<T>> extends Model<T> {
|
||||
|
||||
public static readonly parentIdField = 'parent_id'
|
||||
|
||||
/** @override to include the tree fields */
|
||||
public static query<T2 extends Model<T2>>(): ModelBuilder<T2> {
|
||||
return super.query<T2>()
|
||||
.fields(this.rightTreeField, this.leftTreeField, this.parentIdField)
|
||||
}
|
||||
|
||||
/**
|
||||
* @override to eager-load the subtree by default
|
||||
* @protected
|
||||
@ -52,6 +59,13 @@ export abstract class TreeModel<T extends TreeModel<T>> extends Model<T> {
|
||||
|
||||
protected removedChildren: Collection<TreeModel<T>> = collect()
|
||||
|
||||
/** @override to include the tree fields */
|
||||
public query(): ModelBuilder<T> {
|
||||
const ctor = this.constructor as typeof TreeModel
|
||||
return super.query()
|
||||
.fields(ctor.leftTreeField, ctor.rightTreeField, ctor.parentIdField)
|
||||
}
|
||||
|
||||
/** Get the left tree number for this model. */
|
||||
public leftTreeNum(): Maybe<number> {
|
||||
const ctor = this.constructor as typeof TreeModel
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {Canonical} from './Canonical'
|
||||
import {Singleton} from '../di'
|
||||
import {Singleton} from '../di/decorator/injection'
|
||||
import {Maybe} from '../util'
|
||||
|
||||
/**
|
||||
|
@ -1,5 +1,7 @@
|
||||
import {DebuggingTraceIsNotAnError, Logger, LoggingLevel, LogMessage} from '../util'
|
||||
import {Singleton} from '../di'
|
||||
import {Singleton} from '../di/decorator/injection'
|
||||
import {DebuggingTraceIsNotAnError} from '../util/error/DebuggingTraceIsNotAnError'
|
||||
import {LoggingLevel, LogMessage} from '../util/logging/types'
|
||||
import {Logger} from '../util/logging/Logger'
|
||||
|
||||
/**
|
||||
* A singleton service that manages loggers registered in the application, and
|
||||
|
@ -1,86 +1,28 @@
|
||||
import {
|
||||
AbstractFactory,
|
||||
Container,
|
||||
DependencyRequirement,
|
||||
PropertyDependency,
|
||||
isInstantiable,
|
||||
DEPENDENCY_KEYS_METADATA_KEY,
|
||||
StaticInstantiable,
|
||||
FactoryProducer, getPropertyInjectionMetadata,
|
||||
} from '../../../di'
|
||||
import {Collection, ErrorWithContext} from '../../../util'
|
||||
import {Logging} from '../../../service/Logging'
|
||||
import {Config} from '../../../service/Config'
|
||||
import {FactoryProducer, Instantiable} from '../../../di'
|
||||
import {Maybe} from '../../../util'
|
||||
import {Queue} from './Queue'
|
||||
import {SyncQueue} from './SyncQueue'
|
||||
import {ConfiguredSingletonFactory} from '../../../di/factory/ConfiguredSingletonFactory'
|
||||
|
||||
/**
|
||||
* Dependency container factory that matches the abstract Queue token, but
|
||||
* produces an instance of whatever Queue driver is configured in the `server.queue.driver` config.
|
||||
*/
|
||||
@FactoryProducer()
|
||||
export class QueueFactory extends AbstractFactory<Queue> {
|
||||
/** true if we have printed the synchronous queue driver warning once. */
|
||||
private static loggedSyncQueueWarningOnce = false
|
||||
|
||||
private di(): [Logging, Config] {
|
||||
return [
|
||||
Container.getContainer().make(Logging),
|
||||
Container.getContainer().make(Config),
|
||||
]
|
||||
export class QueueFactory extends ConfiguredSingletonFactory<Queue> {
|
||||
protected getConfigKey(): string {
|
||||
return 'server.queue.driver'
|
||||
}
|
||||
|
||||
produce(): Queue {
|
||||
return new (this.getQueueClass())()
|
||||
protected getDefaultImplementation(): Instantiable<Queue> {
|
||||
return SyncQueue
|
||||
}
|
||||
|
||||
match(something: unknown): boolean {
|
||||
return something === Queue
|
||||
protected getAbstractImplementation(): any {
|
||||
return Queue
|
||||
}
|
||||
|
||||
getDependencyKeys(): Collection<DependencyRequirement> {
|
||||
const meta = Reflect.getMetadata(DEPENDENCY_KEYS_METADATA_KEY, this.getQueueClass())
|
||||
if ( meta ) {
|
||||
return meta
|
||||
}
|
||||
return new Collection<DependencyRequirement>()
|
||||
}
|
||||
|
||||
getInjectedProperties(): Collection<PropertyDependency> {
|
||||
const meta = new Collection<PropertyDependency>()
|
||||
let currentToken = this.getQueueClass()
|
||||
|
||||
do {
|
||||
const loadedMeta = getPropertyInjectionMetadata(currentToken)
|
||||
if ( loadedMeta ) {
|
||||
meta.concat(loadedMeta)
|
||||
}
|
||||
currentToken = Object.getPrototypeOf(currentToken)
|
||||
} while (Object.getPrototypeOf(currentToken) !== Function.prototype && Object.getPrototypeOf(currentToken) !== Object.prototype)
|
||||
|
||||
return meta
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the configured queue driver and return some Instantiable<Queue>.
|
||||
* @protected
|
||||
*/
|
||||
protected getQueueClass(): StaticInstantiable<Queue> {
|
||||
const [logging, config] = this.di()
|
||||
const QueueClass = config.get('server.queue.driver', SyncQueue)
|
||||
if ( QueueClass === SyncQueue && !QueueFactory.loggedSyncQueueWarningOnce ) {
|
||||
logging.warn(`You are using the default synchronous queue driver. It is recommended you configure a background queue driver instead.`)
|
||||
QueueFactory.loggedSyncQueueWarningOnce = true
|
||||
}
|
||||
|
||||
if ( !isInstantiable(QueueClass) || !(QueueClass.prototype instanceof Queue) ) {
|
||||
const e = new ErrorWithContext('Provided queue class does not extend from @extollo/lib.Queue')
|
||||
e.context = {
|
||||
configKey: 'server.queue.driver',
|
||||
class: QueueClass.toString(),
|
||||
}
|
||||
}
|
||||
|
||||
return QueueClass
|
||||
protected getDefaultImplementationWarning(): Maybe<string> {
|
||||
return 'You are using the default synchronous queue driver. It is recommended you configure a background queue driver instead.'
|
||||
}
|
||||
}
|
||||
|
85
src/support/cache/CacheFactory.ts
vendored
85
src/support/cache/CacheFactory.ts
vendored
@ -1,86 +1,27 @@
|
||||
import {
|
||||
AbstractFactory,
|
||||
Container,
|
||||
DependencyRequirement,
|
||||
PropertyDependency,
|
||||
isInstantiable,
|
||||
DEPENDENCY_KEYS_METADATA_KEY,
|
||||
StaticClass, Instantiable, getPropertyInjectionMetadata,
|
||||
} from '../../di'
|
||||
import {Collection, ErrorWithContext} from '../../util'
|
||||
import {Logging} from '../../service/Logging'
|
||||
import {Config} from '../../service/Config'
|
||||
import {Instantiable, FactoryProducer} from '../../di'
|
||||
import {InMemCache, Maybe} from '../../util'
|
||||
import {Cache} from '../../util'
|
||||
import {MemoryCache} from './MemoryCache'
|
||||
import {ConfiguredSingletonFactory} from '../../di/factory/ConfiguredSingletonFactory'
|
||||
|
||||
/**
|
||||
* Dependency container factory that matches the abstract Cache token, but
|
||||
* produces an instance of whatever Cache driver is configured in the `server.cache.driver` config.
|
||||
*/
|
||||
export class CacheFactory extends AbstractFactory<Cache> {
|
||||
protected readonly logging: Logging
|
||||
|
||||
protected readonly config: Config
|
||||
|
||||
/** true if we have printed the memory-based cache driver warning once. */
|
||||
private static loggedMemoryCacheWarningOnce = false
|
||||
|
||||
constructor() {
|
||||
super({})
|
||||
this.logging = Container.getContainer().make<Logging>(Logging)
|
||||
this.config = Container.getContainer().make<Config>(Config)
|
||||
@FactoryProducer()
|
||||
export class CacheFactory extends ConfiguredSingletonFactory<Cache> {
|
||||
protected getConfigKey(): string {
|
||||
return 'server.cache.driver'
|
||||
}
|
||||
|
||||
produce(): Cache {
|
||||
return new (this.getCacheClass())()
|
||||
protected getDefaultImplementation(): Instantiable<Cache> {
|
||||
return InMemCache
|
||||
}
|
||||
|
||||
match(something: unknown): boolean {
|
||||
return something === Cache
|
||||
protected getAbstractImplementation(): any {
|
||||
return Cache
|
||||
}
|
||||
|
||||
getDependencyKeys(): Collection<DependencyRequirement> {
|
||||
const meta = Reflect.getMetadata(DEPENDENCY_KEYS_METADATA_KEY, this.getCacheClass())
|
||||
if ( meta ) {
|
||||
return meta
|
||||
}
|
||||
return new Collection<DependencyRequirement>()
|
||||
}
|
||||
|
||||
getInjectedProperties(): Collection<PropertyDependency> {
|
||||
const meta = new Collection<PropertyDependency>()
|
||||
let currentToken = this.getCacheClass()
|
||||
|
||||
do {
|
||||
const loadedMeta = getPropertyInjectionMetadata(currentToken)
|
||||
if ( loadedMeta ) {
|
||||
meta.concat(loadedMeta)
|
||||
}
|
||||
currentToken = Object.getPrototypeOf(currentToken)
|
||||
} while (Object.getPrototypeOf(currentToken) !== Function.prototype && Object.getPrototypeOf(currentToken) !== Object.prototype)
|
||||
|
||||
return meta
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the configured cache driver and return some Instantiable<Cache>.
|
||||
* @protected
|
||||
*/
|
||||
protected getCacheClass(): StaticClass<Cache, Instantiable<Cache>> {
|
||||
const CacheClass = this.config.get('server.cache.driver', MemoryCache)
|
||||
if ( CacheClass === MemoryCache && !CacheFactory.loggedMemoryCacheWarningOnce ) {
|
||||
this.logging.warn(`You are using the default memory-based cache driver. It is recommended you configure a persistent cache driver instead.`)
|
||||
CacheFactory.loggedMemoryCacheWarningOnce = true
|
||||
}
|
||||
|
||||
if ( !isInstantiable(CacheClass) || !(CacheClass.prototype instanceof Cache) ) {
|
||||
const e = new ErrorWithContext('Provided session class does not extend from @extollo/lib.Cache')
|
||||
e.context = {
|
||||
configKey: 'server.cache.driver',
|
||||
class: CacheClass.toString(),
|
||||
}
|
||||
}
|
||||
|
||||
return CacheClass
|
||||
protected getDefaultImplementationWarning(): Maybe<string> {
|
||||
return 'You are using the default memory-based cache driver. It is recommended you configure a persistent cache driver instead.'
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {Collection} from './Collection'
|
||||
import {InjectionAware} from '../../di'
|
||||
import {InjectionAware} from '../../di/InjectionAware'
|
||||
|
||||
export type MaybeIterationItem<T> = { done: boolean, value?: T }
|
||||
export type ChunkCallback<T> = (items: Collection<T>) => any
|
||||
|
@ -2,6 +2,8 @@ import {RequestInfo, RequestInit, Response} from 'node-fetch'
|
||||
import {unsafeESMImport} from './unsafe'
|
||||
export const fetch = (url: RequestInfo, init?: RequestInit): Promise<Response> => unsafeESMImport('node-fetch').then(({default: nodeFetch}) => nodeFetch(url, init))
|
||||
|
||||
export * from './support/operator'
|
||||
|
||||
export * from './cache/Cache'
|
||||
export * from './cache/InMemCache'
|
||||
|
||||
|
27
src/util/support/operator.ts
Normal file
27
src/util/support/operator.ts
Normal file
@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Apply a series of operators to a value, returning the original value.
|
||||
*
|
||||
* Helpful for values/methods that don't support chaining.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const inOneHour = () => tap(new Date, d => d.setMinutes(d.getMinutes() + 60))
|
||||
* ```
|
||||
*
|
||||
* This is equivalent to:
|
||||
*
|
||||
* ```ts
|
||||
* const inOneHour = () => {
|
||||
* const d = new Date
|
||||
* d.setMinutes(d.getMinutes() + 60)
|
||||
* return d
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param value
|
||||
* @param ops
|
||||
*/
|
||||
export function tap<T>(value: T, ...ops: ((t: T) => unknown)[]): T {
|
||||
ops.forEach(op => op(value))
|
||||
return value
|
||||
}
|
@ -1,79 +1,23 @@
|
||||
import {
|
||||
AbstractFactory,
|
||||
Container,
|
||||
DependencyRequirement,
|
||||
PropertyDependency,
|
||||
isInstantiable,
|
||||
DEPENDENCY_KEYS_METADATA_KEY,
|
||||
StaticClass, Instantiable, getPropertyInjectionMetadata,
|
||||
} from '../di'
|
||||
import {Collection, ErrorWithContext} from '../util'
|
||||
import {Logging} from '../service/Logging'
|
||||
import {Config} from '../service/Config'
|
||||
import {Instantiable, FactoryProducer} from '../di'
|
||||
import {ViewEngine} from './ViewEngine'
|
||||
import {PugViewEngine} from './PugViewEngine'
|
||||
import {ConfiguredSingletonFactory} from '../di/factory/ConfiguredSingletonFactory'
|
||||
|
||||
/**
|
||||
* Dependency factory whose token matches the abstract ViewEngine class, but produces
|
||||
* a particular ViewEngine implementation based on the configuration.
|
||||
*/
|
||||
export class ViewEngineFactory extends AbstractFactory<ViewEngine> {
|
||||
protected readonly logging: Logging
|
||||
|
||||
protected readonly config: Config
|
||||
|
||||
constructor() {
|
||||
super({})
|
||||
this.logging = Container.getContainer().make<Logging>(Logging)
|
||||
this.config = Container.getContainer().make<Config>(Config)
|
||||
@FactoryProducer()
|
||||
export class ViewEngineFactory extends ConfiguredSingletonFactory<ViewEngine> {
|
||||
protected getConfigKey(): string {
|
||||
return 'server.view_engine.driver'
|
||||
}
|
||||
|
||||
produce(): ViewEngine {
|
||||
return new (this.getViewEngineClass())()
|
||||
protected getDefaultImplementation(): Instantiable<ViewEngine> {
|
||||
return PugViewEngine
|
||||
}
|
||||
|
||||
match(something: unknown): boolean {
|
||||
return something === ViewEngine
|
||||
}
|
||||
|
||||
getDependencyKeys(): Collection<DependencyRequirement> {
|
||||
const meta = Reflect.getMetadata(DEPENDENCY_KEYS_METADATA_KEY, this.getViewEngineClass())
|
||||
if ( meta ) {
|
||||
return meta
|
||||
}
|
||||
return new Collection<DependencyRequirement>()
|
||||
}
|
||||
|
||||
getInjectedProperties(): Collection<PropertyDependency> {
|
||||
const meta = new Collection<PropertyDependency>()
|
||||
let currentToken = this.getViewEngineClass()
|
||||
|
||||
do {
|
||||
const loadedMeta = getPropertyInjectionMetadata(currentToken)
|
||||
if ( loadedMeta ) {
|
||||
meta.concat(loadedMeta)
|
||||
}
|
||||
currentToken = Object.getPrototypeOf(currentToken)
|
||||
} while (Object.getPrototypeOf(currentToken) !== Function.prototype && Object.getPrototypeOf(currentToken) !== Object.prototype)
|
||||
|
||||
return meta
|
||||
}
|
||||
|
||||
/**
|
||||
* Using the config, get the implementation of the ViewEngine that should be used in the application.
|
||||
* @protected
|
||||
*/
|
||||
protected getViewEngineClass(): StaticClass<ViewEngine, Instantiable<ViewEngine>> {
|
||||
const ViewEngineClass = this.config.get('server.view_engine.driver', PugViewEngine)
|
||||
|
||||
if ( !isInstantiable(ViewEngineClass) || !(ViewEngineClass.prototype instanceof ViewEngine) ) {
|
||||
const e = new ErrorWithContext('Provided session class does not extend from @extollo/lib.ViewEngine')
|
||||
e.context = {
|
||||
configKey: 'server.view_engine.driver',
|
||||
class: ViewEngineClass.toString(),
|
||||
}
|
||||
}
|
||||
|
||||
return ViewEngineClass
|
||||
protected getAbstractImplementation(): any {
|
||||
return ViewEngine
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user