@athenna/http
Version:
The Athenna Http server. Built on top of fastify.
235 lines (234 loc) • 6.84 kB
JavaScript
/**
* @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');
}
}