From 878de025d8e39816359f0d874d2314979af17085 Mon Sep 17 00:00:00 2001 From: garrettmills Date: Fri, 24 Jul 2020 09:53:30 -0500 Subject: [PATCH] Add generic session interfaces; memory session; factories; kernel --- di/src/Container.ts | 2 +- di/src/type/DependencyKey.ts | 3 +- di/src/type/StaticClass.ts | 5 +++ lib/src/http/kernel/Kernel.ts | 10 +++++ lib/src/http/session/MemorySession.ts | 32 ++++++++++++++ lib/src/http/session/MemorySessionManager.ts | 42 +++++++++++++++++++ lib/src/http/session/Session.ts | 13 ++++++ lib/src/http/session/SessionFactory.ts | 25 +++++++++++ lib/src/http/session/SessionManager.ts | 16 +++++++ lib/src/http/session/SessionManagerFactory.ts | 25 +++++++++++ lib/src/unit/Scaffolding.ts | 17 ++++++-- 11 files changed, 184 insertions(+), 6 deletions(-) create mode 100644 di/src/type/StaticClass.ts create mode 100644 lib/src/http/session/MemorySession.ts create mode 100644 lib/src/http/session/MemorySessionManager.ts create mode 100644 lib/src/http/session/Session.ts create mode 100644 lib/src/http/session/SessionFactory.ts create mode 100644 lib/src/http/session/SessionManager.ts create mode 100644 lib/src/http/session/SessionManagerFactory.ts diff --git a/di/src/Container.ts b/di/src/Container.ts index 385af5c..75f6df5 100755 --- a/di/src/Container.ts +++ b/di/src/Container.ts @@ -146,7 +146,7 @@ class Container { make(target: DependencyKey, ...parameters: any[]) { if ( this.has_key(target) ) return this.resolve_and_create(target, ...parameters) - else if ( typeof target !== 'string' ) + else if ( typeof target !== 'string' && isInstantiable(target) ) return this.produce_factory(new Factory(target), parameters) else throw new TypeError(`Invalid or unknown make target: ${target}`) diff --git a/di/src/type/DependencyKey.ts b/di/src/type/DependencyKey.ts index ccb46fc..06a26ae 100644 --- a/di/src/type/DependencyKey.ts +++ b/di/src/type/DependencyKey.ts @@ -1,4 +1,5 @@ import Instantiable from './Instantiable.ts' +import {StaticClass} from './StaticClass.ts' const DEPENDENCY_KEYS_METADATA_KEY = 'daton:di:dependencyKeys.ts' -type DependencyKey = Instantiable | string +type DependencyKey = Instantiable | StaticClass | string export { DependencyKey, DEPENDENCY_KEYS_METADATA_KEY } diff --git a/di/src/type/StaticClass.ts b/di/src/type/StaticClass.ts new file mode 100644 index 0000000..c7bb961 --- /dev/null +++ b/di/src/type/StaticClass.ts @@ -0,0 +1,5 @@ +export type StaticClass = Function & {prototype: T} + +export function isStaticClass(something: any): something is StaticClass { + return typeof something === 'function' && typeof something.prototype !== 'undefined' +} diff --git a/lib/src/http/kernel/Kernel.ts b/lib/src/http/kernel/Kernel.ts index 273da7d..352e114 100644 --- a/lib/src/http/kernel/Kernel.ts +++ b/lib/src/http/kernel/Kernel.ts @@ -10,6 +10,7 @@ export interface ModuleRegistrationFluency { after: (other?: Instantiable) => Kernel, first: () => Kernel, last: () => Kernel, + core: () => Kernel, } export class KernelModuleNotFoundError extends Error { @@ -21,6 +22,7 @@ export class KernelModuleNotFoundError extends Error { @Service() export default class Kernel extends AppClass { protected preflight: Collection = new Collection() + protected inflight?: Module protected postflight: Collection = new Collection() public async handle(request: Request): Promise { @@ -28,6 +30,10 @@ export default class Kernel extends AppClass { request = await module.apply(request) } + if ( this.inflight ) { + request = await this.inflight.apply(request) + } + for ( const module of this.postflight.toArray() ) { request = await module.apply(request) } @@ -88,6 +94,10 @@ export default class Kernel extends AppClass { this.postflight = this.postflight.push(this.make(module)) return this }, + core: (): Kernel => { + this.inflight = this.make(module) + return this + }, } } } \ No newline at end of file diff --git a/lib/src/http/session/MemorySession.ts b/lib/src/http/session/MemorySession.ts new file mode 100644 index 0000000..75f2ad0 --- /dev/null +++ b/lib/src/http/session/MemorySession.ts @@ -0,0 +1,32 @@ +import Session, { SessionData } from './Session.ts' + +export default class MemorySession extends Session { + private _key!: string + private _data: SessionData = {} + + public get_key(): string { + return this._key + } + + public set_key(key: string) { + this._key = key + } + + public get_data(): SessionData { + return this._data + } + + public set_data(data: SessionData) { + this._data = data + } + + public get_attribute(key: string): any { + return this._data[key] + } + + public set_attribute(key: string, value: any) { + this._data[key] = value + } + + public async persist() {} +} diff --git a/lib/src/http/session/MemorySessionManager.ts b/lib/src/http/session/MemorySessionManager.ts new file mode 100644 index 0000000..5501073 --- /dev/null +++ b/lib/src/http/session/MemorySessionManager.ts @@ -0,0 +1,42 @@ +import { Service } from '../../../../di/src/decorator/Service.ts' +import {Collection} from '../../collection/Collection.ts' +import Session from './Session.ts' +import SessionManager, {InvalidSessionKeyError} from './SessionManager.ts' +import Utility from '../../service/utility/Utility.ts' + +export type SessionRegistrant = { key: string, session: Session } + +@Service() +export default class MemorySessionManager extends SessionManager { + private _sessions: Collection = new Collection() + + public async has_session(key: string): Promise { + return !!this._sessions.firstWhere('key', '=', key) + } + + public async get_session(key?: string): Promise { + if ( !key ) { + const utility: Utility = this.make(Utility) + const session_key: string = key || utility.uuid() + const session = this.make(Session) + + session.set_key(session_key) + await session.persist() + this._sessions.push({ key: session_key, session }) + + return session + } + + const session = this._sessions.firstWhere('key', '=', key) + if ( !session ) throw new InvalidSessionKeyError(key) + return session.session + } + + public async purge(key?: string) { + if ( key ) { + this._sessions = this._sessions.filter(session => session.key !== key) + } else { + this._sessions = new Collection() + } + } +} diff --git a/lib/src/http/session/Session.ts b/lib/src/http/session/Session.ts new file mode 100644 index 0000000..849243f --- /dev/null +++ b/lib/src/http/session/Session.ts @@ -0,0 +1,13 @@ +import AppClass from '../../lifecycle/AppClass.ts' + +export type SessionData = { [key: string]: any } + +export default abstract class Session extends AppClass { + public abstract get_key(): string + public abstract set_key(key: string): void + public abstract async persist(): Promise + public abstract get_data(): SessionData + public abstract set_data(data: SessionData): void + public abstract get_attribute(key: string): any + public abstract set_attribute(key: string, value: any): void +} diff --git a/lib/src/http/session/SessionFactory.ts b/lib/src/http/session/SessionFactory.ts new file mode 100644 index 0000000..28deec2 --- /dev/null +++ b/lib/src/http/session/SessionFactory.ts @@ -0,0 +1,25 @@ +import AbstractFactory from '../../../../di/src/factory/AbstractFactory.ts' +import MemorySession from './MemorySession.ts' +import Session from './Session.ts' +import {DependencyRequirement} from '../../../../di/src/type/DependencyRequirement.ts' +import {Collection} from '../../collection/Collection.ts' + +// TODO support configurable session backends + +export default class SessionFactory extends AbstractFactory { + constructor() { + super({}) + } + + produce(dependencies: any[], parameters: any[]): any { + return new MemorySession() + } + + match(something: any) { + return something === Session + } + + get_dependency_keys(): Collection { + return new Collection() + } +} diff --git a/lib/src/http/session/SessionManager.ts b/lib/src/http/session/SessionManager.ts new file mode 100644 index 0000000..aa28896 --- /dev/null +++ b/lib/src/http/session/SessionManager.ts @@ -0,0 +1,16 @@ +import AppClass from '../../lifecycle/AppClass.ts' +import Session from './Session.ts' + +export class InvalidSessionKeyError extends Error { + constructor(key: any) { + super(`Invalid session key: ${key}. No session exists.`) + } +} + +export default abstract class SessionManager extends AppClass { + + public abstract async get_session(key?: string): Promise + public abstract async has_session(key: string): Promise + public abstract async purge(key?: string): Promise + +} diff --git a/lib/src/http/session/SessionManagerFactory.ts b/lib/src/http/session/SessionManagerFactory.ts new file mode 100644 index 0000000..c7a7a0b --- /dev/null +++ b/lib/src/http/session/SessionManagerFactory.ts @@ -0,0 +1,25 @@ +import AbstractFactory from '../../../../di/src/factory/AbstractFactory.ts' +import {DependencyRequirement} from '../../../../di/src/type/DependencyRequirement.ts' +import {Collection} from '../../collection/Collection.ts' +import MemorySessionManager from './MemorySessionManager.ts' +import SessionManager from './SessionManager.ts' + +// TODO support configurable session backends + +export default class SessionManagerFactory extends AbstractFactory { + constructor() { + super({}) + } + + produce(dependencies: any[], parameters: any[]): any { + return new MemorySessionManager() + } + + match(something: any) { + return something === SessionManager + } + + get_dependency_keys(): Collection { + return new Collection() + } +} diff --git a/lib/src/unit/Scaffolding.ts b/lib/src/unit/Scaffolding.ts index dc9d924..4da3517 100644 --- a/lib/src/unit/Scaffolding.ts +++ b/lib/src/unit/Scaffolding.ts @@ -8,7 +8,9 @@ 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"; +import CacheFactory from '../support/CacheFactory.ts' +import SessionFactory from '../http/session/SessionFactory.ts' +import SessionManagerFactory from '../http/session/SessionManagerFactory.ts' const env = (name: string, fallback?: any) => { const scaffolding = make(Scaffolding) @@ -31,9 +33,7 @@ export default class Scaffolding extends LifecycleUnit { public async up() { this.setup_logging() - - this.logger.verbose('Adding the cache production factory to the container...') - this.injector.register_factory(new CacheFactory()) + this.register_factories() } public setup_logging() { @@ -52,4 +52,13 @@ export default class Scaffolding extends LifecycleUnit { this.logger.info('Logging initialized.', true) } + + public register_factories() { + this.logger.verbose('Adding the cache production factory to the container...') + this.injector.register_factory(new CacheFactory()) + + this.logger.verbose('Adding the session production factories to the container...') + this.injector.register_factory(new SessionFactory()) + this.injector.register_factory(new SessionManagerFactory()) + } }