From 14bb6d8e8b4f5b9b6923f42ae3c12c0ba41a6e7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20Mass=C3=A9?= Date: Fri, 8 Sep 2017 13:05:16 +0200 Subject: [PATCH] REST resources are now generic instead of hardcoded --- server.js | 120 ++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 94 insertions(+), 26 deletions(-) diff --git a/server.js b/server.js index 535a400..55fca4f 100644 --- a/server.js +++ b/server.js @@ -2,15 +2,33 @@ var express = require("express"); var _ = require("underscore"); var bodyParser = require('body-parser'); +var util = require('util'); // ExpressJS Setup var app = express(); var router = express.Router(); var port = 8080; -// Our global list of "Things" -var things = { }; -var counter = 1; +// Default config +var config = { + "things": { + "state": { + "storage": {}, + "counter": 1 + }, + "config": { + "fields": { + "name": { "required": true }, + "price": { "required": true } + } + } + } +}; + +var admin_endpoints = [ + { "url": "/debug/state", "verbs": [ "GET" ] }, + { "url": "/config/", "verbs": [ "GET" ] } +]; // Log every request router.use(function (req,res,next) { @@ -18,14 +36,38 @@ router.use(function (req,res,next) { console.log("%s %s => %d", req.method, req.originalUrl, res.statusCode); }); +// Dump the current state +router.get("/debug/state",function(req,res){ + success(res, 200, + _.mapObject(config, (val, key) => { + return val.state; + }) + ); +}); + +// Dump the current config +router.get("/config",function(req,res){ + success(res, 200, + _.mapObject(config, (val, key) => { + return val.config; + }) + ); +}); + // Any GET on / ends up with a nice documentation as JSON router.get("/",function(req,res){ var response = { name: "API Mockup", description: "A simple API Mockup tool", - endpoints: { - things: "/things/" - }, + endpoints: _.chain(config) + .map((item, key) => { + return { + "url": util.format("/%s/", key), + "verbs": [ "GET", "POST", "PUT", "DELETE" ] + }; + }) + .union(admin_endpoints) + .value(), documentation: { "GitHub": "https://github.com/nmasse-itix/API-Mockup.git" } @@ -34,61 +76,85 @@ router.get("/",function(req,res){ }); // Get all things -router.get("/things",function(req,res){ - success(res, 200, _.values(things)); +router.get("/:object/",function(req,res){ + var resource = req.params.object; + if (! (resource in config)) { + return error(res, 400, util.format("There is no resource '%s', try one of those resources : %s", resource, _.keys(config).join(", "))); + } + success(res, 200, _.values(config[resource].state.storage)); }); // Get a thing -router.get("/things/:id",function(req,res){ +router.get("/:object/:id",function(req,res){ + var resource = req.params.object; + if (! (resource in config)) { + return error(res, 400, util.format("There is no resource '%s', try one of those resources : %s", resource, _.keys(config).join(", "))); + } + var id = req.params.id; - if (! (id in things)) { - return error(res, 404, "No thing with this id"); + if (! (id in config[resource].state.storage)) { + return error(res, 404, util.format("No such resource '%s' with id '%s' !", resource, id)); } - success(res, 200, things[id]); + success(res, 200, config[resource].state.storage[id]); }); // Create a thing -router.post("/things",function(req,res){ +router.post("/:object/",function(req,res){ + var resource = req.params.object; + if (! (resource in config)) { + return error(res, 400, util.format("There is no resource '%s', try one of those resources : %s", resource, _.keys(config).join(", "))); + } + var thing = req.body; if (thing == null) { return error(res, 400, "No body sent !"); } - thing.id = counter++; - things[thing.id] = thing; + thing.id = config[resource].state.counter++; + config[resource].state.storage[thing.id] = thing; success(res, 201, thing); }); // Delete a thing -router.delete("/things/:id",function(req,res){ +router.delete("/:object/:id",function(req,res){ + var resource = req.params.object; + if (! (resource in config)) { + return error(res, 400, util.format("There is no resource '%s', try one of those resources : %s", resource, _.keys(config).join(", "))); + } + var id = req.params.id; - if (! (id in things)) { - return error(res, 404, "No thing with this id"); + if (! (id in config[resource].state.storage)) { + return error(res, 404, util.format("No such resource '%s' with id '%s' !", resource, id)); } - var thing = things[id]; - delete things[id] + var thing = config[resource].state.storage[id]; + delete config[resource].state.storage[id] success(res, 202, thing); }); // Update a thing -router.put("/things/:id",function(req,res){ +router.put("/:object/:id",function(req,res){ + var resource = req.params.object; + if (! (resource in config)) { + return error(res, 400, util.format("There is no resource '%s', try one of those resources : %s", resource, _.keys(config).join(", "))); + } + var thing = req.body; if (thing == null) { return error(res, 400, "No body sent !"); } var id = req.params.id; - if (! (id in things)) { - return error(res, 404, "No thing with this id"); + if (! (id in config[resource].state.storage)) { + return error(res, 404, util.format("No such resource '%s' with id '%s' !", resource, id)); } if (thing.id != id) { - return error(res, 400, "The id cannot be updated"); + return error(res, 400, util.format("The id cannot be updated: '%s' vs '%s'", thing.id, id)); } - things[id] = thing; + config[resource].state.storage[id] = thing; success(res, 202, thing); }); @@ -108,7 +174,7 @@ app.use("*",function(req,res){ }); // Start the HTTP Server -app.listen(port,function(){ +var server = app.listen(port,function(){ console.log("API Mockup listening at port %d", port); }); @@ -127,3 +193,5 @@ function success(res, code, response) { .type("application/json") .send(JSON.stringify(response)); } + +module.exports = server; // Export the expressJS server object, to be used in unit tests