|
|
@ -5,15 +5,17 @@ import {RouteGroup} from "./RouteGroup";
|
|
|
|
import {ResponseFactory} from "../response/ResponseFactory";
|
|
|
|
import {ResponseFactory} from "../response/ResponseFactory";
|
|
|
|
import {Response} from "../lifecycle/Response";
|
|
|
|
import {Response} from "../lifecycle/Response";
|
|
|
|
import {Controllers} from "../../service/Controllers";
|
|
|
|
import {Controllers} from "../../service/Controllers";
|
|
|
|
import {ErrorWithContext} from "@extollo/util";
|
|
|
|
import {ErrorWithContext, Collection} from "@extollo/util";
|
|
|
|
import {Controller} from "../Controller";
|
|
|
|
import {Controller} from "../Controller";
|
|
|
|
|
|
|
|
import {Middlewares} from "../../service/Middlewares";
|
|
|
|
|
|
|
|
import {Middleware} from "./Middleware";
|
|
|
|
|
|
|
|
|
|
|
|
export type ResponseObject = ResponseFactory | string | number | void | any | Promise<ResponseObject>
|
|
|
|
export type ResponseObject = ResponseFactory | string | number | void | any | Promise<ResponseObject>
|
|
|
|
export type RouteHandler = ((request: Request, response: Response) => ResponseObject) | ((request: Request) => ResponseObject) | (() => ResponseObject) | string
|
|
|
|
export type RouteHandler = ((request: Request, response: Response) => ResponseObject) | ((request: Request) => ResponseObject) | (() => ResponseObject) | string
|
|
|
|
export type ResolvedRouteHandler = (request: Request) => ResponseObject
|
|
|
|
export type ResolvedRouteHandler = (request: Request) => ResponseObject
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// TODO middleware, domains, named routes - support this on groups as well
|
|
|
|
// TODO domains, named routes - support this on groups as well
|
|
|
|
|
|
|
|
|
|
|
|
export class Route extends AppClass {
|
|
|
|
export class Route extends AppClass {
|
|
|
|
private static registeredRoutes: Route[] = []
|
|
|
|
private static registeredRoutes: Route[] = []
|
|
|
@ -36,9 +38,18 @@ export class Route extends AppClass {
|
|
|
|
for ( const route of registeredRoutes ) {
|
|
|
|
for ( const route of registeredRoutes ) {
|
|
|
|
for ( const group of stack ) {
|
|
|
|
for ( const group of stack ) {
|
|
|
|
route.prepend(group.prefix)
|
|
|
|
route.prepend(group.prefix)
|
|
|
|
|
|
|
|
group.getGroupMiddlewareDefinitions()
|
|
|
|
|
|
|
|
.each(def => route.prependMiddleware(def))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
route.resolveHandler() // Try to resolve here to catch any errors at boot-time
|
|
|
|
for ( const group of this.compiledGroupStack ) {
|
|
|
|
|
|
|
|
group.getGroupMiddlewareDefinitions()
|
|
|
|
|
|
|
|
.each(def => route.appendMiddleware(def))
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
route.resolvePreflight() // Try to resolve here to catch any errors at boot-time and pre-compile
|
|
|
|
|
|
|
|
route.resolveHandler()
|
|
|
|
|
|
|
|
route.resolvePostflight()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for ( const group of registeredGroups ) {
|
|
|
|
for ( const group of registeredGroups ) {
|
|
|
@ -90,6 +101,11 @@ export class Route extends AppClass {
|
|
|
|
return grp
|
|
|
|
return grp
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
protected middlewares: Collection<{ stage: 'pre' | 'post', handler: RouteHandler }> = new Collection<{stage: "pre" | "post"; handler: RouteHandler}>()
|
|
|
|
|
|
|
|
protected _compiledPreflight?: ResolvedRouteHandler[]
|
|
|
|
|
|
|
|
protected _compiledHandler?: ResolvedRouteHandler
|
|
|
|
|
|
|
|
protected _compiledPostflight?: ResolvedRouteHandler[]
|
|
|
|
|
|
|
|
|
|
|
|
constructor(
|
|
|
|
constructor(
|
|
|
|
protected method: HTTPMethod | HTTPMethod[],
|
|
|
|
protected method: HTTPMethod | HTTPMethod[],
|
|
|
|
protected readonly handler: RouteHandler,
|
|
|
|
protected readonly handler: RouteHandler,
|
|
|
@ -138,7 +154,63 @@ export class Route extends AppClass {
|
|
|
|
return params
|
|
|
|
return params
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public resolvePreflight(): ResolvedRouteHandler[] {
|
|
|
|
|
|
|
|
if ( !this._compiledPreflight ) {
|
|
|
|
|
|
|
|
this._compiledPreflight = this.resolveMiddlewareHandlersForStage('pre')
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return this._compiledPreflight
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public resolvePostflight(): ResolvedRouteHandler[] {
|
|
|
|
|
|
|
|
if ( !this._compiledPostflight ) {
|
|
|
|
|
|
|
|
this._compiledPostflight = this.resolveMiddlewareHandlersForStage('post')
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return this._compiledPostflight
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public resolveHandler(): ResolvedRouteHandler {
|
|
|
|
public resolveHandler(): ResolvedRouteHandler {
|
|
|
|
|
|
|
|
if ( !this._compiledHandler ) {
|
|
|
|
|
|
|
|
this._compiledHandler = this._resolveHandler()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return this._compiledHandler
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pre(middleware: RouteHandler) {
|
|
|
|
|
|
|
|
this.middlewares.push({
|
|
|
|
|
|
|
|
stage: 'pre',
|
|
|
|
|
|
|
|
handler: middleware
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return this
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
post(middleware: RouteHandler) {
|
|
|
|
|
|
|
|
this.middlewares.push({
|
|
|
|
|
|
|
|
stage: 'post',
|
|
|
|
|
|
|
|
handler: middleware,
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return this
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private prepend(prefix: string) {
|
|
|
|
|
|
|
|
if ( !prefix.endsWith('/') ) prefix = `${prefix}/`
|
|
|
|
|
|
|
|
if ( this.route.startsWith('/') ) this.route = this.route.substring(1)
|
|
|
|
|
|
|
|
this.route = `${prefix}${this.route}`
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private prependMiddleware(def: { stage: 'pre' | 'post', handler: RouteHandler }) {
|
|
|
|
|
|
|
|
this.middlewares.prepend(def)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private appendMiddleware(def: { stage: 'pre' | 'post', handler: RouteHandler }) {
|
|
|
|
|
|
|
|
this.middlewares.push(def)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private _resolveHandler(): ResolvedRouteHandler {
|
|
|
|
if ( typeof this.handler !== 'string' ) {
|
|
|
|
if ( typeof this.handler !== 'string' ) {
|
|
|
|
return (request: Request) => {
|
|
|
|
return (request: Request) => {
|
|
|
|
// @ts-ignore
|
|
|
|
// @ts-ignore
|
|
|
@ -146,7 +218,7 @@ export class Route extends AppClass {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
const parts = this.handler.split('.')
|
|
|
|
const parts = this.handler.split('.')
|
|
|
|
if ( parts.length < 1 ) {
|
|
|
|
if ( parts.length < 2 ) {
|
|
|
|
const e = new ErrorWithContext('Route handler does not specify a method name.')
|
|
|
|
const e = new ErrorWithContext('Route handler does not specify a method name.')
|
|
|
|
e.context = {
|
|
|
|
e.context = {
|
|
|
|
handler: this.handler
|
|
|
|
handler: this.handler
|
|
|
@ -179,10 +251,45 @@ export class Route extends AppClass {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private prepend(prefix: string) {
|
|
|
|
private resolveMiddlewareHandlersForStage(stage: 'pre' | 'post'): ResolvedRouteHandler[] {
|
|
|
|
if ( !prefix.endsWith('/') ) prefix = `${prefix}/`
|
|
|
|
return this.middlewares.where('stage', '=', stage)
|
|
|
|
if ( this.route.startsWith('/') ) this.route = this.route.substring(1)
|
|
|
|
.map<ResolvedRouteHandler>(def => {
|
|
|
|
this.route = `${prefix}${this.route}`
|
|
|
|
if ( typeof def.handler !== 'string' ) {
|
|
|
|
|
|
|
|
return (request: Request) => {
|
|
|
|
|
|
|
|
// @ts-ignore
|
|
|
|
|
|
|
|
return def.handler(request, request.response)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
const parts = def.handler.split('.')
|
|
|
|
|
|
|
|
if ( parts.length < 2 ) {
|
|
|
|
|
|
|
|
parts.push('apply') // default middleware method name, if none provided
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const [middlewareName, methodName] = parts
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const middlewaresService = <Middlewares> this.make(Middlewares)
|
|
|
|
|
|
|
|
const middlewareClass = middlewaresService.get(middlewareName)
|
|
|
|
|
|
|
|
if ( !middlewareClass ) {
|
|
|
|
|
|
|
|
const e = new ErrorWithContext('Middleware not found for route handler.')
|
|
|
|
|
|
|
|
e.context = {
|
|
|
|
|
|
|
|
handler: def.handler,
|
|
|
|
|
|
|
|
middlewareName,
|
|
|
|
|
|
|
|
methodName,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
throw e
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return (request: Request) => {
|
|
|
|
|
|
|
|
// If not a function, then we got a string reference to a middleware method
|
|
|
|
|
|
|
|
// So, we need to use the request container to instantiate the middleware
|
|
|
|
|
|
|
|
// and bind the method
|
|
|
|
|
|
|
|
const middleware = <Middleware> request.make(middlewareClass, request)
|
|
|
|
|
|
|
|
const method = middleware.getBoundMethod(methodName)
|
|
|
|
|
|
|
|
return method()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
.toArray()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
toString() {
|
|
|
|
toString() {
|
|
|
|