UNPKG

steady-api

Version:

Configurable REST API built with Express and TypeScript

160 lines (134 loc) 4.07 kB
export interface IRoute { description: string method: string url: string controller: string action: string streaming: boolean authentication: IRouteAuthentication params: IRouteParameter[] } export interface IRouteAuthentication { controller: string action: string } export interface IDocsRoute extends IRoute { id: string name: string } export interface IRouteParameter { name: string required: boolean type: string default?: any values?: any[] regex?: string min?: number max?: number example?: string more?: IRouteParameterMore } export interface IRouteParameterMore { default?: string regex?: string values?: any[] min?: number max?: number } export class Routes { public routes: IRoute[] = []; constructor(routes) { this.routes = routes; this.validateRoutes(); this.routes = this.routes.map((route: IRoute) => { route.method = route.method.toLowerCase(); route.params.forEach((param: IRouteParameter) => { param.type = param.type.toLowerCase(); param.example = this.getParamExample(param); param.more = this.getParamMoreDetails(param); }) return route; }); } validateRoutes() { const routeRequiredFields = ['description', 'method', 'url', 'controller', 'action']; const paramRequiredFields = ['name', 'required', 'type'] this.routes.forEach(route => { // make sure params is an array if (!route.params) { route.params = []; } if (!Array.isArray(route.params)) { throw new Error(`Params must be an array for ${route.method.toUpperCase()} ${route.url}`) } // does this route have the required properties const hasRequiredFields = routeRequiredFields.every(field => !!route[field]) if (!hasRequiredFields) throw new Error(`Invalid Route definition for ${route.method.toUpperCase()} ${route.url} - missing required field`) route.params.forEach((param, index) => { const hasRequiredParamFields = paramRequiredFields.every(field => typeof param[field] !== 'undefined'); if (!hasRequiredParamFields) throw new Error(`Invalid parameter definition at index ${index} for ${route.method.toUpperCase()} ${route.url} - missing required field`); // does enum have associated values if (param.type === 'enum' && (!param.values || !Array.isArray(param.values))) { throw new Error(`No values defined for enum parameter in ${route.method.toUpperCase()} ${route.url}`) } }) }); } getDocsRoutes(): IDocsRoute[] { const routes = this.routes.map((route: IRoute) => { const d = Object.assign( {}, route, { id: `${route.method}-${route.url}`, name: `${route.method.toUpperCase()} ${route.url}` } ); return d; }) .sort((a: IDocsRoute, b: IDocsRoute) => { if (a.id < b.id) return -1; if (a.id > b.id) return 1; return 0; }); return routes; } getParamExample(param: IRouteParameter): string { if (param.example) return param.example; let example: any = ''; switch (param.type) { case 'string': example = 'foo'; break; case 'boolean': example = true; break; case 'number': example = 1234; break; case 'enum': example = param.values.join(', ') break; case 'date': example = '2014-09-27' break; case 'url': example = 'http://www.example.com'; break; case 'email': example = 'john@example.com'; break; } return example; } getParamMoreDetails(param: IRouteParameter): IRouteParameterMore { const options: string[] = ['default', 'regex', 'values', 'min', 'max']; const more: IRouteParameterMore = {}; options.forEach((option: string) => { if (typeof param[option] !== 'undefined') { more[option] = param[option] } }) return more; } }