├── http.ts ├── controllers.ts ├── test ├── globals.d.ts ├── public │ └── index.html ├── server.ts ├── Server.js.map ├── tsconfig.json ├── startup.js.map ├── startup.ts ├── controllers │ └── UsersController.ts └── Controllers │ └── UsersController.js.map ├── src ├── middleware │ ├── Filter.ts │ ├── HttpResponseMessage.ts │ ├── HttpMessageHandler.ts │ ├── HttpRequestMessage.ts │ └── RequestHandler.ts ├── security │ ├── Identity.ts │ └── Principal.ts ├── server │ ├── Startup.ts │ ├── StartOptions.ts │ ├── AppBuilder.ts │ ├── HttpConfiguration.ts │ └── WebApp.ts ├── routing │ ├── HttpRoute.ts │ └── annotations │ │ ├── Index.ts │ │ ├── HttpPost.ts │ │ ├── HttpPut.ts │ │ ├── HttpDelete.ts │ │ ├── Route.ts │ │ ├── HttpGet.ts │ │ └── RoutePrefix.ts ├── http │ └── HttpStatusCode.ts ├── controllers │ └── ApiController.ts └── collections │ ├── Collection.ts │ ├── Dictionary2.ts │ └── Dictionary.ts ├── security.ts ├── .vscode ├── settings.json ├── tasks.json └── launch.json ├── collections.ts ├── .npmignore ├── server.ts ├── middleware.ts ├── typings.json ├── CHANGELOG.md ├── routing.ts ├── tsconfig.json ├── .gitignore ├── gulpfile.js ├── LICENSE ├── package.json └── README.md /http.ts: -------------------------------------------------------------------------------- 1 | export * from './src/http/HttpStatusCode'; 2 | -------------------------------------------------------------------------------- /controllers.ts: -------------------------------------------------------------------------------- 1 | export * from './src/controllers/ApiController'; 2 | -------------------------------------------------------------------------------- /test/globals.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /src/middleware/Filter.ts: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | export interface IFilter { 4 | 5 | } -------------------------------------------------------------------------------- /test/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Hello World

