Support named route groups; canonical namespaces

This commit is contained in:
Garrett Mills 2021-03-21 21:10:42 -05:00
parent a93cd6d192
commit e8fdb04ae8
Signed by: garrettmills
GPG Key ID: D2BF5FBA8298F246
2 changed files with 60 additions and 2 deletions

View File

@ -1,16 +1,39 @@
import {Collection} from "@extollo/util" import {Collection, ErrorWithContext} from "@extollo/util"
import {AppClass} from "../../lifecycle/AppClass" import {AppClass} from "../../lifecycle/AppClass"
import {RouteHandler} from "./Route" import {RouteHandler} from "./Route"
import {Container} from "@extollo/di"
import {Logging} from "../../service/Logging";
export class RouteGroup extends AppClass { export class RouteGroup extends AppClass {
private static currentGroupNesting: RouteGroup[] = [] private static currentGroupNesting: RouteGroup[] = []
protected static namedGroups: {[key: string]: () => void } = {}
protected middlewares: Collection<{ stage: 'pre' | 'post', handler: RouteHandler }> = new Collection<{stage: "pre" | "post"; handler: RouteHandler}>() protected middlewares: Collection<{ stage: 'pre' | 'post', handler: RouteHandler }> = new Collection<{stage: "pre" | "post"; handler: RouteHandler}>()
public static getCurrentGroupHierarchy(): RouteGroup[] { public static getCurrentGroupHierarchy(): RouteGroup[] {
return [...this.currentGroupNesting] return [...this.currentGroupNesting]
} }
public static named(name: string, define: () => void) {
if ( this.namedGroups[name] ) {
Container.getContainer()
.make<Logging>(Logging)
.warn(`Replacing named route group: ${name}`)
}
this.namedGroups[name] = define
}
public static include(name: string) {
if (!this.namedGroups[name]) {
throw new ErrorWithContext(`No route group exists with name: ${name}`, {name})
}
this.namedGroups[name]()
}
constructor( constructor(
public readonly group: () => void | Promise<void>, public readonly group: () => void | Promise<void>,
public readonly prefix: string public readonly prefix: string

View File

@ -2,7 +2,7 @@
* Base type for a canonical definition. * Base type for a canonical definition.
*/ */
import {Canon} from "./Canon"; import {Canon} from "./Canon";
import {universalPath, UniversalPath} from "@extollo/util"; import {universalPath, UniversalPath, ErrorWithContext} from "@extollo/util";
import {Logging} from "./Logging"; import {Logging} from "./Logging";
import {Inject} from "@extollo/di"; import {Inject} from "@extollo/di";
import * as nodePath from 'path' import * as nodePath from 'path'
@ -14,6 +14,8 @@ export interface CanonicalDefinition {
imported: any, imported: any,
} }
export type CanonicalResolver<T> = (key: string) => T | undefined
/** /**
* Base type for a canonical name reference. * Base type for a canonical name reference.
*/ */
@ -55,6 +57,12 @@ export abstract class Canonical<T> extends Unit {
*/ */
protected loadedItems: { [key: string]: T } = {} protected loadedItems: { [key: string]: T } = {}
/**
* Object mapping canonical namespaces to resolver functions.
* @protected
*/
protected loadedNamespaces: { [key: string]: CanonicalResolver<T> } = {}
/** /**
* Resolve a canonical reference from its string form to a CanonicalReference. * Resolve a canonical reference from its string form to a CanonicalReference.
* @param {string} reference * @param {string} reference
@ -86,9 +94,36 @@ export abstract class Canonical<T> extends Unit {
} }
public get(key: string): T | undefined { public get(key: string): T | undefined {
if ( key.startsWith('@') ) {
const [namespace, ...rest] = key.split(':')
key = rest.join(':')
if ( !this.loadedNamespaces[namespace] ) {
throw new ErrorWithContext(`Unable to find namespace for ${this.canonicalItem}: ${namespace}`, {
canonicalItem: this.canonicalItem,
namespace,
key,
})
}
return this.loadedNamespaces[namespace](key)
}
return this.loadedItems[key] return this.loadedItems[key]
} }
public registerNamespace(name: string, resolver: CanonicalResolver<T>) {
if ( !name.startsWith('@') ) {
throw new ErrorWithContext(`Canonical namespaces must start with @.`, { name })
}
if ( this.loadedNamespaces[name] ) {
this.logging.warn(`Replacing canonical namespace resolver for: ${name}`)
}
this.loadedNamespaces[name] = resolver
}
public async up() { public async up() {
for await ( const entry of this.path.walk() ) { for await ( const entry of this.path.walk() ) {
if ( !entry.endsWith(this.suffix) ) { if ( !entry.endsWith(this.suffix) ) {