db structure abstraction; async collection; update/insert queries; model saving

This commit is contained in:
garrettmills
2020-07-06 09:53:03 -05:00
parent eddb4f1fbe
commit e4f5da7ac6
73 changed files with 3301 additions and 57 deletions

35
lib/src/unit/Canon.ts Normal file
View File

@@ -0,0 +1,35 @@
import {Service} from '../../../di/src/decorator/Service.ts'
export type CanonicalResolver = (key: string) => any
export class DuplicateResolverKeyError extends Error {
constructor(key: string) {
super(`There is already a canonical resource with the scope ${key} registered.`)
}
}
@Service()
export class Canon {
protected resources: { [key: string]: any } = {}
get(key: string): any {
const key_parts = key.split('::')
let desc_value = this.resources
key_parts.forEach(part => {
if ( typeof desc_value === 'function' ) {
desc_value = desc_value(part)
} else {
desc_value = desc_value[part]
}
})
return desc_value
}
register_resource(scope: string, resolver: CanonicalResolver) {
if ( this.resources[scope] ) {
throw new DuplicateResolverKeyError(scope)
}
this.resources[scope] = resolver
}
}

58
lib/src/unit/Canonical.ts Normal file
View File

@@ -0,0 +1,58 @@
import LifecycleUnit from '../lifecycle/Unit.ts'
import {fs, path} from '../external/std.ts'
import {Canon} from './Canon.ts'
export interface CanonicalDefinition {
canonical_name: string,
original_name: string,
imported: any,
}
export class Canonical extends LifecycleUnit {
protected base_path: string = '.'
protected suffix: string = '.ts'
protected canonical_item: string = ''
protected _items: { [key: string]: any } = {}
public get path(): string {
return path.resolve(this.base_path)
}
public get canonical_items() {
return `${this.canonical_item}s`
}
public async up() {
for await ( const entry of fs.walk(this.path) ) {
if ( !entry.isFile || !entry.path.endsWith(this.suffix) ) continue
const def = await this._get_canonical_definition(entry.path)
this._items[def.canonical_name] = await this.init_canonical_item(def)
}
this.make(Canon).register_resource(this.canonical_items, (key: string) => this.get(key))
}
public async init_canonical_item(definition: CanonicalDefinition): Promise<any> {
return definition.imported.default
}
private async _get_canonical_definition(file_path: string): Promise<CanonicalDefinition> {
const original_name = file_path.replace(this.path, '').substr(1)
const path_regex = new RegExp(path.SEP, 'g')
const canonical_name = original_name.replace(path_regex, ':')
.split('').reverse().join('')
.substr(this.suffix.length)
.split('').reverse().join('')
const imported = await import(file_path)
return { canonical_name, original_name, imported }
}
public get(key: string): any {
const key_parts = key.split('.')
let desc_value = this._items
key_parts.forEach(part => {
desc_value = desc_value[part]
})
return desc_value
}
}

9
lib/src/unit/Config.ts Normal file
View File

@@ -0,0 +1,9 @@
import {Canonical} from './Canonical.ts'
import { Unit } from '../lifecycle/decorators.ts'
@Unit()
export default class Config extends Canonical {
protected base_path = './app/configs'
protected suffix = '.config.ts'
protected canonical_item = 'config'
}

View File

@@ -0,0 +1,20 @@
import { InstantiableCanonical } from './InstantiableCanonical.ts'
import { CanonicalDefinition } from './Canonical.ts'
import Controller from '../http/Controller.ts'
import { Unit } from '../lifecycle/decorators.ts'
@Unit()
export default class Controllers extends InstantiableCanonical {
protected base_path = './app/http/controllers'
protected canonical_item = 'controller'
protected suffix = '.controller.ts'
public async init_canonical_item(def: CanonicalDefinition) {
const item = await super.init_canonical_item(def)
if ( !(item instanceof Controller) ) {
throw new TypeError(`Invalid controller definition: ${def.original_name}. Controllers must extend from Daton's base Controller class.`)
}
return item
}
}

View File

@@ -0,0 +1,18 @@
import {Canonical, CanonicalDefinition} from './Canonical.ts'
import {isInstantiable} from '../../../di/src/type/Instantiable.ts'
export class InvalidCanonicalExportError extends Error {
constructor(name: string) {
super(`Unable to import canonical item from "${name}". The default export of this file is invalid.`)
}
}
export class InstantiableCanonical extends Canonical {
public async init_canonical_item(def: CanonicalDefinition) {
if ( isInstantiable(def.imported.default) ) {
return this.make(def.imported.default)
}
throw new InvalidCanonicalExportError(def.original_name)
}
}

View File

@@ -0,0 +1,20 @@
import { InstantiableCanonical } from './InstantiableCanonical.ts'
import { CanonicalDefinition } from './Canonical.ts'
import { Middleware } from '../http/Middleware.ts'
import { Unit } from '../lifecycle/decorators.ts'
@Unit()
export default class Middlewares extends InstantiableCanonical {
protected base_path = './app/http/middleware'
protected canonical_item = 'middleware'
protected suffix = '.middleware.ts'
public async init_canonical_item(def: CanonicalDefinition) {
const item = await super.init_canonical_item(def)
if ( !(item instanceof Middleware) ) {
throw new TypeError(`Invalid middleware definition: ${def.original_name}. Middleware must extend from Daton's base Middleware class.`)
}
return item
}
}

View File

@@ -1,30 +1,55 @@
import LifecycleUnit from '../lifecycle/Unit.ts'
import { Unit } from '../lifecycle/decorators.ts'
import { dotenv } from '../external/std.ts'
import { Logging } from '../service/logging/Logging.ts'
import StandardLogger from '../service/logging/StandardLogger.ts'
import { isLoggingLevel } from '../service/logging/types.ts'
import Utility from '../service/utility/Utility.ts'
import { make } from '../../../di/src/global.ts'
import 'https://deno.land/x/dotenv/load.ts'
import { Container } from '../../../di/src/Container.ts'
import { Inject } from '../../../di/src/decorator/Injection.ts'
import CacheFactory from "../support/CacheFactory.ts";
const env = (name: string, fallback: any) => {
const scaffolding = make(Scaffolding)
return scaffolding.env(name) ?? fallback
}
export { env }
@Unit()
export default class Scaffolding extends LifecycleUnit {
private config = {}
constructor(
protected logger: Logging
) {
super()
}
protected logger: Logging,
protected utility: Utility,
@Inject('injector') protected injector: Container,
) { super() }
public refresh_env() {
this.config = dotenv()
}
public setup_logging() {
StandardLogger.register()
this.logger.info('Logging initialized.', true)
public env(name: string) {
return this.utility.infer(Deno.env.get(name) ?? '')
}
public async up() {
this.setup_logging()
this.refresh_env()
}
public setup_logging() {
StandardLogger.register()
try {
this.logger.verbose('Attempting to load logging level from the environment')
const env_level = this.env('DATON_LOGGING_LEVEL')
this.logger.verbose(`Read logging level: ${env_level}`)
if ( isLoggingLevel(env_level) ) {
this.logger.verbose('Logging level is valid.')
this.logger.level = env_level
this.logger.debug(`Set logging level from environment: ${env_level}`)
}
} catch (e) {}
this.logger.info('Logging initialized.', true)
this.logger.verbose('Adding the cache production factory to the container...')
this.injector.register_factory(new CacheFactory())
}
}