4 | 5 | -------------------------------------------------------------------------------- /security.ts: -------------------------------------------------------------------------------- 1 | export * from './src/security/Identity'; 2 | export * from './src/security/Principal'; 3 | 4 | -------------------------------------------------------------------------------- /src/middleware/HttpResponseMessage.ts: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | export class HttpResponseMessage { 4 | 5 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "files.autoSave": "afterDelay", 4 | } -------------------------------------------------------------------------------- /collections.ts: -------------------------------------------------------------------------------- 1 | export * from './src/collections/Collection'; 2 | export * from './src/collections/Dictionary'; 3 | export * from './src/collections/Dictionary2'; 4 | -------------------------------------------------------------------------------- /src/security/Identity.ts: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | export interface IIdentity { 4 | authenticationType: string, 5 | isAuthenticated: boolean, 6 | name: string 7 | } -------------------------------------------------------------------------------- /src/server/Startup.ts: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import {IAppBuilder} from "./AppBuilder"; 4 | 5 | export interface IStartup { 6 | Configuration(IAppBuilder); 7 | } 8 | -------------------------------------------------------------------------------- /src/security/Principal.ts: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import {IIdentity} from "./Identity"; 4 | 5 | export interface IPrincipal { 6 | identity: IIdentity; 7 | isInRole(role: string); 8 | } -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | 3 | test/ 4 | typings/ 5 | hapi-webapi-* 6 | 7 | *.ts 8 | !*.d.ts 9 | 10 | .npmignore 11 | 12 | *.log 13 | *.tgz 14 | 15 | tsconfig.json 16 | typings.json 17 | npm-debug.log* 18 | -------------------------------------------------------------------------------- /src/routing/HttpRoute.ts: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import {IApiController} from "../controllers/ApiController"; 4 | 5 | export interface IHttpRoute { 6 | handler: IApiController; 7 | routeTemplate: string; 8 | } -------------------------------------------------------------------------------- /src/routing/annotations/Index.ts: -------------------------------------------------------------------------------- 1 | export * from "./HttpDelete"; 2 | export * from "./HttpPut"; 3 | export * from "./HttpGet"; 4 | export * from "./HttpPost"; 5 | export * from "./Route"; 6 | export * from "./RoutePrefix"; 7 | -------------------------------------------------------------------------------- /src/server/StartOptions.ts: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import {IDictionary2} from "../collections/Dictionary2"; 4 | export class StartOptions { 5 | public port: number; 6 | public settings: IDictionary2; 7 | public cors: boolean; 8 | } -------------------------------------------------------------------------------- /server.ts: -------------------------------------------------------------------------------- 1 | export * from './src/server/AppBuilder'; 2 | export * from './src/server/HttpConfiguration'; 3 | export * from './src/server/StartOptions'; 4 | export * from './src/server/Startup'; 5 | export * from './src/server/WebApp'; 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /test/server.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import {WebApp, StartOptions} from '../server'; 4 | import {Startup} from "./Startup"; 5 | 6 | var options = new StartOptions(); 7 | options.port = 4600; 8 | 9 | WebApp.Start(Startup, options); -------------------------------------------------------------------------------- /middleware.ts: -------------------------------------------------------------------------------- 1 | export * from './src/middleware/Filter'; 2 | export * from './src/middleware/HttpMessageHandler'; 3 | export * from './src/middleware/HttpRequestMessage'; 4 | export * from './src/middleware/HttpResponseMessage'; 5 | export * from './src/middleware/RequestHandler'; 6 | 7 | 8 | -------------------------------------------------------------------------------- /typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "globalDependencies": { 3 | "hapi": "registry:dt/hapi#13.0.0+20160417144856", 4 | "node": "registry:dt/node#4.0.0+20160412142033" 5 | }, 6 | "dependencies": { 7 | "es6-promise": "registry:npm/es6-promise#3.0.0+20160211003958" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/middleware/HttpMessageHandler.ts: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import {HttpRequestMessage} from "./HttpRequestMessage"; 4 | import {HttpResponseMessage} from "./HttpResponseMessage"; 5 | 6 | export abstract class HttpMessageHandler { 7 | public abstract sendAsync(request: HttpRequestMessage): HttpResponseMessage; 8 | } -------------------------------------------------------------------------------- /test/Server.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"Server.js","sourceRoot":"","sources":["Server.ts"],"names":[],"mappings":"AAGA,YAAY,CAAC;AAEb,IAAO,MAAM,WAAW,cAAc,CAAC,CAAC;AACxC,wBAAsB,WAAW,CAAC,CAAA;AAElC,IAAI,OAAO,GAAG,IAAI,MAAM,CAAC,YAAY,EAAE,CAAC;AACxC,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;AAEpB,MAAM,CAAC,MAAM,CAAC,KAAK,CAAU,iBAAO,EAAE,OAAO,CAAC,CAAC"} -------------------------------------------------------------------------------- /src/routing/annotations/HttpPost.ts: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import "reflect-metadata"; 4 | 5 | export function HttpPost() { 6 | return (target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor) => { 7 | //descriptor.enumerable = route; 8 | Reflect.defineMetadata("Method", 'POST', descriptor.value); 9 | return descriptor; 10 | }; 11 | } -------------------------------------------------------------------------------- /src/routing/annotations/HttpPut.ts: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import "reflect-metadata"; 4 | 5 | export function HttpPut() { 6 | return (target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor) => { 7 | //descriptor.enumerable = route; 8 | Reflect.defineMetadata("Method", 'PUT', descriptor.value); 9 | return descriptor; 10 | }; 11 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 0.0.5 (2016-05-19) 2 | 3 | ### Bug Fixes 4 | 5 | * Updates dependencies, updates typings, disables good-console due to runtime error. 6 | 7 | 8 | # 0.0.1 (2016-03-06) 9 | 10 | ### Bug Fixes 11 | 12 | ### Features 13 | 14 | * **AppBuilder:** Adds static files support. ([4c35462](https://github.com/sondreb/hapi-webapi/commit/4c35462)) 15 | 16 | ### BREAKING CHANGES 17 | 18 | -------------------------------------------------------------------------------- /src/routing/annotations/HttpDelete.ts: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import "reflect-metadata"; 4 | 5 | export function HttpDelete() { 6 | return (target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor) => { 7 | //descriptor.enumerable = route; 8 | Reflect.defineMetadata("Method", 'DELETE', descriptor.value); 9 | return descriptor; 10 | }; 11 | } -------------------------------------------------------------------------------- /routing.ts: -------------------------------------------------------------------------------- 1 | export * from './src/routing/HttpRoute'; 2 | export * from './src/routing/annotations/HttpDelete'; 3 | export * from './src/routing/annotations/HttpGet'; 4 | export * from './src/routing/annotations/HttpPost'; 5 | export * from './src/routing/annotations/HttpPut'; 6 | export * from './src/routing/annotations/Route'; 7 | export * from './src/routing/annotations/RoutePrefix'; 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/routing/annotations/Route.ts: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import "reflect-metadata"; 4 | 5 | export function Route(template: string) { 6 | return (target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor) => { 7 | //descriptor.enumerable = route; 8 | console.log('ROUTE DECORATOR!'); 9 | Reflect.defineMetadata("Route", template, descriptor.value); 10 | return descriptor; 11 | }; 12 | } -------------------------------------------------------------------------------- /src/routing/annotations/HttpGet.ts: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import "reflect-metadata"; 4 | 5 | export function HttpGet() { 6 | return (target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor) => { 7 | //descriptor.enumerable = route; 8 | console.log('HTTP GET DECORATOR!'); 9 | console.log(descriptor); 10 | 11 | //target.actions.add(); 12 | 13 | Reflect.defineMetadata("Method", 'GET', descriptor.value); 14 | return descriptor; 15 | }; 16 | } -------------------------------------------------------------------------------- /src/http/HttpStatusCode.ts: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | export enum HttpStatusCode { 4 | Continue = 100, 5 | OK = 200, 6 | Created = 201, 7 | Accepted = 202, 8 | NoContent = 204, 9 | MovedPermanently = 301, 10 | Moved = 301, 11 | Found = 302, 12 | Redirect = 302, 13 | BadRequest = 400, 14 | Unauthorized = 401, 15 | Forbidden = 403, 16 | NotFound = 404, 17 | MethodNotAllowed = 405, 18 | NotAcceptable = 406, 19 | InternalServerError = 500, 20 | NotImplemented = 501, 21 | ServiceUnavailable = 503 22 | }; -------------------------------------------------------------------------------- /src/routing/annotations/RoutePrefix.ts: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import "reflect-metadata"; 4 | 5 | export function RoutePrefix(prefix: string) { 6 | return function(target: Function) { 7 | Reflect.defineMetadata("RoutePrefix", prefix, target); 8 | //Reflect.defineMetadata("RoutePrefix", prefix, RoutePrefix, "method"); 9 | //Reflect.defineMetadata("routeprefix", prefix, target, null); 10 | 11 | //console.log('Register RoutePrefix for service: "/' + prefix + '"'); 12 | //console.log("Target: ", target); 13 | } 14 | } -------------------------------------------------------------------------------- /test/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "declaration": true, 4 | "target": "es6", 5 | "module": "commonjs", 6 | "watch": false, 7 | "experimentalDecorators": true, 8 | "emitDecoratorMetadata": true, 9 | "noImplicitAny": false, 10 | "removeComments": true, 11 | "preserveConstEnums": true, 12 | "inlineSourceMap": false, 13 | "sourceMap": false, 14 | "moduleResolution": "node" 15 | }, 16 | "exclude": [ 17 | "node_modules", 18 | "typings" 19 | ] 20 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "declaration": true, 4 | "target": "es6", 5 | "module": "commonjs", 6 | "watch": false, 7 | "experimentalDecorators": true, 8 | "emitDecoratorMetadata": true, 9 | "noImplicitAny": false, 10 | "removeComments": true, 11 | "preserveConstEnums": true, 12 | "inlineSourceMap": false, 13 | "sourceMap": false, 14 | "moduleResolution": "node" 15 | }, 16 | "exclude": [ 17 | "node_modules", 18 | "typings", 19 | "test" 20 | ] 21 | } -------------------------------------------------------------------------------- /src/middleware/HttpRequestMessage.ts: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | export class HttpRequestMessage { 4 | 5 | constructor(hapiRequest: any) { 6 | this.hapiRequest = hapiRequest; 7 | } 8 | 9 | get requestUri(): any { 10 | return this.hapiRequest.url; 11 | } 12 | 13 | get method(): any { 14 | return this.hapiRequest.method.toUpperCase(); 15 | } 16 | 17 | get headers(): any { 18 | return this.hapiRequest.headers; 19 | } 20 | 21 | get content(): any { 22 | return this.hapiRequest.raw.req; 23 | } 24 | 25 | get version(): string { 26 | return this.hapiRequest.raw.req.httpVersion; 27 | } 28 | 29 | //content: string; 30 | properties: Array; 31 | hapiRequest: any; 32 | } 33 | -------------------------------------------------------------------------------- /test/startup.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"Startup.js","sourceRoot":"","sources":["Startup.ts"],"names":["Startup","Startup.Configuration"],"mappings":"AACA,YAAY,CAAC;AAEb,IAAO,MAAM,WAAW,cAAc,CAAC,CAAC;AACxC,gCAA8B,+BAA+B,CAAC,CAAA;AAE9D,MAAM,GAAG,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;AAEvC;IACIA,aAAaA,CAACA,GAAuBA;QAEjCC,IAAIA,MAAMA,GAAGA,IAAIA,MAAMA,CAACA,iBAAiBA,EAAEA,CAACA;QAC5CA,GAAGA,CAACA,SAASA,CAACA,MAAMA,CAACA,CAACA;QAEtBA,MAAMA,CAACA,aAAaA,CAACA,EAAEA,KAAKA,EAAEA,eAAeA,EAAEA,WAAWA,EAAEA,GAAGA,CAACA,WAAWA,EAAEA,OAAOA,EAAEA,GAAGA,CAACA,OAAOA,EAAEA,CAACA,CAACA;QACrGA,MAAMA,CAACA,eAAeA,CAACA,EAAEA,KAAKA,EAAEA,qBAAqBA,GAAGA,GAAGA,CAACA,OAAOA,EAAEA,IAAIA,EAAEA,OAAOA,EAAEA,CAACA,CAACA;QAGtFA,GAAGA,CAACA,WAAWA,CAACA,GAAGA,CAACA,iCAAeA,CAACA,CAACA;QAErCA,GAAGA,CAACA,cAAcA,EAAEA,CAACA;IAGzBA,CAACA;AACLD,CAACA;AAhBY,eAAO,UAgBnB,CAAA"} -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.1.0", 3 | "command": "tsc", 4 | "isShellCommand": true, 5 | "args": [ 6 | "-module", 7 | "commonjs", 8 | "--experimentalDecorators" 9 | ], 10 | "showOutput": "silent", 11 | "isWatching": false, 12 | "problemMatcher": "$tsc-watch" 13 | } 14 | /* 15 | { 16 | "version": "0.1.0", 17 | "command": "gulp", 18 | "isShellCommand": true, 19 | "args": [ 20 | "--no-color" 21 | ], 22 | "tasks": [ 23 | { 24 | "taskName": "build", 25 | "args": [], 26 | "isBuildCommand": true, 27 | "isWatching": false, 28 | "problemMatcher": [ 29 | "$lessCompile", 30 | "$tsc", 31 | "$jshint" 32 | ] 33 | } 34 | ] 35 | }*/ -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | hapi-webapi-* 3 | 4 | *.d.ts 5 | *.js.map 6 | *.js 7 | !gulpfile.js 8 | 9 | typings/ 10 | *.log 11 | *.tgz 12 | 13 | # Logs 14 | logs 15 | *.log 16 | npm-debug.log* 17 | 18 | # Runtime data 19 | pids 20 | *.pid 21 | *.seed 22 | 23 | # Directory for instrumented libs generated by jscoverage/JSCover 24 | lib-cov 25 | 26 | # Coverage directory used by tools like istanbul 27 | coverage 28 | 29 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 30 | .grunt 31 | 32 | # node-waf configuration 33 | .lock-wscript 34 | 35 | # Compiled binary addons (http://nodejs.org/api/addons.html) 36 | build/Release 37 | 38 | # Dependency directories 39 | node_modules 40 | jspm_packages 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional REPL history 46 | .node_repl_history 47 | 48 | -------------------------------------------------------------------------------- /src/controllers/ApiController.ts: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import {IPrincipal} from "../security/Principal"; 4 | import {HttpRequestMessage} from "../middleware/HttpRequestMessage"; 5 | import {HttpStatusCode} from "../http/HttpStatusCode"; 6 | 7 | export interface IApiController { 8 | user: IPrincipal; 9 | actionContext: any; 10 | controllerContext: any; 11 | requestContext: any; 12 | request: HttpRequestMessage; 13 | } 14 | 15 | export class ApiController implements IApiController { 16 | constructor() { 17 | this.user = null; 18 | } 19 | 20 | user: IPrincipal 21 | actionContext: any; 22 | controllerContext: any; 23 | requestContext: any; 24 | request: HttpRequestMessage; 25 | 26 | protected ok() { }; 27 | 28 | protected notFound() { 29 | return new Error(); 30 | }; 31 | 32 | protected statusCode(code: HttpStatusCode) { }; 33 | protected internalServerError() { }; 34 | } -------------------------------------------------------------------------------- /test/startup.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import {IStartup, IAppBuilder, HttpConfiguration} from '../server'; 4 | import {UsersController} from "./Controllers/UsersController"; 5 | 6 | const pgk = require("../package.json"); 7 | 8 | export class Startup implements IStartup { 9 | Configuration(app: IAppBuilder) { 10 | 11 | var config = new HttpConfiguration(); 12 | app.useWebApi(config); 13 | 14 | config.enableSwagger({ title: 'Directory API', description: pgk.description, version: pgk.version }); 15 | config.enableSwaggerUi({ title: 'API Documentation v' + pgk.version, path: '/docs' }); 16 | 17 | // This is different from ASP.NET WebAPI, controllers needs to manually be registered. 18 | app.controllers.add(UsersController); 19 | //app.controllers.add(GroupsController); 20 | 21 | app.useWelcomePage(); 22 | //app.useDirectoryBrowser('./public/', '/files/'); 23 | //app.useStaticFiles('static'); 24 | } 25 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Launch", 6 | "type": "node", 7 | "request": "launch", 8 | "program": "${workspaceRoot}/test/Server.js", 9 | "stopOnEntry": false, 10 | "args": [], 11 | "cwd": "${workspaceRoot}", 12 | "runtimeExecutable": null, 13 | "runtimeArgs": [ 14 | "--nolazy" 15 | ], 16 | "env": { 17 | "NODE_ENV": "development" 18 | }, 19 | "externalConsole": false, 20 | "sourceMaps": true, 21 | "outDir": null 22 | }, 23 | { 24 | "name": "Attach", 25 | "type": "node", 26 | "request": "attach", 27 | "port": 5858, 28 | "sourceMaps": false, 29 | "outDir": null, 30 | "localRoot": "${workspaceRoot}", 31 | "remoteRoot": null 32 | } 33 | ] 34 | } -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var exec = require('child_process').exec; 3 | var dtsGenerator = require('dts-generator'); 4 | var gulpSequence = require('gulp-sequence'); 5 | 6 | var appName = (function (p) { 7 | return p.name; 8 | })(require('./package.json')); 9 | 10 | gulp.task('build', gulpSequence('compile', 'definition')); 11 | 12 | gulp.task('compile', function (cb) { 13 | exec('tsc --version', function (err, stdout, stderr) { 14 | console.log('TypeScript ', stdout); 15 | if (stderr) { 16 | console.log(stderr); 17 | } 18 | }); 19 | 20 | return exec('tsc', function (err, stdout, stderr) { 21 | console.log(stdout); 22 | if (stderr) { 23 | console.log(stderr); 24 | } 25 | cb(err); 26 | }); 27 | }); 28 | 29 | gulp.task('definition', function(cb) { 30 | return dtsGenerator.default({ 31 | name: appName, 32 | project: '.', 33 | out: './lib/index.d.ts', 34 | exclude: ['node_modules/**/*.d.ts', 'typings/**/*.d.ts'] 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /test/controllers/UsersController.ts: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import {ApiController} from '../../controllers'; 4 | import {RoutePrefix, Route, HttpGet, HttpDelete, HttpPut, HttpPost} from '../../routing'; 5 | 6 | @RoutePrefix("users") 7 | export class UsersController extends ApiController { 8 | @Route("{id}") 9 | @HttpGet() // Also supports @HttpPut, @HttpPost, @HttpDelete 10 | getUserById(id: string) { 11 | 12 | return "getUserById:" + id; 13 | } 14 | 15 | @Route("search") 16 | @HttpPost() 17 | searchUsers(id: string) { 18 | return this.notFound(); 19 | } 20 | 21 | @Route("list") 22 | @HttpGet() 23 | list() { 24 | // Examples of request object values available: 25 | // Url object. 26 | console.log(this.request.requestUri); 27 | 28 | // HTTP version. 29 | console.log('HTTP Version: ' + this.request.version); 30 | 31 | // HTTP headers. 32 | console.log(this.request.headers); 33 | 34 | return "Hello World!"; 35 | } 36 | } -------------------------------------------------------------------------------- /test/Controllers/UsersController.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"UsersController.js","sourceRoot":"","sources":["UsersController.ts"],"names":["UsersController","UsersController.getUserById","UsersController.searchUsers","UsersController.list"],"mappings":"AACA,YAAY,CAAC;;;;;;;;;;AAEb,IAAO,MAAM,WAAW,iBAAiB,CAAC,CAAC;AAE3C,oCACqC,MAAM,CAAC,aAAa;IAGrDA,WAAWA,CAACA,EAAUA;QAElBC,MAAMA,CAACA,cAAcA,GAAGA,EAAEA,CAACA;IAC/BA,CAACA;IAIDD,WAAWA,CAACA,EAAUA;QAClBE,MAAMA,CAACA,IAAIA,CAACA,QAAQA,EAAEA,CAACA;IAC3BA,CAACA;IAIDF,IAAIA;QAGAG,OAAOA,CAACA,GAAGA,CAACA,IAAIA,CAACA,OAAOA,CAACA,UAAUA,CAACA,CAACA;QAGrCA,OAAOA,CAACA,GAAGA,CAACA,gBAAgBA,GAAGA,IAAIA,CAACA,OAAOA,CAACA,OAAOA,CAACA,CAACA;QAGrDA,OAAOA,CAACA,GAAGA,CAACA,IAAIA,CAACA,OAAOA,CAACA,OAAOA,CAACA,CAACA;QAElCA,MAAMA,CAACA,cAAcA,CAACA;IAC1BA,CAACA;AACLH,CAACA;AA5BG;IAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC;IACpB,MAAM,CAAC,OAAO,EAAE;;;;GACjB,wCAAW,QAGV;AAED;IAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC;IACtB,MAAM,CAAC,QAAQ,EAAE;;;;GAClB,wCAAW,QAEV;AAED;IAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC;IACpB,MAAM,CAAC,OAAO,EAAE;;;;GACjB,iCAAI,QAYH;AA7BL;IAAC,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC;;oBA8B3B;AA7BY,uBAAe,kBA6B3B,CAAA"} -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Sondre Bjellås 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/collections/Collection.ts: -------------------------------------------------------------------------------- 1 | //A typescript implementation of a generic Collection 2 | "use strict"; 3 | 4 | export class Collection { 5 | 6 | // The underlying array data structure of the collection 7 | private _items = []; 8 | 9 | // Get the collection as an array 10 | public getItems() { 11 | return this._items; 12 | } 13 | 14 | // Get a specific item from a collection given it's index 15 | public getItem(index: number): T { 16 | return this._items[index]; 17 | } 18 | 19 | // Length of the collection 20 | public count() { return this._items.length; } 21 | 22 | // Add an object to the collection 23 | public add(item: T) { 24 | this._items.push(item); 25 | } 26 | 27 | // Delete an object from the collection 28 | public delete(itemIndex: number) { 29 | this._items.splice(itemIndex, 1); 30 | } 31 | 32 | // Find the index of a given object in a collection 33 | public indexOfItem(obj: T, fromIndex?: number): number { 34 | if (fromIndex == null) { 35 | fromIndex = 0; 36 | } else if (fromIndex < 0) { 37 | fromIndex = Math.max(0, this._items.length + fromIndex); 38 | } 39 | for (var i = fromIndex, j = this._items.length; i < j; i++) { 40 | if (this._items[i] === obj) 41 | return i; 42 | } 43 | return -1; 44 | } 45 | } -------------------------------------------------------------------------------- /src/collections/Dictionary2.ts: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | export interface IDictionary2 { 4 | add(key: string, value: any): void; 5 | remove(key: string): void; 6 | containsKey(key: string): boolean; 7 | keys(): string[]; 8 | values(): any[]; 9 | } 10 | 11 | export class Dictionary2 implements IDictionary2 { 12 | 13 | private _keys: string[] = new Array(); 14 | private _values: any[] = new Array(); 15 | 16 | constructor(init: { key: string; value: any; }[]) { 17 | 18 | for (var x = 0; x < init.length; x++) { 19 | this[init[x].key] = init[x].value; 20 | this._keys.push(init[x].key); 21 | this._values.push(init[x].value); 22 | } 23 | } 24 | 25 | add(key: string, value: any) { 26 | this[key] = value; 27 | this._keys.push(key); 28 | this._values.push(value); 29 | } 30 | 31 | remove(key: string) { 32 | var index = this._keys.indexOf(key, 0); 33 | this._keys.splice(index, 1); 34 | this._values.splice(index, 1); 35 | 36 | delete this[key]; 37 | } 38 | 39 | keys(): string[] { 40 | return this._keys; 41 | } 42 | 43 | values(): any[] { 44 | return this._values; 45 | } 46 | 47 | containsKey(key: string) { 48 | if (typeof this[key] === "undefined") { 49 | return false; 50 | } 51 | 52 | return true; 53 | } 54 | 55 | toLookup(): IDictionary2 { 56 | return this; 57 | } 58 | } -------------------------------------------------------------------------------- /src/collections/Dictionary.ts: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | export interface IDictionary { 4 | add(key: TKey, value: TValue): void; 5 | remove(key: TKey): void; 6 | containsKey(key: TKey): boolean; 7 | keys(): TKey[]; 8 | values(): TValue[]; 9 | } 10 | 11 | export class Dictionary { 12 | 13 | private _keys: TKey[] = new Array(); 14 | private _values: TValue[] = new Array(); 15 | 16 | /* 17 | constructor(init: { key: string; value: any; }[]) { 18 | 19 | for (var x = 0; x < init.length; x++) { 20 | this[init[x].key] = init[x].value; 21 | this._keys.push(init[x].key); 22 | this._values.push(init[x].value); 23 | } 24 | }*/ 25 | 26 | add(key: TKey, value: TValue) { 27 | this[String(key)] = value; 28 | this._keys.push(key); 29 | this._values.push(value); 30 | } 31 | 32 | remove(key: TKey) { 33 | var index = this._keys.indexOf(key, 0); 34 | this._keys.splice(index, 1); 35 | this._values.splice(index, 1); 36 | 37 | delete this[String(key)]; 38 | } 39 | 40 | keys(): TKey[] { 41 | return this._keys; 42 | } 43 | 44 | values(): TValue[] { 45 | return this._values; 46 | } 47 | 48 | containsKey(key: TKey) { 49 | if (typeof this[String(key)] === "undefined") { 50 | return false; 51 | } 52 | 53 | return true; 54 | } 55 | 56 | /* 57 | containsKey(key: TKey) { 58 | return this._keys.indexOf(key, 0) > -1; 59 | }*/ 60 | 61 | /* 62 | toLookup(): IDictionary { 63 | return this; 64 | }*/ 65 | } -------------------------------------------------------------------------------- /src/middleware/RequestHandler.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import {HttpRequestMessage} from "./HttpRequestMessage" 4 | import {Promise} from "es6-promise"; 5 | 6 | export class RequestHandler { 7 | type: any; 8 | method: string; 9 | 10 | constructor(type: any, method: string) { 11 | 12 | console.log('CONSTRUCTOR OF REQUEST HANDLER'); 13 | console.log(type); 14 | console.log(method); 15 | 16 | this.type = type; 17 | this.method = method; 18 | } 19 | 20 | process(request, reply) { 21 | console.log('Processing Request...'); 22 | var controller = new this.type(); 23 | 24 | //console.log('Controller Instance:'); 25 | //console.log(controller); 26 | 27 | // Encapsulate the request in WebAPI-similar request message. 28 | controller.request = new HttpRequestMessage(request); 29 | 30 | var parameters = []; 31 | 32 | // TODO: Register the parameter NAMES for controller methods so we can send correct params. 33 | for (var k in request.params) { 34 | console.log('Parameter: ' + k + ':' + request.params[k]); 35 | parameters.push(request.params[k]); 36 | /*if (request.params.hasOwnProperty(k)) { 37 | user[k] = request.params[k]; 38 | }*/ 39 | } 40 | 41 | if(request.payload){ 42 | parameters.push(request.payload); 43 | } 44 | 45 | var response = controller[this.method](parameters); 46 | if(response instanceof Promise ){ 47 | response.then(reply).catch(function(error){ 48 | reply({error:error}); 49 | }); 50 | } 51 | reply(response); 52 | }; 53 | } 54 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hapi-webapi", 3 | "version": "0.0.5", 4 | "description": "Implements abstraction for Hapi in TypeScript that makes it similar to ASP.NET WebAPI to implement APIs on Hapi.", 5 | "main": "index.js", 6 | "scripts": { 7 | "preinstall": "", 8 | "postinstall": "", 9 | "prepublish": "", 10 | "test": "tsc --project test; node test/test.js", 11 | "setup": "npm install && typings install", 12 | "build": "tsc", 13 | "package": "npm pack" 14 | }, 15 | "author": { 16 | "name": "Sondre Bjellås", 17 | "email": "post-at-sondreb.com" 18 | }, 19 | "repository": { 20 | "type": "git", 21 | "url": "git://github.com/sondreb/hapi-webapi.git" 22 | }, 23 | "bugs": { 24 | "url": "https://github.com/sondreb/hapi-webapi/issues" 25 | }, 26 | "keywords": [ 27 | "hapi", 28 | "webapi", 29 | "microservice", 30 | "api" 31 | ], 32 | "license": "MIT", 33 | "homepage": "https://github.com/sondreb/hapi-webapi#readme", 34 | "dependencies": { 35 | "blipp": "2.x.x", 36 | "glob": "7.x.x", 37 | "glue": "3.x.x", 38 | "good": "7.x.x", 39 | "good-console": "6.x.x", 40 | "hapi": "13.x.x", 41 | "hapi-api-version": "1.x.x", 42 | "hapi-auth-jwt2": "6.x.x", 43 | "hapi-swaggered": "2.x.x", 44 | "hapi-swaggered-ui": "2.x.x", 45 | "inert": "4.x.x", 46 | "joi": "8.x.x", 47 | "merge": "1.x.x", 48 | "plugo": "0.3.x", 49 | "reflect-metadata": "0.1.x", 50 | "vision": "4.x.x", 51 | "es6-promise": "3.x.x" 52 | }, 53 | "devDependencies": { 54 | "dts-generator": "1.x.x", 55 | "code": "2.x.x", 56 | "eslint": "2.x.x", 57 | "gulp": "3.x.x", 58 | "lab": "10.x.x", 59 | "typescript": "1.x.x", 60 | "gulp-sequence": "0.4.x" 61 | }, 62 | "eslintConfig": { 63 | "env": { 64 | "node": true, 65 | "es6": true 66 | } 67 | } 68 | } -------------------------------------------------------------------------------- /src/server/AppBuilder.ts: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import {Collection} from "../collections/Collection"; 4 | import {HttpConfiguration} from "./HttpConfiguration"; 5 | 6 | export interface IAppBuilder { 7 | /* Consider moving these methods into separate "extensions" to this interface? */ 8 | //userCors(corsOptions: any); 9 | 10 | configuration: HttpConfiguration; 11 | 12 | useDirectoryBrowser(localPath: string, requestPath: string); 13 | useJwtBearerAuthentication(jwtBearerAuthenticationOptions: any); 14 | useOAuthBearerAuthentication(oAuthBearerAuthenticationOptions: any); 15 | useStaticFiles(requestPath: string); 16 | useWelcomePage(); 17 | useWebApi(config: HttpConfiguration); 18 | controllers: Collection; 19 | } 20 | 21 | export class AppBuilder implements IAppBuilder { 22 | 23 | private config: HttpConfiguration 24 | 25 | public get configuration(): HttpConfiguration { 26 | return this.config; 27 | } 28 | 29 | /** Not implemented. */ 30 | /*public useCors(corsOptions: any) { 31 | 32 | }*/ 33 | 34 | /** Not implemented. */ 35 | public useDirectoryBrowser(localPath: string, requestPath: string) { 36 | if (this.config == undefined) 37 | { 38 | throw Error('Method must be called after useWebApi.'); 39 | } 40 | 41 | this.config.properties.add('directory:browser', true); 42 | this.config.properties.add('directory:path', localPath); 43 | this.config.properties.add('directory:route', requestPath); 44 | } 45 | 46 | /** Not implemented. */ 47 | public useJwtBearerAuthentication(jwtBearerAuthenticationOptions: any) { 48 | 49 | } 50 | 51 | /** Not implemented. */ 52 | public useOAuthBearerAuthentication(oAuthBearerAuthenticationOptions: any) { 53 | 54 | } 55 | 56 | /** Not implemented */ 57 | public useStaticFiles(requestPath: string) { 58 | if (this.config == undefined) 59 | { 60 | throw Error('Method must be called after useWebApi.'); 61 | } 62 | } 63 | 64 | public useWelcomePage() { 65 | 66 | } 67 | 68 | public controllers: Collection = new Collection(); 69 | 70 | public useWebApi(config: HttpConfiguration) { 71 | this.config = config; 72 | console.log('CONFIG!!!'); 73 | console.log(this.config); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/server/HttpConfiguration.ts: -------------------------------------------------------------------------------- 1 | "use strict" 2 | 3 | const merge = require("merge"); 4 | 5 | import {IDictionary, Dictionary} from "../collections/Dictionary"; 6 | import {Collection} from "../collections/Collection"; 7 | 8 | import {IFilter} from "../middleware/Filter"; 9 | import {IHttpRoute} from "../routing/HttpRoute"; 10 | import {WebApp} from "./WebApp"; 11 | 12 | /** Represents a configuration of the API */ 13 | export class HttpConfiguration { 14 | 15 | private swaggerOptions: any; 16 | private swaggerUiOptions: any; 17 | 18 | public app: WebApp; 19 | 20 | /** Properties populated during configuration that is used during api startup. */ 21 | public properties: IDictionary = new Dictionary(); 22 | 23 | /** Collection of filters applied to all incoming requests. */ 24 | public filters: Collection; 25 | 26 | /** Ordered list of message handlers which will be invoked upon requests. */ 27 | //public messageHandlers: Array; 28 | public messageHandlers: Collection; 29 | 30 | public services: Collection; 31 | 32 | /** Collection of routes discovered at runtime. */ 33 | public routes: Collection; 34 | 35 | constructor() { 36 | this.swaggerOptions = { 37 | title: 'API', 38 | description: '', 39 | version: '0.0.1' 40 | }; 41 | 42 | this.swaggerUiOptions = { 43 | path: '/swagger' 44 | }; 45 | } 46 | 47 | /** Enables swagger API definition to be downloaded */ 48 | public enableSwagger(options: any) { 49 | this.properties.add("swagger:enabled", true); 50 | this.swaggerOptions = merge(this.swaggerOptions, options); 51 | this.properties.add("swagger:options", this.swaggerOptions); 52 | 53 | //this.swaggerEnabled = true; 54 | //console.log('Default Swagger Options: ', this.swaggerOptions); 55 | //console.log('Configured Swagger Options: ', this.swaggerOptions); 56 | }; 57 | 58 | /** Enables swagger UI to be accessible and used for API testing and documentation. */ 59 | public enableSwaggerUi(options: any) { 60 | this.properties.add("swaggerui:enabled", true); 61 | this.swaggerUiOptions = merge(this.swaggerUiOptions, options); 62 | this.properties.add("swaggerui:options", this.swaggerUiOptions); 63 | }; 64 | 65 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hapi-webapi ![build status](https://projects.visualstudio.com/DefaultCollection/_apis/public/build/definitions/312b44a5-2760-43de-8938-d8319566aa52/43/badge) 2 | Implements an abstraction for [Hapi](https://github.com/hapijs/hapi) using TypeScript, that provides a 3 | similar pattern to the ASP.NET WebApi framework for implementing 4 | Apis on [Hapi](http://hapijs.com/). 5 | 6 | # Quickstart 7 | 8 | To get started quickly, you can clone the ['hapi-webapi-seed'](https://github.com/sondreb/hapi-webapi-seed) repository, 9 | which includes all files needed to get started with TypeScript and the hapi-webapi. 10 | 11 | # Getting started 12 | 13 | Start by creating a package.json: 14 | 15 | ```sh 16 | npm init 17 | ``` 18 | 19 | Install hapi-webapi and save it to your package.json dependencies: 20 | 21 | ```sh 22 | npm install hapi-webapi --save 23 | ``` 24 | 25 | Create a server.ts file with the following contents: 26 | 27 | ```js 28 | import {WebApp, StartOptions} from 'hapi-webapi/server'; 29 | import {Startup} from "./Startup"; 30 | 31 | var options = new StartOptions(); 32 | options.port = 4600; 33 | 34 | //To enable CORS set options.cors to true. Default value is false 35 | options.cors = true; 36 | 37 | WebApp.Start(Startup, options); 38 | ``` 39 | 40 | Create a startup.ts file with the following contents: 41 | 42 | ```js 43 | import {IStartup, IAppBuilder, HttpConfiguration} from 'hapi-webapi/server'; 44 | import {UsersController} from "./Controllers/UsersController"; 45 | 46 | const pgk = require("./package.json"); 47 | 48 | export class Startup implements IStartup { 49 | Configuration(app: IAppBuilder) { 50 | 51 | var config = new HttpConfiguration(); 52 | app.useWebApi(config); 53 | 54 | config.enableSwagger({ title: 'Directory API', description: pgk.description, version: pgk.version }); 55 | config.enableSwaggerUi({ title: 'API Documentation v' + pgk.version, path: '/docs' }); 56 | 57 | // This is different from ASP.NET WebAPI, controllers needs to manually be registered. 58 | app.controllers.add(UsersController); 59 | 60 | app.useWelcomePage(); 61 | //app.useDirectoryBrowser('./public/', '/files/'); 62 | //app.useStaticFiles('static'); 63 | } 64 | } 65 | ``` 66 | 67 | Create a controller.ts file with the following contents: 68 | 69 | ```js 70 | import {ApiController} from 'hapi-webapi/controllers'; 71 | import {RoutePrefix, Route, HttpGet, HttpDelete, HttpPut, HttpPost} from 'hapi-webapi/routing'; 72 | 73 | @RoutePrefix("users") 74 | export class UsersController extends ApiController { 75 | @Route("{id}") 76 | @HttpGet() // Also supports @HttpPut, @HttpPost, @HttpDelete 77 | getUserById(id: string) { 78 | 79 | return "getUserById:" + id; 80 | } 81 | 82 | @Route("search") 83 | @HttpPost() 84 | searchUsers(id: string) { 85 | return this.notFound(); 86 | } 87 | 88 | @Route("list") 89 | @HttpGet() 90 | list() { 91 | // Examples of request object values available: 92 | // Url object. 93 | console.log(this.request.requestUri); 94 | 95 | // HTTP version. 96 | console.log('HTTP Version: ' + this.request.version); 97 | 98 | // HTTP headers. 99 | console.log(this.request.headers); 100 | 101 | return "Hello World!"; 102 | } 103 | } 104 | 105 | ``` 106 | 107 | Launch the application by running: 108 | 109 | ```sh 110 | npm start 111 | ``` 112 | 113 | And open [localhost:4600](http://localhost:6500) or [localhost:4600/docs](http://localhost:4600/docs) for Swagger UI in your browser. 114 | 115 | ## Contributors 116 | 117 | [ sondreb](https://github.com/sondreb) | [kommundsen](https://github.com/kommundsen) | [SteffenVetrhus](https://github.com/SteffenVetrhus) | 118 | :---: |:---: |:---: | 119 | [sondreb](https://github.com/sondreb) |[kommundsen](https://github.com/kommundsen) |[SteffenVetrhus](https://github.com/SteffenVetrhus) | 120 | 121 | ## Change Log 122 | 123 | View the [change log](CHANGELOG.md) to keep up-to-date on API and project changes. 124 | 125 | ## License 126 | 127 | MIT © [Sondre Bjellås](http://sondreb.com) 128 | -------------------------------------------------------------------------------- /src/server/WebApp.ts: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import * as Hapi from 'hapi'; 4 | import {Collection} from "../collections/Collection"; 5 | import {RequestHandler} from "../middleware/RequestHandler"; 6 | import {IAppBuilder, AppBuilder} from "./AppBuilder"; 7 | import {StartOptions} from "./StartOptions"; 8 | import {IStartup} from "./Startup"; 9 | 10 | export class WebApp { 11 | 12 | private static activeModules(app: IAppBuilder): Collection { 13 | 14 | var modules = new Collection(); 15 | 16 | modules.add({ type: require('good'), options: { 17 | ops: { interval: 1000} 18 | } }); 19 | 20 | // Disable for now, returns runtime error. 21 | //modules.add({ type: require('good-console') }); 22 | modules.add({ type: require('blipp'), options: {} }); 23 | 24 | if (app.configuration.properties["swagger:enabled"] === true) 25 | { 26 | let options = app.configuration.properties["swagger:options"]; 27 | 28 | console.log('Swagger is enabled: ' + options); 29 | 30 | modules.add({ type: require('hapi-swaggered'), options: { 31 | tags: options.tags, 32 | info: { 33 | title: options.title, 34 | description: options.description, 35 | version: options.version 36 | } 37 | }}); 38 | } 39 | 40 | if (app.configuration.properties["swaggerui:enabled"] === true) 41 | { 42 | console.log('Swagger UI is enabled.'); 43 | 44 | // Register inert and vision which swagger UI is dependent upon. 45 | modules.add({type: require('inert')}); 46 | modules.add({type: require('vision')}); 47 | 48 | let options = app.configuration.properties["swaggerui:options"]; 49 | 50 | modules.add({ type: require('hapi-swaggered-ui'), options: { 51 | title: options.title, 52 | path: options.path, 53 | authorization: { 54 | field: 'apiKey', 55 | scope: 'query', // header works as well 56 | valuePrefix: 'bearer ',// prefix incase 57 | //defaultValue: 'token', 58 | placeholder: 'Enter your apiKey here' 59 | }, 60 | swaggerOptions: { 61 | validatorUrl: null 62 | } 63 | }}); 64 | } 65 | 66 | return modules; 67 | } 68 | 69 | static getMethods(obj) { 70 | var res = []; 71 | for (var m in obj) { 72 | console.log('LOOOOP!!'); 73 | console.log(m); 74 | 75 | if (typeof obj[m] == "function") { 76 | res.push(m) 77 | } 78 | } 79 | return res; 80 | } 81 | 82 | static Start(startup: { new (): T; }, options: StartOptions) { 83 | //static Start(options: StartOptions) { 84 | 85 | //var t = new T(); 86 | var start = new startup(); 87 | console.log('START CONFIGURATION: ' + start); 88 | 89 | var appBuilder = new AppBuilder(); 90 | start.Configuration(appBuilder); 91 | 92 | const server = new Hapi.Server(); 93 | server.connection( 94 | { 95 | port: options.port, 96 | routes: { cors: options.cors} 97 | } 98 | ); 99 | 100 | console.log('Count of controllers: ' + appBuilder.controllers.count()); 101 | 102 | appBuilder.controllers.getItems().forEach(function(type) { 103 | 104 | console.log('Controller Type: ' + type); 105 | var controllerInstance = new type(); 106 | console.log('Controller Instance: ' + controllerInstance); 107 | 108 | var routePrefix = Reflect.getMetadata("RoutePrefix", controllerInstance.constructor); 109 | console.log('routePrefix: ' + routePrefix); 110 | 111 | var methods = WebApp.getMethods(controllerInstance.constructor); 112 | 113 | var methodNames = Object.getOwnPropertyNames(type.prototype).filter(function(p) { 114 | if (p == 'constructor') { 115 | return false; 116 | } 117 | 118 | return typeof type.prototype[p] === 'function'; 119 | }) 120 | 121 | console.log('methodNames:'); 122 | console.log(methodNames); 123 | 124 | methodNames.forEach(function(name) { 125 | 126 | //console.log(controllerInstance[name]); 127 | //console.log(Object.getOwnPropertyDescriptor(type, name)); 128 | //console.log(Object.getOwnPropertyDescriptor(type.prototype, name)); 129 | //console.log(Object.getOwnPropertyDescriptor(controllerInstance, name)); 130 | 131 | var methodDescriptor = Object.getOwnPropertyDescriptor(type.prototype, name); 132 | 133 | var metaMethod = Reflect.getMetadata('Method', methodDescriptor.value); 134 | var metaRoute = Reflect.getMetadata('Route', methodDescriptor.value); 135 | 136 | console.log('Method: ' + metaMethod); 137 | console.log('Route: ' + metaRoute); 138 | 139 | //var metaMethod = Reflect.getMetadata("Method", methodDescriptor.value); 140 | //var metaRoute = Reflect.getMetadata("Route", methodDescriptor.value); 141 | //console.log('Method:' + metaMethod); 142 | //console.log('Route:' + metaRoute); 143 | 144 | // TODO: Do a safe route building here, supporting prefix with and without trailing /. 145 | var safeRoute = '/' + routePrefix + '/' + metaRoute; 146 | 147 | console.log('Safe Route: ' + safeRoute); 148 | 149 | var handler = new RequestHandler(type, name); 150 | 151 | server.route({ 152 | method: metaMethod, 153 | path: safeRoute, 154 | config: { 155 | tags: ['api'], 156 | description: '[Should be read from decoration on API method]' 157 | }, 158 | handler: function(request, reply) { 159 | // IMPORTANT: We can't bind the process method directly to the handler property, that will invalidate .this within the handler. 160 | handler.process(request, reply); 161 | } 162 | }); 163 | }); 164 | }); 165 | 166 | var modules = WebApp.activeModules(appBuilder); 167 | 168 | modules.getItems().forEach(function(module) { 169 | 170 | let moduleRegistration : any = { 171 | register: module.type, 172 | }; 173 | 174 | if (module.options) 175 | { 176 | console.log('ADDING OPTIONS'); 177 | console.log(module.options); 178 | moduleRegistration.options = module.options; 179 | } 180 | 181 | server.register(moduleRegistration, (err) => { 182 | if (err) { 183 | console.error('Failed to load plugin:', err); 184 | } 185 | }); 186 | }); 187 | 188 | if (appBuilder.configuration.properties["directory:browser"] === true) { 189 | 190 | let browserPath = appBuilder.configuration.properties["directory:path"]; 191 | let browserRoute = appBuilder.configuration.properties["directory:route"]; 192 | 193 | server.route({ 194 | method: 'GET', 195 | path: browserRoute, 196 | handler: { 197 | directory: { 198 | path: browserPath, 199 | listing: true 200 | } 201 | } 202 | }); 203 | } 204 | 205 | server.start((err) => { 206 | 207 | if (err) { 208 | throw err; 209 | } 210 | 211 | console.log('✅ Server is listening on ' + server.info.uri.toLowerCase()); 212 | }); 213 | } 214 | } 215 | --------------------------------------------------------------------------------