|
|
|
@ -2,8 +2,16 @@ import {AppClass} from "../../lifecycle/AppClass";
|
|
|
|
|
import {HTTPMethod, Request} from "../lifecycle/Request";
|
|
|
|
|
import {Application} from "../../lifecycle/Application";
|
|
|
|
|
import {RouteGroup} from "./RouteGroup";
|
|
|
|
|
import {ResponseFactory} from "../response/ResponseFactory";
|
|
|
|
|
import {Response} from "../lifecycle/Response";
|
|
|
|
|
import {Controllers} from "../../service/Controllers";
|
|
|
|
|
import {ErrorWithContext} from "@extollo/util";
|
|
|
|
|
import {Controller} from "../Controller";
|
|
|
|
|
|
|
|
|
|
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 ResolvedRouteHandler = (request: Request) => ResponseObject
|
|
|
|
|
|
|
|
|
|
export type RouteHandler = (request: Request) => void | Promise<void> // FIXME want to do some improvements here
|
|
|
|
|
|
|
|
|
|
// TODO middleware, domains, named routes - support this on groups as well
|
|
|
|
|
|
|
|
|
@ -29,6 +37,8 @@ export class Route extends AppClass {
|
|
|
|
|
for ( const group of stack ) {
|
|
|
|
|
route.prepend(group.prefix)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
route.resolveHandler() // Try to resolve here to catch any errors at boot-time
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for ( const group of registeredGroups ) {
|
|
|
|
@ -82,7 +92,7 @@ export class Route extends AppClass {
|
|
|
|
|
|
|
|
|
|
constructor(
|
|
|
|
|
protected method: HTTPMethod | HTTPMethod[],
|
|
|
|
|
protected handler: RouteHandler,
|
|
|
|
|
protected readonly handler: RouteHandler,
|
|
|
|
|
protected route: string
|
|
|
|
|
) { super() }
|
|
|
|
|
|
|
|
|
@ -128,6 +138,47 @@ export class Route extends AppClass {
|
|
|
|
|
return params
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public resolveHandler(): ResolvedRouteHandler {
|
|
|
|
|
if ( typeof this.handler !== 'string' ) {
|
|
|
|
|
return (request: Request) => {
|
|
|
|
|
// @ts-ignore
|
|
|
|
|
return this.handler(request, request.response)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
const parts = this.handler.split('.')
|
|
|
|
|
if ( parts.length < 1 ) {
|
|
|
|
|
const e = new ErrorWithContext('Route handler does not specify a method name.')
|
|
|
|
|
e.context = {
|
|
|
|
|
handler: this.handler
|
|
|
|
|
}
|
|
|
|
|
throw e
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const [controllerName, methodName] = parts
|
|
|
|
|
|
|
|
|
|
const controllersService = <Controllers> this.make(Controllers)
|
|
|
|
|
const controllerClass = controllersService.get(controllerName)
|
|
|
|
|
if ( !controllerClass ) {
|
|
|
|
|
const e = new ErrorWithContext('Controller not found for route handler.')
|
|
|
|
|
e.context = {
|
|
|
|
|
handler: this.handler,
|
|
|
|
|
controllerName,
|
|
|
|
|
methodName,
|
|
|
|
|
}
|
|
|
|
|
throw e
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (request: Request) => {
|
|
|
|
|
// If not a function, then we got a string reference to a controller method
|
|
|
|
|
// So, we need to use the request container to instantiate the controller
|
|
|
|
|
// and bind the method
|
|
|
|
|
const controller = <Controller> request.make(controllerClass, request)
|
|
|
|
|
const method = controller.getBoundMethod(methodName)
|
|
|
|
|
return method()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private prepend(prefix: string) {
|
|
|
|
|
if ( !prefix.endsWith('/') ) prefix = `${prefix}/`
|
|
|
|
|
if ( this.route.startsWith('/') ) this.route = this.route.substring(1)
|
|
|
|
|