src/classes/elder.ts
import Model, { serializers } from './model'
import Base from './base'
import Type from './type'
import { PostgresAdapter } from '../adapters'
import Adapter from './adapter'
import Serializer from './serializer'
import { StringType, NumberType, DateType, BooleanType } from '../types'
export type databaseConfig = {
database: string
host?: string
user?: string
password?: string
port?: number
}
export type config = {
adapters: {
default: databaseConfig
[name: string]: databaseConfig
}
}
export type modelClasses = {
[name: string]: typeof Model
}
export type typeClasses = {
[name: string]: typeof Type
}
export type serializerClasses = {
default: typeof Serializer
[name: string]: typeof Serializer
}
export type adapterClasses = {
default: typeof Adapter
[name: string]: typeof Adapter
}
export type options = {
config: config
types?: typeClasses
models: modelClasses
adapters?: adapterClasses
serializers?: serializerClasses
}
export type adapters = {
default: Adapter
[name: string]: Adapter
}
export type serializers = {
default: Serializer
[name: string]: Serializer
}
export type types = {
[name: string]: Type
}
export type models = {
[name: string]: typeof Model
}
/**
* Merges a default adapter class with additional adapter classes
*/
function mergeAdapters(
defaultAdapter: typeof Adapter,
adapters?: {
[name: string]: typeof Adapter
}
) {
return Object.assign({}, { default: defaultAdapter }, adapters)
}
/**
* Merges a default serializer class with additional serializer classes
*/
function mergeSerializers(
defaultSerializer: typeof Serializer,
serializers?: {
[name: string]: typeof Serializer
}
) {
return Object.assign({}, { default: defaultSerializer }, serializers)
}
/**
* Merges default type classes with additional type classes
*/
function mergeTypes(types?: { [name: string]: typeof Type }) {
return Object.assign(
{},
{
string: StringType,
number: NumberType,
date: DateType,
boolean: BooleanType
},
types
)
}
/**
* Iterates over given models calling each models setup method
*/
function setupModels(
models: modelClasses,
types: types,
adapters: adapters,
serializers: serializers
) {
Object.keys(models).forEach(name => {
const Model = models[name]
Model.setup(types, adapters, serializers)
})
}
/**
* Creates and returns adapter class instances for given adapter classes
*/
function setupAdapters(
adapters: adapterClasses,
config: {
default: databaseConfig
[name: string]: databaseConfig
}
): adapters {
const instances: adapters = {
default: adapters.default.create(config.default)
}
for (let [key, value] of Object.entries(adapters)) {
if (key === 'default') continue
instances[key] = value.create(config[key])
}
return instances
}
/**
* Creates and returns serializer class instances for given serializer classes
*/
function setupSerializers(serializers: serializerClasses): serializers {
const instances: serializers = {
default: serializers.default.create()
}
for (let [key, value] of Object.entries(serializers)) {
if (key === 'default') continue
instances[key] = value.create()
}
return instances
}
/**
* Creates and returns type class instances for given type classes
*/
function setupTypes(types: typeClasses): types {
const instances: types = {}
for (let [key, value] of Object.entries(types)) {
instances[key] = value.create()
}
return instances
}
/**
* The Elder base class. Does the work of setting up and tying together
* models, adapters, serializers and types
*/
export default class Elder extends Base {
/**
* Initialized serializer classes
*/
serializers: serializers
/**
* Initialized adapter classes
*/
adapters: adapters
/**
* Initialized and setup model classes
*/
models: models
/**
* Initialized type classes
*/
types: types
/**
* Configuration reference
*/
config: config
constructor(options: options) {
super()
this.config = options.config
// setup serializers by merging default classes with those optionally given in
// options before creating instances from classes and holding onto a reference
const serializers = mergeSerializers(Serializer, options.serializers)
this.serializers = setupSerializers(serializers)
// setup adapters by merging default classes with those optionally given in
// options before creating instances from classes and holding onto a reference
const adapters = mergeAdapters(PostgresAdapter, options.adapters)
this.adapters = setupAdapters(adapters, options.config.adapters)
// setup types by merging default classes with those optionally given in
// options before creating instances from classes and holding onto a reference
const types = mergeTypes(options.types)
this.types = setupTypes(types)
// setup models by calling each models setup method passing initialized
// serializers, adapters and types
this.models = options.models || {}
setupModels(this.models, this.types, this.adapters, this.serializers)
}
/**
* Alternative to using `new`
*/
static create(options: options) {
return new this(options)
}
/**
* Tears down connection pools allowing scripts to exit.
*/
destroy(): any {
return Promise.all(
Object.entries(this.adapters).map(([key, value]) => {
return this.adapters[key].destroy()
})
)
}
}