db structure abstraction; async collection; update/insert queries; model saving
This commit is contained in:
35
lib/src/unit/Canon.ts
Normal file
35
lib/src/unit/Canon.ts
Normal 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
58
lib/src/unit/Canonical.ts
Normal 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
9
lib/src/unit/Config.ts
Normal 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'
|
||||
}
|
||||
20
lib/src/unit/Controllers.ts
Normal file
20
lib/src/unit/Controllers.ts
Normal 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
|
||||
}
|
||||
}
|
||||
18
lib/src/unit/InstantiableCanonical.ts
Normal file
18
lib/src/unit/InstantiableCanonical.ts
Normal 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)
|
||||
}
|
||||
}
|
||||
20
lib/src/unit/Middlewares.ts
Normal file
20
lib/src/unit/Middlewares.ts
Normal 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
|
||||
}
|
||||
}
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user