UNPKG

@athenna/http

Version:

The Athenna Http server. Built on top of fastify.

235 lines (234 loc) 6.84 kB
/** * @athenna/http * * (c) João Lenon <lenon@athenna.io> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ import { Route } from '#src/router/Route'; import { Is, String, Macroable, Options } from '@athenna/common'; export class RouteResource extends Macroable { constructor(resource, controller) { super(); /** * All routes registered in the resource. */ this.routes = []; this.resource = resource.replace(/^\/|\/$/g, ''); this.controller = controller; this.buildRoutes(); } /** * Set a validator for the route resource. * * @example * ```ts * Route.resource('/test', 'TestController').validator('auth') * ``` */ validator(validator, prepend) { this.routes.forEach(route => route.validator(validator, prepend)); return this; } /** * Set a middleware for the route resource. * * @example * ```ts * Route.resource('/test', 'TestController').middleware('auth') * ``` */ middleware(middleware, options = {}) { options = Options.create(options, { only: [], except: [], prepend: false }); if (options.only.length) { this.filter(options.only).forEach(route => { route.middleware(middleware, options.prepend); }); return this; } if (options.except.length) { this.filter(options.except, true).forEach(route => { route.middleware(middleware, options.prepend); }); return this; } this.routes.forEach(route => route.middleware(middleware, options.prepend)); return this; } /** * Set a interceptor for the route resource. * * @example * ```ts * Route.resource('/test', 'TestController').interceptor('response') * ``` */ interceptor(interceptor, options = {}) { options = Options.create(options, { only: [], except: [], prepend: false }); if (options.only.length) { this.filter(options.only).forEach(route => { route.interceptor(interceptor, options.prepend); }); return this; } if (options.except.length) { this.filter(options.except, true).forEach(route => { route.interceptor(interceptor, options.prepend); }); return this; } this.routes.forEach(route => route.interceptor(interceptor, options.prepend)); return this; } /** * Set a terminator for the route resource. * * @example * ```ts * Route.resource('/test', 'TestController').terminator('response') * ``` */ terminator(terminator, options = {}) { options = Options.create(options, { only: [], except: [], prepend: false }); if (options.only.length) { this.filter(options.only).forEach(route => { route.terminator(terminator, options.prepend); }); return this; } if (options.except.length) { this.filter(options.except, true).forEach(route => { route.terminator(terminator, options.prepend); }); return this; } this.routes.forEach(route => route.terminator(terminator, options.prepend)); return this; } /** * Register only the methods in the array. * * @example * ```ts * Route.resource('/test', 'TestController').only(['index']) * ``` */ only(names) { this.filter(names, true).forEach(route => (route.route.deleted = true)); return this; } /** * Register all methods except the methods in the array. * * @example * ```ts * Route.resource('/test', 'TestController').except(['index']) * ``` */ except(names) { this.filter(names, false).forEach(route => (route.route.deleted = true)); return this; } /** * Set up helmet options for route resource. * *@example * ```ts * Route.helmet({ * dnsPrefetchControl: { allow: true } * }) * ``` */ helmet(options) { this.routes.forEach(route => route.helmet(options)); return this; } /** * Set up rate limit options for route resource method. * * @example * ```ts * Route.rateLimit({ * max: 3, * timeWindow: '1 minute' * }) * ``` */ rateLimit(options) { this.routes.forEach(route => route.rateLimit(options)); return this; } /** * Set up schema options for specific route resource methods. * * @example * ```ts * Route.resource('/test', 'TestController').schema({ * index: { response: { 200: { type: 'object' } } }, * store: { body: { type: 'object' } } * }) * ``` */ schema(options) { Object.entries(options).forEach(([name, schema]) => { if (!schema) { return; } this.filter([name]).forEach(route => route.schema(schema)); }); return this; } /** * Filter routes by name. */ filter(names, inverse = false) { return this.routes.filter(route => { const match = names.find(name => route.route.name.endsWith(name)); return inverse ? !match : match; }); } /** * Create the route. */ makeRoute(url, methods, action) { let name = ''; let handler = ''; if (Is.String(this.controller)) { name = `${this.controller}.${action}`; handler = `${this.controller}.${action}`; } else { name = `${this.controller.constructor.name}.${action}`; handler = this.controller[action]; } this.routes.push(new Route(url, methods, handler).name(name)); } /** * Create all the routes. */ buildRoutes() { const resourceTokens = this.resource.split('.'); const mainResource = resourceTokens.pop(); const fullUrl = `${resourceTokens .map(string => `${string}/:${String.toSnakeCase(String.singularize(string))}_id`) .join('/')}/${mainResource}`; this.makeRoute(fullUrl, ['GET'], 'index'); this.makeRoute(fullUrl, ['POST'], 'store'); this.makeRoute(`${fullUrl}/:id`, ['GET'], 'show'); this.makeRoute(`${fullUrl}/:id`, ['PUT'], 'update'); this.makeRoute(`${fullUrl}/:id`, ['DELETE'], 'delete'); } }