diff --git a/.gitignore b/.gitignore index 6d43ade..a5a7160 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,8 @@ release/ yarn-error.log .nyc_output/ swagger.json +src/**/*.map +src/**/*.js + +*.map +tsconfig.temp.json diff --git a/.vscode/launch.json b/.vscode/launch.json index 88a1d92..a638110 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -7,7 +7,7 @@ { "type": "node", "request": "launch", - "name": "Mocha Tests", + "name": "Tests samples", "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha", "args": [ "-u", @@ -15,7 +15,7 @@ "--timeout", "999999", "--colors", - "${workspaceFolder}/dist/tests" + "${workspaceFolder}/dist/samples/tests" ], "preLaunchTask": "compile", "internalConsoleOptions": "openOnSessionStart" @@ -24,7 +24,7 @@ "args": [], "cwd": "${workspaceRoot}", "externalConsole": false, - "name": "DEBUG", + "name": "DEBUG samples", "outDir": "${workspaceRoot}/dist", "program": "${workspaceRoot}/dist/samples/server.js", "request": "launch", @@ -36,6 +36,6 @@ "sourceMaps": true, "stopOnEntry": false, "type": "node" - }, + } ] } diff --git a/README.md b/README.md index f76e440..885f254 100644 --- a/README.md +++ b/README.md @@ -31,26 +31,52 @@ Framework to help building a REST API using typescript and node. ``` ## Sample -1. Create your first controller class `TestController.ts` +1. Create your first controller class `user-controller.ts` ```javascript - import { Get, Post, JsonController, Param, Body } from 'kiwi-server'; + import { Get, Post, Put, JsonController, Param, Body, QueryParam, Authorize, HeaderParam, Delete } from '../../../src/index'; + import { UserModel } from '../../models/models'; + import { isNil } from 'lodash'; + import { Utils } from '../../utils'; + + @JsonController('/user') + export class UserController { + constructor() {} + + @Authorize(['Admin']) + @Post('/create') + public create(@Body() user: UserModel) { + user.id = Utils.userList.length + 1; + Utils.userList.push(user); + return user; + } - @JsonController('/testcontroller') - export class TestController { + @Authorize(['Admin']) + @Get('/get/:id') + public getById(@Param('id') id: number) { + var user = Utils.userList.filter(function(obj) { + return obj.id === id; + }); - @Get('/getAction/:id/:id2/:id3') - public get(@Param('id') id: string, @Param('id2') id2: string, @Param('id3') id3: string) { - return { method: "get test" }; + return user; } - @Post('/postAction/:id') - public post( @Body() request: any, @Param('id') id: string) { - return { - method: "post test", - request: request - }; + @Authorize(['Admin']) + @Put('/update') + public update(@Body() user: UserModel) { + let userAux = Utils.userList.find(x => x.id == user.id); + let index = Utils.userList.indexOf(userAux); + Utils.userList[index] = user; + return true; + } + + @Authorize(['Admin']) + @Delete('/delete/:id') + public delete(@Param('id') id: number) { + Utils.userList = Utils.userList.filter(function(obj) { + return obj.id !== id; + }); + return true; } - } ``` @@ -59,16 +85,21 @@ Framework to help building a REST API using typescript and node. For example if you send something like http://.../testcontroller/queryparam/1?name=guille&lastname=fernandez you will receive an object like below: ```javascript - @Get('/queryparam/:id') - public queryparam(@QueryParam() object: any, @Param('id') id: string) { - return object; - } + @Get('/listFilter') + public listFilter(@QueryParam() params: any) { + if (!isNil(params)) { + var users = Utils.userList.filter(function(obj) { + return obj.name === params.name && obj.age === +params.age; + }); + } + return users; + } ``` ```javascript { "name": "guille", - "lastname": "fernandez" + "age": 33 } ``` @@ -76,19 +107,25 @@ Framework to help building a REST API using typescript and node. In the next example we are going to receive the token HTTP header if it exists. ```javascript - @Get('/queryparam/:id') - public queryparam(@Param('id') id: string, @HeaderParam('token') token: string) { - return object; + @Get('/search/:name') + public queryparam(@Param('name') name: string, @HeaderParam('token') token: string) { + this.aux.print(token); + if (!isNil(name)) { + var users = Utils.userList.filter(function(obj) { + return obj.name === name; + }); + } + return users; } ``` 2. After creating the controller, create the server that uses that controller. ```javascript import { createKiwiServer } from 'kiwi-server'; - import { TestController } from './test-controller'; + import { UserController } from './controllers/user/user-controller'; const options = { - controllers: [TestController], + controllers: [UserController], }; const server = createKiwiServer(options); server.listen(8086); @@ -96,22 +133,23 @@ Framework to help building a REST API using typescript and node. ## Middlewares 1. You can create middlewares to execute activities before and after the execution of an action. - For example to enable CORS we use a specific middleware that is in charge of adding the HTTP headers for that. - It's important to execute `next` if you want the flow to continue executing. Otherwise the flow finishes and you must do something with the response, if you don't the client never gets a response. - Below is an example that executes before any action. + For example to enable CORS we use a specific middleware that is in charge of adding the HTTP headers for that. + It's important to execute `next` if you want the flow to continue executing. Otherwise the flow finishes and you must do something with the response, if you don't the client never gets a response. + Below is an example that executes before any action. - Also you can add the order that you want to execute your middlewares: - - ``` - import { IMiddleware } from 'kiwi-server'; - import { MiddlewareBefore } from 'kiwi-server'; + Also you can add the order that you want to execute your middlewares: + + ```javascript + import { IMiddleware } from '../../src/middlewares/middleware'; + import { MiddlewareAfter } from '../../src/decorators/middlewareAfter'; import * as http from 'http'; - @MiddlewareBefore(7) - export class TestMiddleware implements IMiddleware { - execute(request: http.IncomingMessage, response: http.ServerResponse, next: any) { - response.setHeader( 'Authorization', 'hola' ); - console.log('TestMiddleware execute'); + @MiddlewareAfter(1) + export class UserMiddleware implements IMiddleware{ + + execute(request: http.IncomingMessage, response: http.ServerResponse, next: any){ + response.setHeader( 'Authorization', 'token' ); + console.log('UserMiddleware execute'); next(); } } @@ -119,35 +157,30 @@ Framework to help building a REST API using typescript and node. ## Authorization 1. On the controller specify what actions need to be authorized, using the `@Authorize` decorator. - In the following example we only need to authorize the `post` action. You can also put the decorator in the controller if all the actions need to be authorized. + In the following example we only need to authorize the `put` action. You can also put the decorator in the controller if all the actions need to be authorized. ```javascript - import { Get, Post, JsonController, Authorize } from 'kiwi-server'; - - @JsonController('/testcontroller2') - export class TestController2 { - @Get('/getAction') - public get(){ - return "get test2"; - } - - @Authorize(['ADMIN', 'USER']) - @Post('/postAction') - public post(@Body() request: any) { - return { - method: "post test2", - request: request - }; - } + @Get('/list') + public listAll() { + return Utils.userList; } + + @Authorize(['Admin']) + @Put('/update') + public update(@Body() user: UserModel) { + let userAux = Utils.userList.find(x => x.id == user.id); + let index = Utils.userList.indexOf(userAux); + Utils.userList[index] = user; + return true; + } + ``` 2. On the server define the function that is going to be executed everytime an action or a controller has the `@Authorize` decorator. If that function returns `false` the service is going to return 401 HTTP error, in other case it will continue the normal execution path. ```javascript import { createKiwiServer } from 'kiwi-server'; - import { TestController } from './test-controller'; - import { TestController2 } from './test-controller2'; + import { UserController } from './controllers/user/user-controller'; async function validateAuthentication(request: http.IncomingMessage, roles: Array): Promise { console.log(roles); @@ -156,7 +189,7 @@ Framework to help building a REST API using typescript and node. } const options = { - controllers: [TestController, TestController2], + controllers: [UserController], authorization: validateAuthentication } const server = createKiwiServer(options); @@ -168,11 +201,10 @@ Framework to help building a REST API using typescript and node. ```javascript import { createKiwiServer } from 'kiwi-server'; - import { TestController } from './test-controller'; - import { TestController2 } from './test-controller2'; + import { UserController } from './controllers/user/user-controller'; const options = { - controllers: [TestController, TestController2], + controllers: [UserController], cors: { enabled: true, domains: ['domain1.com', 'domain2.com'] @@ -187,11 +219,10 @@ Framework to help building a REST API using typescript and node. ```javascript import { createKiwiServer } from 'kiwi-server'; - import { TestController } from './test-controller'; - import { TestController2 } from './test-controller2'; + import { UserController } from './controllers/user/user-controller'; const options = { - controllers: [TestController, TestController2], + controllers: [UserController], prefix: 'v1/' } const server = createKiwiServer(options); @@ -204,32 +235,33 @@ Framework to help building a REST API using typescript and node. Then you can use that in any method that you want. ```javascript - import { Get, Post, JsonController, Param, Body, QueryParam, Authorize } from 'kiwi-server'; - import { Utils } from './utils'; - - @Authorize(['role1, role2']) - @JsonController('/testcontroller') - export class TestController { - - constructor(private utils: Utils) {} - - @Post('/meetupjs') - public test23(@Body() body: any) { - return body; - } - - @Get('/queryparam/:id') - public queryparam(@QueryParam() object: any, @Param('id') id: string) { - this.utils.print(); - return object; + import { Get, Post, Put, JsonController, Param, Body, QueryParam, Authorize, HeaderParam, Delete } from '../../../src/index'; + import { UserModel } from '../../models/models'; + import { isNil } from 'lodash'; + import { AuxiliaryFunctions } from '../../auxiliaryFunctions'; + + @JsonController('/user') + export class UserController { + constructor(private aux: AuxiliaryFunctions) {} + + @Get('/search/:name') + public queryparam(@Param('name') name: string, @HeaderParam('token') token: string) { + this.aux.print(token); + if (!isNil(name)) { + var users = Utils.userList.filter(function(obj) { + return obj.name === name; + }); + } + return users; } + } ``` ## Sockets 1. socket.io is integrated to our framework. Enable socket support by adding the `socket` property to the options. ```javascript const options = { - controllers: [TestController, TestController2], + controllers: [UserController], documentation: { enabled: true, path: '/apidoc' @@ -253,11 +285,10 @@ Then you can use that in any method that you want. ```javascript import { createKiwiServer } from 'kiwi-server'; - import { TestController } from './test-controller'; - import { TestController2 } from './test-controller2'; + import { UserController } from '../controllers/user/user-controller'; const options = { - controllers: [TestController, TestController2], + controllers: [UserController], prefix: 'v1/', cors: true, documentation: { @@ -270,17 +301,19 @@ Then you can use that in any method that you want. ``` 2. Decorate your models ```javascript - import { IsArray, IsInt, IsDate, IsOptional } from 'kiwi-server'; + import { IsString, IsNumber, IsArray } from '../../src/index' - export class TimesheetEntry { - @IsInt() projectId: number; - @IsDate() date: Date; - @IsOptional() @IsInt() hours?: number; + export class AddressModel { + @IsString() public street: string; + @IsNumber() public number: number; } - export class TimesheetEntries { - @IsArray(() => TimesheetEntry) - entries: TimesheetEntry[]; + export class UserModel { + @IsNumber() public id: number; + @IsString() public name: string; + @IsString() public lastname: string; + @IsNumber() public age: number; + @IsArray(() => AddressModel) public address: AddressModel[]; } ``` 3. Visit the documentation page, in this example it would be at http://localhost:8086/v1/apidoc diff --git a/gulpfile.js b/gulpfile.js index 8bb3420..e7508ae 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -34,7 +34,7 @@ gulp.task('copy', () => { }); gulp.task('execute-tests', function () { - return gulp.src(['dist/tests/*.test.js'], { read: false }) + return gulp.src(['dist/samples/tests/*.test.js'], { read: false }) .pipe(mocha({ reporter: 'spec' })); diff --git a/package.json b/package.json index e4b08da..2d84442 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "scripts": { "compile": "tsc", "test-load": "node ./tests/load.test.js", - "test": "tsc && nyc mocha ./dist/tests && nyc report --reporter=text-lcov | coveralls" + "test": "tsc && nyc mocha ./dist/samples/tests && nyc report --reporter=text-lcov | coveralls" }, "repository": { "type": "git", @@ -36,7 +36,7 @@ "class-validator": "^0.8.5", "class-validator-jsonschema": "^1.2.0", "fs": "0.0.1-security", - "lodash": "^4.17.10", + "lodash": "^4.17.15", "mocha": "^5.2.0", "reflect-metadata": "^0.1.12", "socket.io": "^2.1.1", @@ -44,10 +44,10 @@ }, "devDependencies": { "@types/chai": "^4.1.7", - "@types/node": "^10.3.4", + "@types/node": "^13.1.6", "chai": "^4.2.0", "coveralls": "^3.0.4", - "gulp": "^3.9.1", + "gulp": "^4.0.2", "gulp-exec": "^3.0.2", "gulp-mocha": "^6.0.0", "gulp-sequence": "^1.0.0", @@ -56,7 +56,7 @@ "loadtest": "^3.0.3", "mocha-typescript": "^1.1.17", "node-mocks-http": "^1.7.3", - "nyc": "^13.1.0", + "nyc": "^15.0.0", "sinon": "^7.1.1", "source-map-support": "^0.5.9", "typescript": "^2.9.2" diff --git a/samples/auxiliaryFunctions.ts b/samples/auxiliaryFunctions.ts new file mode 100644 index 0000000..14e29f3 --- /dev/null +++ b/samples/auxiliaryFunctions.ts @@ -0,0 +1,5 @@ +export class AuxiliaryFunctions { + public print(text: string) { + console.log(text); + } +} diff --git a/samples/test-controller.ts b/samples/controllers/test-controller.ts similarity index 80% rename from samples/test-controller.ts rename to samples/controllers/test-controller.ts index dd6a8a7..4f6cb9b 100644 --- a/samples/test-controller.ts +++ b/samples/controllers/test-controller.ts @@ -1,7 +1,6 @@ -import { Get, Post, JsonController, Param, Body, QueryParam, Authorize, HeaderParam } from '../src/index'; -import { Utils } from './utils'; -import { setInterval } from 'timers'; -import { User } from './models/models'; +import { Get, Post, JsonController, Param, Body, QueryParam, Authorize, HeaderParam } from '../../src/index'; +import { Utils } from '../utils'; +import { UserModel } from '../models/models'; @Authorize(['role1, role2']) @JsonController('/testcontroller') @@ -15,14 +14,13 @@ export class TestController { } @Post('/user') - public nestedArray(@Body() user: User) { + public nestedArray(@Body() user: UserModel) { return ""; } @Get('/queryparam/:id') - public queryparam(@QueryParam() object: User, @Param('id') id: string, @HeaderParam('token1') token1: string, + public queryparam(@QueryParam() object: UserModel, @Param('id') id: string, @HeaderParam('token1') token1: string, @HeaderParam('token2') token2: string) { - this.utils.print(); return object; } diff --git a/samples/test-controller2.ts b/samples/controllers/test-controller2.ts similarity index 95% rename from samples/test-controller2.ts rename to samples/controllers/test-controller2.ts index 1738754..6d71493 100644 --- a/samples/test-controller2.ts +++ b/samples/controllers/test-controller2.ts @@ -1,4 +1,4 @@ -import { Get, Post, JsonController, Authorize, Body, Param } from '../src/index'; +import { Get, Post, JsonController, Authorize, Body, Param } from '../../src/index'; @JsonController('/testcontroller2') diff --git a/samples/controllers/test-controller3.ts b/samples/controllers/test-controller3.ts new file mode 100644 index 0000000..55f30f1 --- /dev/null +++ b/samples/controllers/test-controller3.ts @@ -0,0 +1,15 @@ +import { Get, Post, JsonController, Authorize, Body, Param, QueryParam } from '../../src/index'; +import { UserModel } from '../models/models'; +import { Utils } from '../utils'; + +@JsonController('/testcontroller3') +export class TestController3 { + constructor(private utils: Utils){ + } + + @Post('/login/:id') + public post(@Body() body: UserModel, @Param('id') id : number){ + return body; + } + +} diff --git a/samples/controllers/user/user-controller.ts b/samples/controllers/user/user-controller.ts new file mode 100644 index 0000000..513ccb7 --- /dev/null +++ b/samples/controllers/user/user-controller.ts @@ -0,0 +1,72 @@ +import { Get, Post, Put, JsonController, Param, Body, QueryParam, Authorize, HeaderParam, Delete } from '../../../src/index'; +import { UserModel } from '../../models/models'; +import { isNil } from 'lodash'; +import { Utils } from '../../utils'; +import { AuxiliaryFunctions } from '../../auxiliaryFunctions'; + +@JsonController('/user') +export class UserController { + constructor(private aux: AuxiliaryFunctions) {} + + @Authorize(['Admin']) + @Post('/create') + public create(@Body() user: UserModel) { + user.id = Utils.userList.length + 1; + Utils.userList.push(user); + return user; + } + + @Authorize(['Admin']) + @Get('/get/:id') + public getById(@Param('id') id: number) { + var user = Utils.userList.filter(function(obj) { + return obj.id === id; + }); + + return user; + } + + @Authorize(['Admin']) + @Put('/update') + public update(@Body() user: UserModel) { + let userAux = Utils.userList.find(x => x.id == user.id); + let index = Utils.userList.indexOf(userAux); + Utils.userList[index] = user; + return true; + } + + @Get('/list') + public listAll() { + return Utils.userList; + } + + @Get('/listFilter') + public listFilter(@QueryParam() params: any) { + if (!isNil(params)) { + var users = Utils.userList.filter(function(obj) { + return obj.name === params.name && obj.age === +params.age; + }); + } + return users; + } + + @Authorize(['Admin']) + @Delete('/delete/:id') + public delete(@Param('id') id: number) { + Utils.userList = Utils.userList.filter(function(obj) { + return obj.id !== id; + }); + return true; + } + + @Get('/search/:name') + public queryparam(@Param('name') name: string, @HeaderParam('token') token: string) { + this.aux.print(token); + if (!isNil(name)) { + var users = Utils.userList.filter(function(obj) { + return obj.name === name; + }); + } + return users; + } +} diff --git a/samples/test-middlware.ts b/samples/middlewares/test-middlware.ts similarity index 66% rename from samples/test-middlware.ts rename to samples/middlewares/test-middlware.ts index ced4338..f21c5b5 100644 --- a/samples/test-middlware.ts +++ b/samples/middlewares/test-middlware.ts @@ -1,5 +1,5 @@ -import { IMiddleware } from '../src/middlewares/middleware'; -import { MiddlewareBefore } from '../src/decorators/middlewareBefore'; +import { IMiddleware } from '../../src/middlewares/middleware'; +import { MiddlewareBefore } from '../../src/decorators/middlewareBefore'; import * as http from 'http'; @MiddlewareBefore(3) diff --git a/samples/test-middlware2.ts b/samples/middlewares/test-middlware2.ts similarity index 55% rename from samples/test-middlware2.ts rename to samples/middlewares/test-middlware2.ts index c306335..9151cd1 100644 --- a/samples/test-middlware2.ts +++ b/samples/middlewares/test-middlware2.ts @@ -1,14 +1,13 @@ -import { IMiddleware } from '../src/middlewares/middleware'; -import { MiddlewareAfter } from '../src/decorators/middlewareAfter'; +import { IMiddleware } from '../../src/middlewares/middleware'; +import { MiddlewareAfter } from '../../src/decorators/middlewareAfter'; import * as http from 'http'; -import { Utils } from './utils'; -@MiddlewareAfter(1) +@MiddlewareAfter(2) export class TestMiddleware2 implements IMiddleware{ - + execute(request: http.IncomingMessage, response: http.ServerResponse, next: any){ response.setHeader( 'Authorization2', 'hola2' ); - + next(); } -} \ No newline at end of file +} diff --git a/samples/middlewares/user-middleware.ts b/samples/middlewares/user-middleware.ts new file mode 100644 index 0000000..8fe6921 --- /dev/null +++ b/samples/middlewares/user-middleware.ts @@ -0,0 +1,13 @@ +import { IMiddleware } from '../../src/middlewares/middleware'; +import { MiddlewareAfter } from '../../src/decorators/middlewareAfter'; +import * as http from 'http'; + +@MiddlewareAfter(1) +export class UserMiddleware implements IMiddleware{ + + execute(request: http.IncomingMessage, response: http.ServerResponse, next: any){ + response.setHeader( 'Authorization', 'token' ); + console.log('UserMiddleware execute'); + next(); + } +} diff --git a/samples/models/models.ts b/samples/models/models.ts index 0b5b335..cf47383 100644 --- a/samples/models/models.ts +++ b/samples/models/models.ts @@ -5,7 +5,8 @@ export class AddressModel { @IsNumber() public number: number; } -export class User { +export class UserModel { + @IsNumber() public id: number; @IsString() public name: string; @IsString() public lastname: string; @IsNumber() public age: number; diff --git a/samples/server.ts b/samples/server.ts index a066625..20005e8 100644 --- a/samples/server.ts +++ b/samples/server.ts @@ -1,11 +1,12 @@ import { createKiwiServer, IKiwiOptions, AuthorizeResponse } from '../src/index'; -import { TestController3 } from './test-controller3'; -import { TestMiddleware2 } from './test-middlware2'; -import { TestMiddleware } from './test-middlware'; import * as http from 'http'; import { environment } from './environments/environment'; -import { TestController2 } from './test-controller2'; -import { TestController } from './test-controller'; +import { UserController } from './controllers/user/user-controller'; +import { TestController } from './controllers/test-controller'; +import { TestController2 } from './controllers/test-controller2'; +import { TestController3 } from './controllers/test-controller3'; +import { TestMiddleware } from './middlewares/test-middlware'; +import { TestMiddleware2 } from './middlewares/test-middlware2'; async function validateAuthentication(request: http.IncomingMessage, roles: Array): Promise { console.log(roles); @@ -13,7 +14,7 @@ async function validateAuthentication(request: http.IncomingMessage, roles: Arra } const options: IKiwiOptions = { - controllers: [TestController, TestController2, TestController3], + controllers: [UserController, TestController, TestController2, TestController3], authorization: validateAuthentication, middlewares: [TestMiddleware2, TestMiddleware], cors: { diff --git a/samples/test-controller3.ts b/samples/test-controller3.ts deleted file mode 100644 index c1d5027..0000000 --- a/samples/test-controller3.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Get, Post, JsonController, Authorize, Body, Param, QueryParam } from '../src/index'; -import { User } from './models/models'; -import { Utils } from './utils'; - -@JsonController('/user') -export class TestController3 { - constructor(private utils: Utils){ - } - - @Post('/login/:id') - public post(@Body() body: User, @Param('id') id : number){ - return body; - } - -} diff --git a/samples/tests/authentication.test.ts b/samples/tests/authentication.test.ts new file mode 100644 index 0000000..87eb651 --- /dev/null +++ b/samples/tests/authentication.test.ts @@ -0,0 +1,46 @@ +import { suite, test } from "mocha-typescript"; +import { assert } from 'chai'; +import { IKiwiOptions, createKiwiServer, processRequest } from '../../src/index'; +import { UserController } from '../controllers/user/user-controller'; +import { KiwiMetadataStorage } from '../../src/metadata/metadataStorage'; +import * as http from 'http'; +var sinon = require('sinon'); +var httpMocks = require('node-mocks-http'); + +var callback = sinon.spy(); + +var options: IKiwiOptions = { + controllers: [UserController], + authorization: function(request: http.IncomingMessage, roles: Array){ + callback(); + return true; + }, + middlewares: [], + cors: { + enabled: true + }, + documentation: { + enabled: true, + path: '/apidoc' + }, + log: true, + port: 8086, + prefix: '/v1' +} + +@suite class AuthenticationSuite { + static before() { + KiwiMetadataStorage.init(options); + } + + before() { + } + + static after() { + + } + + after() { + + } +} \ No newline at end of file diff --git a/samples/tests/controllers.test.ts b/samples/tests/controllers.test.ts new file mode 100644 index 0000000..aab0d58 --- /dev/null +++ b/samples/tests/controllers.test.ts @@ -0,0 +1,249 @@ +import { suite, test } from 'mocha-typescript'; +import { assert } from 'chai'; +import { IKiwiOptions, createKiwiServer, processRequest } from '../../src/index'; +import { KiwiMetadataStorage } from '../../src/metadata/metadataStorage'; +import { UserController } from '../controllers/user/user-controller'; +import { TestController } from '../controllers/test-controller'; +import { TestController2 } from '../controllers/test-controller2'; +import { TestController3 } from '../controllers/test-controller3'; +import { TestMiddleware } from '../middlewares/test-middlware'; +import { TestMiddleware2 } from '../middlewares/test-middlware2'; +var httpMocks = require('node-mocks-http'); + +const options: IKiwiOptions = { + controllers: [UserController, TestController, TestController2, TestController3], + authorization: null, + middlewares: [TestMiddleware2, TestMiddleware], + cors: { + enabled: true, + domains: ['localhost:8086'] + }, + documentation: { + enabled: true, + path: '/apidoc' + }, + log: true, + port: 8086, + prefix: '/v1' +}; + +@suite +class UserControllersSuite { + static before() { + KiwiMetadataStorage.init(options); + } + + before() {} + + @test async 'It must return 1'() { + const body = { + name: 'NewUser', + lastname: 'NewUser', + age: 33, + address: [ + { + street: 'user street', + number: 2213 + } + ] + }; + var request = httpMocks.createRequest({ + method: 'POST', + url: '/v1/user/create', + headers: { + 'content-type': 'application/json' + } + }); + var response = httpMocks.createResponse({ + eventEmitter: require('events').EventEmitter + }); + + setImmediate(() => request.send(JSON.stringify(body))); + await processRequest(request, response); + assert.equal(response.statusCode, 200); + var data = JSON.parse(response._getData()); + assert.equal(data.name, body.name); + } + + @test async 'It must return true'() { + const body = { + id: 1, + name: 'UserModifyed', + lastname: 'UserModifyed', + age: 33, + address: [ + { + street: 'user street', + number: 2213 + } + ] + }; + var request = httpMocks.createRequest({ + method: 'PUT', + url: '/v1/user/update', + headers: { + 'content-type': 'application/json' + } + }); + var response = httpMocks.createResponse({ + eventEmitter: require('events').EventEmitter + }); + + setImmediate(() => request.send(JSON.stringify(body))); + await processRequest(request, response); + assert.equal(response.statusCode, 200); + var data = JSON.parse(response._getData()); + assert.equal(data, true); + } + + @test async 'It must get'() { + var request = httpMocks.createRequest({ + method: 'GET', + url: '/v1/user/get/1' + }); + + var response = httpMocks.createResponse(); + await processRequest(request, response); + var data = JSON.parse(response._getData()); + assert.equal(response.statusCode, 200); + assert.equal(data[0].name, 'UserModifyed'); + } + + @test async 'It must return a list'() { + var request = httpMocks.createRequest({ + method: 'GET', + url: '/v1/user/list' + }); + + var response = httpMocks.createResponse(); + await processRequest(request, response); + var data = JSON.parse(response._getData()); + assert.equal(response.statusCode, 200); + assert.lengthOf(data, 1); + } + + @test async 'It must return a filtered list'() { + var request = httpMocks.createRequest({ + method: 'GET', + url: '/v1/user/listFilter?name=UserModifyed&age=33' + }); + + var response = httpMocks.createResponse(); + await processRequest(request, response); + var data = JSON.parse(response._getData()); + assert.equal(response.statusCode, 200); + assert.lengthOf(data, 1); + } + + + @test async 'It must return list'() { + const token = 'token1'; + var request = httpMocks.createRequest({ + method: 'GET', + url: '/v1/user/search/UserModifyed', + headers: { + token: token + } + }); + + var response = httpMocks.createResponse(); + await processRequest(request, response); + assert.equal(response.statusCode, 200); + } + + static after() {} + + after() {} +} + +@suite +class ControllersSuite { + static before() { + KiwiMetadataStorage.init(options); + } + + before() {} + + @test async 'It must return the param 1'() { + const param = 'pepe'; + var request = httpMocks.createRequest({ + method: 'GET', + url: `/v1/testcontroller/testinguy/${param}` + }); + + var response = httpMocks.createResponse(); + await processRequest(request, response); + var data = JSON.parse(response._getData()); + assert.equal(response.statusCode, 200); + assert.equal(data.name, param); + } + + @test async 'It must return 404 http error'() { + var request = httpMocks.createRequest({ + method: 'GET', + url: '/v1/testcontroller/queryparadsdm/1' + }); + + var response = httpMocks.createResponse(); + await processRequest(request, response); + var data = response._getData(); + assert.equal(response.statusCode, 404); + assert.equal(data, 'Method doesnt match'); + } + + @test async 'It must create an object with query params values as properies'() { + var request = httpMocks.createRequest({ + method: 'GET', + url: '/v1/testcontroller/queryparam/1?name=guille' + }); + + var response = httpMocks.createResponse(); + await processRequest(request, response); + var data = JSON.parse(response._getData()); + assert.equal(response.statusCode, 200); + assert.equal(data.name, 'guille'); + } + + @test async 'It must add two header params'() { + const h1 = 'header1'; + const h2 = 'header2'; + var request = httpMocks.createRequest({ + method: 'GET', + url: '/v1/testcontroller/testHeaders', + headers: { + h1: h1, + h2: h2 + } + }); + + var response = httpMocks.createResponse(); + await processRequest(request, response); + var data = JSON.parse(response._getData()); + assert.equal(response.statusCode, 200); + assert.equal(data.h1, h1); + assert.equal(data.h2, h2); + } + + @test async 'It mus parse body'() { + const body = { name: 'kiwi' }; + var request = httpMocks.createRequest({ + method: 'POST', + url: '/v1/testcontroller/test123', + headers: { + 'content-type': 'application/json' + } + }); + var response = httpMocks.createResponse({ + eventEmitter: require('events').EventEmitter + }); + + setImmediate(() => request.send(JSON.stringify(body))); + await processRequest(request, response); + var data = JSON.parse(response._getData()); + assert.equal(data.name, body.name); + } + + static after() {} + + after() {} +} diff --git a/tests/load.test.js b/samples/tests/load.test.js similarity index 100% rename from tests/load.test.js rename to samples/tests/load.test.js diff --git a/tests/metadataStorage.test.ts b/samples/tests/metadataStorage.test.ts similarity index 52% rename from tests/metadataStorage.test.ts rename to samples/tests/metadataStorage.test.ts index eb18758..c1672da 100644 --- a/tests/metadataStorage.test.ts +++ b/samples/tests/metadataStorage.test.ts @@ -1,15 +1,16 @@ import { suite, test } from "mocha-typescript"; import { assert } from 'chai'; -import { IKiwiOptions } from '../src/index'; -import { TestController } from '../samples/test-controller'; -import { TestController2 } from '../samples/test-controller2'; -import { TestController3 } from '../samples/test-controller3'; -import { TestMiddleware2 } from '../samples/test-middlware2'; -import { TestMiddleware } from '../samples/test-middlware'; -import { KiwiMetadataStorage } from '../src/metadata/metadataStorage'; +import { IKiwiOptions } from '../../src/index'; +import { TestController } from '../controllers/test-controller'; +import { TestController2 } from '../controllers/test-controller2'; +import { TestController3 } from '../controllers/test-controller3'; +import { UserController } from "../controllers/user/user-controller"; +import { TestMiddleware2 } from '../middlewares/test-middlware2'; +import { TestMiddleware } from '../middlewares/test-middlware'; +import { KiwiMetadataStorage } from '../../src/metadata/metadataStorage'; const options: IKiwiOptions = { - controllers: [TestController, TestController2, TestController3], + controllers: [UserController, TestController, TestController2, TestController3], authorization: null, middlewares: [TestMiddleware2, TestMiddleware], cors: { @@ -24,21 +25,6 @@ const options: IKiwiOptions = { prefix: '/v1' } -/* -describe("Metadata storage test", () => { - it("it must create metadata for server", () => { - KiwiMetadataStorage.init(options); - assert.equal(9, Object.keys(KiwiMetadataStorage.routes).length); - }); - - it("it must match route", () => { - KiwiMetadataStorage.init(options); - const match = KiwiMetadataStorage.matchRoute('/v1/testcontroller/queryparam/1', 'get'); - assert.isNotNull(match); - }); -}); -*/ - @suite class KiwiMetadataStorageSuite { static before() { KiwiMetadataStorage.init(options); @@ -48,8 +34,8 @@ describe("Metadata storage test", () => { } - @test 'It must exist 11 routes'() { - assert.equal(11, Object.keys(KiwiMetadataStorage.routes).length); + @test 'It must exist 18 routes'() { + assert.equal(18, Object.keys(KiwiMetadataStorage.routes).length); } @test 'it must match route'() { diff --git a/tests/middlewares.test.ts b/samples/tests/middlewares.test.ts similarity index 91% rename from tests/middlewares.test.ts rename to samples/tests/middlewares.test.ts index 70a7b94..a575e49 100644 --- a/tests/middlewares.test.ts +++ b/samples/tests/middlewares.test.ts @@ -1,8 +1,8 @@ import { suite, test } from "mocha-typescript"; import { assert } from 'chai'; -import { CorsMiddleware } from '../src/middlewares/corsMiddlware'; -import { LogMiddleware } from '../src/middlewares/logMiddlware'; -import { DocMiddleware } from '../src/middlewares/docMiddleware'; +import { CorsMiddleware } from '../../src/middlewares/corsMiddlware'; +import { LogMiddleware } from '../../src/middlewares/logMiddlware'; +import { DocMiddleware } from '../../src/middlewares/docMiddleware'; var httpMocks = require('node-mocks-http'); var sinon = require('sinon'); diff --git a/samples/utils.ts b/samples/utils.ts index 03466ac..17a1340 100644 --- a/samples/utils.ts +++ b/samples/utils.ts @@ -1,5 +1,5 @@ -export class Utils{ - public print(){ - //console.log('entre1'); - } -} \ No newline at end of file +import { UserModel } from "./models/models"; + +export class Utils { + public static userList: Array = []; +} diff --git a/server.js.map b/server.js.map deleted file mode 100644 index 34e8c48..0000000 --- a/server.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"server.js","sourceRoot":"","sources":["test/server.ts"],"names":[],"mappings":";;AAAA,wCAAgD;AAChD,MAAM,MAAM,GAAG,wBAAgB,EAAE,CAAC;AAClC,MAAM,CAAC,MAAM,EAAE,CAAC"} \ No newline at end of file diff --git a/tests/authentication.test.ts b/tests/authentication.test.ts deleted file mode 100644 index b286b2f..0000000 --- a/tests/authentication.test.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { suite, test } from "mocha-typescript"; -import { assert } from 'chai'; -import { IKiwiOptions, createKiwiServer, processRequest } from '../src/index'; -import { TestController } from '../samples/test-controller'; -import { TestController2 } from '../samples/test-controller2'; -import { TestController3 } from '../samples/test-controller3'; -import { TestMiddleware2 } from '../samples/test-middlware2'; -import { TestMiddleware } from '../samples/test-middlware'; -import { KiwiMetadataStorage } from '../src/metadata/metadataStorage'; -import * as http from 'http'; -var sinon = require('sinon'); -var httpMocks = require('node-mocks-http'); - -var callback = sinon.spy(); - -var options: IKiwiOptions = { - controllers: [TestController, TestController2, TestController3], - authorization: function(request: http.IncomingMessage, roles: Array){ - callback(); - return true; - }, - middlewares: [TestMiddleware2, TestMiddleware], - cors: { - enabled: true - }, - documentation: { - enabled: true, - path: '/apidoc' - }, - log: true, - port: 8086, - prefix: '/v1' -} - -@suite class AuthenticationSuite { - static before() { - KiwiMetadataStorage.init(options); - } - - before() { - } - - @test async 'It must call validateAuthentication method'() { - var request = httpMocks.createRequest({ - method: 'GET', - url: `/v1/testcontroller2/getAction` - }); - - var response = httpMocks.createResponse(); - await processRequest(request, response); - assert.equal(response.statusCode, 200); - assert.isTrue(callback.calledOnce); - } - - static after() { - - } - - after() { - - } -} \ No newline at end of file diff --git a/tests/controllers.test.ts b/tests/controllers.test.ts deleted file mode 100644 index e912171..0000000 --- a/tests/controllers.test.ts +++ /dev/null @@ -1,124 +0,0 @@ -import { suite, test } from "mocha-typescript"; -import { assert } from 'chai'; -import { IKiwiOptions, createKiwiServer, processRequest } from '../src/index'; -import { TestController } from '../samples/test-controller'; -import { TestController2 } from '../samples/test-controller2'; -import { TestController3 } from '../samples/test-controller3'; -import { TestMiddleware2 } from '../samples/test-middlware2'; -import { TestMiddleware } from '../samples/test-middlware'; -import { KiwiMetadataStorage } from '../src/metadata/metadataStorage'; -var httpMocks = require('node-mocks-http'); - -const options: IKiwiOptions = { - controllers: [TestController, TestController2, TestController3], - authorization: null, - middlewares: [TestMiddleware2, TestMiddleware], - cors: { - enabled: true, - domains: ['localhost:8086'] - }, - documentation: { - enabled: true, - path: '/apidoc' - }, - log: true, - port: 8086, - prefix: '/v1' -} - -@suite class ControllersSuite { - static before() { - KiwiMetadataStorage.init(options); - } - - before() { - - } - - @test async 'It must return the param 1'() { - const param = 'pepe'; - var request = httpMocks.createRequest({ - method: 'GET', - url: `/v1/testcontroller/testinguy/${param}` - }); - - var response = httpMocks.createResponse(); - await processRequest(request, response); - var data = JSON.parse(response._getData()); - assert.equal(response.statusCode, 200); - assert.equal(data.name, param); - } - - @test async 'It must return 404 http error'() { - var request = httpMocks.createRequest({ - method: 'GET', - url: '/v1/testcontroller/queryparadsdm/1' - }); - - var response = httpMocks.createResponse(); - await processRequest(request, response); - var data = response._getData(); - assert.equal(response.statusCode, 404); - assert.equal(data, 'Method doesnt match'); - } - - @test async 'It must create an object with query params values as properies'() { - var request = httpMocks.createRequest({ - method: 'GET', - url: '/v1/testcontroller/queryparam/1?name=guille' - }); - - var response = httpMocks.createResponse(); - await processRequest(request, response); - var data = JSON.parse(response._getData()); - assert.equal(response.statusCode, 200); - assert.equal(data.name, 'guille'); - } - - @test async 'It must add two header params'() { - const h1 = 'header1'; - const h2 = 'header2'; - var request = httpMocks.createRequest({ - method: 'GET', - url: '/v1/testcontroller/testHeaders', - headers: { - h1: h1, - h2: h2 - } - }); - - var response = httpMocks.createResponse(); - await processRequest(request, response); - var data = JSON.parse(response._getData()); - assert.equal(response.statusCode, 200); - assert.equal(data.h1, h1); - assert.equal(data.h2, h2); - } - - @test async 'It mus parse body'() { - const body = { name: 'kiwi' }; - var request = httpMocks.createRequest({ - method: 'POST', - url: '/v1/testcontroller/test123', - headers:{ - "content-type": "application/json" - } - }); - var response = httpMocks.createResponse({ - eventEmitter: require('events').EventEmitter - }); - - setImmediate(() => request.send(JSON.stringify(body))); - await processRequest(request, response); - var data = JSON.parse(response._getData()); - assert.equal(data.name, body.name); - } - - static after() { - - } - - after() { - - } -} diff --git a/tsconfig.json b/tsconfig.json index ed8c690..2158c5c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -9,7 +9,8 @@ "outDir": "./dist", "baseUrl": "./", "emitDecoratorMetadata": true, - "experimentalDecorators": true + "experimentalDecorators": true, + "typeRoots": ["node_modules/@types"] }, "include": [ "samples/**/*", @@ -18,4 +19,4 @@ "exclude": [ "src" ] -} \ No newline at end of file +}