From 1602032e96a51de8b4fe81231ca05fe17a54503d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Barbeau?= Date: Fri, 24 Feb 2017 13:28:30 -0500 Subject: [PATCH] feat(tool): add tool service --- src/tools/index.ts | 10 +++ src/tools/routes.ts | 150 +++++++++++++++++++++++++++++++++++ src/tools/tool.controller.ts | 87 ++++++++++++++++++++ src/tools/tool.model.ts | 69 ++++++++++++++++ src/tools/tool.test.ts | 52 ++++++++++++ src/tools/tool.validator.ts | 23 ++++++ 6 files changed, 391 insertions(+) create mode 100644 src/tools/index.ts create mode 100644 src/tools/routes.ts create mode 100644 src/tools/tool.controller.ts create mode 100644 src/tools/tool.model.ts create mode 100644 src/tools/tool.test.ts create mode 100644 src/tools/tool.validator.ts diff --git a/src/tools/index.ts b/src/tools/index.ts new file mode 100644 index 0000000..0cc53d9 --- /dev/null +++ b/src/tools/index.ts @@ -0,0 +1,10 @@ +import * as Hapi from 'hapi'; +import Routes from './routes'; +import { IDatabase } from '../database'; +import { IServerConfigurations } from '../configurations'; + +export function init(server: Hapi.Server, + configs: IServerConfigurations, + database: IDatabase) { + Routes(server, configs, database); +} diff --git a/src/tools/routes.ts b/src/tools/routes.ts new file mode 100644 index 0000000..53b4f0f --- /dev/null +++ b/src/tools/routes.ts @@ -0,0 +1,150 @@ +import * as Hapi from 'hapi'; +import * as Joi from 'joi'; +import ToolController from './tool.controller'; +import * as ToolValidator from './tool.validator'; +// import { jwtValidator } from '../users/user-validator'; +import { IDatabase } from '../database'; +import { IServerConfigurations } from '../configurations'; + +export default function (server: Hapi.Server, + configs: IServerConfigurations, + database: IDatabase) { + + const toolController = new ToolController(configs, database); + server.bind(toolController); + + server.route({ + method: 'GET', + path: '/tools/{id}', + config: { + handler: toolController.getToolById, + // auth: 'jwt', + auth: false, + tags: ['api', 'tools'], + description: 'Get tools by id.', + validate: { + params: { + id: Joi.string().required() + } + // headers: jwtValidator + }, + plugins: { + 'hapi-swagger': { + responses: { + '200': { + 'description': 'Tool founded.' + }, + '404': { + 'description': 'Tool does not exists.' + } + } + } + } + } + }); + + server.route({ + method: 'GET', + path: '/tools', + config: { + handler: toolController.getTools, + // auth: 'jwt', + auth: false, + tags: ['api', 'tools'], + description: 'Get all tools.', + validate: { + query: { + // top: Joi.number().default(5), + // skip: Joi.number().default(0) + } + // headers: jwtValidator + } + } + }); + + server.route({ + method: 'DELETE', + path: '/tools/{id}', + config: { + handler: toolController.deleteTool, + // auth: 'jwt', + auth: false, + tags: ['api', 'tools'], + description: 'Delete tool by id.', + validate: { + params: { + id: Joi.string().required() + } + // headers: jwtValidator + }, + plugins: { + 'hapi-swagger': { + responses: { + '200': { + 'description': 'Deleted Tool.', + }, + '404': { + 'description': 'Tool does not exists.' + } + } + } + } + } + }); + + server.route({ + method: 'PUT', + path: '/tools/{id}', + config: { + handler: toolController.updateTool, + // auth: 'jwt', + auth: false, + tags: ['api', 'tools'], + description: 'Update tool by id.', + validate: { + params: { + id: Joi.string().required() + }, + payload: ToolValidator.updateToolModel + // headers: jwtValidator + }, + plugins: { + 'hapi-swagger': { + responses: { + '200': { + 'description': 'Deleted Tool.', + }, + '404': { + 'description': 'Tool does not exists.' + } + } + } + } + } + }); + + server.route({ + method: 'POST', + path: '/tools', + config: { + handler: toolController.createTool, + // auth: 'jwt', + auth: false, + tags: ['api', 'tools'], + description: 'Create a tool.', + validate: { + payload: ToolValidator.createToolModel + // headers: jwtValidator + }, + plugins: { + 'hapi-swagger': { + responses: { + '201': { + 'description': 'Created Tool.' + } + } + } + } + } + }); +} diff --git a/src/tools/tool.controller.ts b/src/tools/tool.controller.ts new file mode 100644 index 0000000..85e3983 --- /dev/null +++ b/src/tools/tool.controller.ts @@ -0,0 +1,87 @@ +import * as Hapi from 'hapi'; +import * as Boom from 'boom'; +import { ITool, ToolInstance } from './tool.model'; +import { IDatabase } from '../database'; +import { IServerConfigurations } from '../configurations'; + +export default class ToolController { + + private database: IDatabase; + private configs: IServerConfigurations; + + constructor(configs: IServerConfigurations, database: IDatabase) { + this.configs = configs; + this.database = database; + } + + public createTool(request: Hapi.Request, reply: Hapi.IReply) { + const newTool: ITool = request.payload; + this.database.tool.create(newTool).then((tool) => { + reply(tool).code(201); + }).catch((error) => { + reply(Boom.badImplementation(error)); + }); + } + + public updateTool(request: Hapi.Request, reply: Hapi.IReply) { + const id = request.params['id']; + const tool: ITool = request.payload; + + this.database.tool.update(tool, { + where: { + id: id + } + }).then((count: [number, ToolInstance[]]) => { + if (count[0]) { + reply({}); + } else { + reply(Boom.notFound()); + } + }).catch((error) => { + reply(Boom.badImplementation(error)); + }); + } + + public deleteTool(request: Hapi.Request, reply: Hapi.IReply) { + const id = request.params['id']; + this.database.tool.destroy({ + where: { + id: id + } + }).then((count: number) => { + if (count) { + reply({}); + } else { + reply(Boom.notFound()); + } + }).catch((error) => { + reply(Boom.badImplementation(error)); + }); + } + + public getToolById(request: Hapi.Request, reply: Hapi.IReply) { + const id = request.params['id']; + this.database.tool.findOne({ + where: { + id: id + } + }).then((tool: ToolInstance) => { + if (tool) { + reply(tool); + } else { + reply(Boom.notFound()); + } + }).catch((error) => { + reply(Boom.badImplementation(error)); + }); + } + + public getTools(request: Hapi.Request, reply: Hapi.IReply) { + this.database.tool.findAll() + .then((tools: Array) => { + reply(tools); + }).catch((error) => { + reply(Boom.badImplementation(error)); + }); + } +} diff --git a/src/tools/tool.model.ts b/src/tools/tool.model.ts new file mode 100644 index 0000000..66634ef --- /dev/null +++ b/src/tools/tool.model.ts @@ -0,0 +1,69 @@ +import * as Sequelize from 'sequelize'; + +interface PropertiesTool { + attribution: string; + minZoom: number; + maxZoom: number; +}; + +export interface ITool { + name: string; + url: string; + protected: boolean; + properties: PropertiesTool; +}; + +export interface ToolInstance extends Sequelize.Instance { + id: string; + createdAt: Date; + updatedAt: Date; + + name: string; + url: string; + protected: boolean; + properties: PropertiesTool; +} + +export interface ToolModel + extends Sequelize.Model { } + + +export default function define(sequelize: Sequelize.Sequelize, DataTypes) { + const tool = sequelize.define('tool', { + 'id': { + 'type': DataTypes.INTEGER, + 'allowNull': false, + 'primaryKey': true, + 'autoIncrement': true + }, + 'name': { + 'type': DataTypes.STRING(64) + }, + 'url': { + 'type': DataTypes.STRING(255), + 'validate': { + 'isUrl': true + } + }, + 'protected': { + 'type': DataTypes.BOOLEAN + }, + 'properties': { + 'type': DataTypes.TEXT, + 'get': function() { + return JSON.parse(this.getDataValue('properties')); + }, + 'set': function(val) { + this.setDataValue('properties', JSON.stringify({})); + } + } + }, + { + 'tableName': 'tool', + 'timestamps': true + }); + + tool.sync(); + + return tool; +} diff --git a/src/tools/tool.test.ts b/src/tools/tool.test.ts new file mode 100644 index 0000000..7da38bd --- /dev/null +++ b/src/tools/tool.test.ts @@ -0,0 +1,52 @@ +import * as test from 'tape'; +// import ToolCont from './tool.controller'; +import * as Server from '../server'; +import * as Configs from '../configurations'; + +const serverConfigs = Configs.getServerConfigs(); + +Server.init(serverConfigs).then((server) => { + + test('Basic HTTP Tests - GET /tools', function(t) { + const options = { + method: 'GET', + url: '/tools' + }; + server.inject(options, function(response) { + t.equal(response.statusCode, 200); + t.equal(response.result.length, 0); + server.stop(t.end); + }); + }); + + + test('Basic HTTP Tests - GET /tools/{id}', function(t) { + const options = { + method: 'GET', + url: '/tools/2' + }; + server.inject(options, function(response) { + t.equal(response.statusCode, 404); + server.stop(t.end); + }); + }); + + + test('Basic HTTP Tests - POST /tools', function(t) { + const options = { + method: 'POST', + url: '/tools', + payload: { + name: 'dummy', + protected: false, + properties: {} + } + }; + server.inject(options, function(response) { + t.equal(response.statusCode, 201); + server.stop(t.end); + }); + }); + + +}); diff --git a/src/tools/tool.validator.ts b/src/tools/tool.validator.ts new file mode 100644 index 0000000..1624588 --- /dev/null +++ b/src/tools/tool.validator.ts @@ -0,0 +1,23 @@ +import * as Joi from 'joi'; + +export const createToolModel = Joi.object().keys({ + name: Joi.string().max(64), + url: Joi.string(), + protected: Joi.boolean(), + properties: Joi.object().required().keys({ + attribution: Joi.string(), + minZoom: Joi.number(), + maxZoom: Joi.number() + }) +}); + +export const updateToolModel = Joi.object().keys({ + name: Joi.string().max(64), + url: Joi.string(), + protected: Joi.boolean(), + properties: Joi.object().required().keys({ + attribution: Joi.string(), + minZoom: Joi.number(), + maxZoom: Joi.number() + }) +});