diff --git a/README.md b/README.md index e72d5ab..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: - + Also you can add the order that you want to execute your middlewares: + ```javascript - import { IMiddleware } from 'kiwi-server'; - import { MiddlewareBefore } from 'kiwi-server'; + 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/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/controllers/user/user-controller.ts b/samples/controllers/user/user-controller.ts index 03bca7f..513ccb7 100644 --- a/samples/controllers/user/user-controller.ts +++ b/samples/controllers/user/user-controller.ts @@ -2,10 +2,11 @@ import { Get, Post, Put, JsonController, Param, Body, QueryParam, Authorize, Hea import { UserModel } from '../../models/models'; import { isNil } from 'lodash'; import { Utils } from '../../utils'; +import { AuxiliaryFunctions } from '../../auxiliaryFunctions'; @JsonController('/user') export class UserController { - constructor() {} + constructor(private aux: AuxiliaryFunctions) {} @Authorize(['Admin']) @Post('/create') @@ -34,13 +35,11 @@ export class UserController { return true; } - @Authorize() @Get('/list') public listAll() { return Utils.userList; } - @Authorize() @Get('/listFilter') public listFilter(@QueryParam() params: any) { if (!isNil(params)) { @@ -59,4 +58,15 @@ export class UserController { }); 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/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/tests/controllers.test.ts b/samples/tests/controllers.test.ts index 3250499..aab0d58 100644 --- a/samples/tests/controllers.test.ts +++ b/samples/tests/controllers.test.ts @@ -1,8 +1,8 @@ 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 { UserController } from '../controllers/user/user-controller'; import { TestController } from '../controllers/test-controller'; import { TestController2 } from '../controllers/test-controller2'; import { TestController3 } from '../controllers/test-controller3'; @@ -135,6 +135,22 @@ class UserControllersSuite { 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() {} diff --git a/samples/tests/metadataStorage.test.ts b/samples/tests/metadataStorage.test.ts index 8c4200c..c1672da 100644 --- a/samples/tests/metadataStorage.test.ts +++ b/samples/tests/metadataStorage.test.ts @@ -34,8 +34,8 @@ const options: IKiwiOptions = { } - @test 'It must exist 17 routes'() { - assert.equal(17, Object.keys(KiwiMetadataStorage.routes).length); + @test 'It must exist 18 routes'() { + assert.equal(18, Object.keys(KiwiMetadataStorage.routes).length); } @test 'it must match route'() {