From 32050cb2ce133c2a847c825563851992d58ae7f6 Mon Sep 17 00:00:00 2001 From: garrettmills Date: Thu, 20 Jan 2022 00:54:55 -0600 Subject: [PATCH] Fix route CLI commands --- src/cli/directive/RouteDirective.ts | 43 +++++++++++++---------- src/cli/directive/RoutesDirective.ts | 13 ++++--- src/http/routing/Route.ts | 52 ++++++++++++++++++++++++++-- 3 files changed, 84 insertions(+), 24 deletions(-) diff --git a/src/cli/directive/RouteDirective.ts b/src/cli/directive/RouteDirective.ts index 5cc281d..e81270a 100644 --- a/src/cli/directive/RouteDirective.ts +++ b/src/cli/directive/RouteDirective.ts @@ -2,6 +2,7 @@ import {Directive, OptionDefinition} from '../Directive' import {Inject, Injectable} from '../../di' import {Routing} from '../../service/Routing' import Table = require('cli-table') +import {HTTPMethod} from '../../http/lifecycle/Request' @Injectable() export class RouteDirective extends Directive { @@ -32,34 +33,40 @@ export class RouteDirective extends Directive { .toLowerCase() .trim() - /* this.routing.getCompiled() - .filter(match => match.getRoute().trim() === route && (!method || match.getMethod() === method)) - .tap(matches => { - if ( !matches.length ) { - this.error('No matching routes found. (Use `./ex routes` to list)') - process.exitCode = 1 + const matched = this.routing.getCompiled() + .filter(match => { + if ( !method ) { + return match.getRoute().trim() === route } + + return ( + (match.getRoute().trim() === route && match.getMethods().includes(method as HTTPMethod)) + || match.match(method as HTTPMethod, route) + ) }) - .each(match => { - const pre = match.getPreflight() - .map<[string, string]>(ware => [ware.stage, this.handlerToString(ware.handler)]) + .some(match => { + const displays = match.getDisplays() + .map<[string, string]>(ware => [ware.stage, ware.display]) - const post = match.getMiddlewares() - .where('stage', '=', 'post') - .map<[string, string]>(ware => [ware.stage, this.handlerToString(ware.handler)]) + if ( displays.isEmpty() ) { + return + } - const maxLen = match.getMiddlewares().max(ware => this.handlerToString(ware.handler).length) + const maxLen = displays.max(x => x[1].length) const table = new Table({ head: ['Stage', 'Handler'], - colWidths: [10, Math.max(maxLen, match.getDisplayableHandler().length) + 2], + colWidths: [10, maxLen + 2], }) - table.push(...pre.toArray()) - table.push(['handler', match.getDisplayableHandler()]) - table.push(...post.toArray()) + displays.each(x => table.push(x)) this.info(`\nRoute: ${match}\n\n${table}`) - })*/ + return true + }) + + if ( !matched ) { + this.error('No matching routes found.') + } } } diff --git a/src/cli/directive/RoutesDirective.ts b/src/cli/directive/RoutesDirective.ts index 55309bb..c59538f 100644 --- a/src/cli/directive/RoutesDirective.ts +++ b/src/cli/directive/RoutesDirective.ts @@ -17,9 +17,14 @@ export class RoutesDirective extends Directive { } async handle(): Promise { - /* const maxRouteLength = this.routing.getCompiled().max(route => String(route).length) - const maxHandlerLength = this.routing.getCompiled().max(route => route.getDisplayableHandler().length) - const rows = this.routing.getCompiled().map<[string, string]>(route => [String(route), route.getDisplayableHandler()]) + const compiled = this.routing.getCompiled() + + const maxRouteLength = compiled.strings().max('length') + const maxHandlerLength = compiled.mapCall('getHandlerDisplay') + .whereDefined() + .max('length') + + const rows = compiled.map(route => [String(route), route.getHandlerDisplay()]) const table = new Table({ head: ['Route', 'Handler'], @@ -28,6 +33,6 @@ export class RoutesDirective extends Directive { table.push(...rows.toArray()) - this.info('\n' + table)*/ + this.info('\n' + table) } } diff --git a/src/http/routing/Route.ts b/src/http/routing/Route.ts index 03a52fc..575ddad 100644 --- a/src/http/routing/Route.ts +++ b/src/http/routing/Route.ts @@ -1,4 +1,4 @@ -import {Collection, Either, ErrorWithContext, Pipeline, PrefixTypeArray, right} from '../../util' +import {Collection, Either, ErrorWithContext, Maybe, Pipeline, PrefixTypeArray, right} from '../../util' import {ResponseFactory} from '../response/ResponseFactory' import {HTTPMethod, Request} from '../lifecycle/Request' import {constructable, Constructable, Container, Instantiable, isInstantiableOf, TypedDependencyKey} from '../../di' @@ -180,6 +180,8 @@ export class Route TReturn> + protected displays: Collection<{stage: 'pre'|'post'|'handler', display: string}> = new Collection() + constructor( protected method: HTTPMethod | HTTPMethod[], protected route: string, @@ -230,6 +232,14 @@ export class Route { + return this.displays.clone() + } + + public getHandlerDisplay(): Maybe { + return this.displays.firstWhere('stage', '=', 'handler')?.display + } + /** * Returns true if this route matches the given HTTP verb and request path. * @param method @@ -313,6 +323,7 @@ export class Route( @@ -322,6 +333,11 @@ export class Route(key) .tap(inst => Function.prototype.bind.call(selector(inst), inst as any) as ((...params: THandlerParams) => TReturn)) + this.displays.push({ + stage: 'handler', + display: `${key.name}(${selector})`, + }) + Route.registeredRoutes.push(this as unknown as Route) // man this is stupid return this as HandledRoute } @@ -332,25 +348,45 @@ export class Route() .tap(() => handler) + this.displays.push({ + stage: 'handler', + display: `(closure)`, + }) + Route.registeredRoutes.push(this as unknown as Route) return this as HandledRoute } public pre(middleware: Instantiable): this { this.preflight.push(request => request.make(middleware, request).apply()) + this.displays.push({ + stage: 'pre', + display: `${middleware.name}`, + }) + return this } public post(middleware: Instantiable): this { this.postflight.push(request => request.make(middleware, request).apply()) + this.displays.push({ + stage: 'pre', + display: `${middleware.name}`, + }) + return this } - public input>(validator: ValidatorFactory): Route, THandlerParams>> { + public input(validator: ValidatorFactory): Route, THandlerParams>> { if ( !(validator instanceof Validator) ) { validator = validator() } + this.displays.push({ + stage: 'pre', + display: `input(${validator.constructor.name})`, + }) + return this.parameterMiddleware(validateMiddleware(validator)) } @@ -372,6 +408,18 @@ export class Route ${this.route}` } + /** + * Return a new Pipe of this collection. + */ + pipeTo(pipeline: Pipeline): TOut { + return pipeline.apply(this) + } + + /** Build and apply a pipeline. */ + pipe(builder: (pipeline: Pipeline) => Pipeline): TOut { + return builder(Pipeline.id()).apply(this) + } + /** Prefix the route's path with the given prefix, normalizing `/` characters. */ private prepend(prefix: string): this { if ( !prefix.endsWith('/') ) {