├── README.md ├── .gitignore ├── package.json ├── LICENSE ├── stub-generator.js └── server.js /README.md: -------------------------------------------------------------------------------- 1 | rest-stubs 2 | ========== 3 | 4 | Generates trivial REST endpoints for arbitrary "entities," caches them in memory for quick dev, testing, and demo services 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | lib-cov 2 | *.seed 3 | *.log 4 | *.csv 5 | *.dat 6 | *.out 7 | *.pid 8 | *.gz 9 | 10 | pids 11 | logs 12 | results 13 | 14 | npm-debug.log 15 | node_modules 16 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rest-stub", 3 | "description": "Generates temporary stub REST endpoints", 4 | "version": "0.1.0", 5 | "author": { 6 | "name": "Adam Breindel", 7 | "email": "adbreind@gmail.com" 8 | }, 9 | "dependencies": { 10 | "express": "~> 4.5.0" 11 | }, 12 | "engines": { 13 | "node": ">= 0.8.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 adbreind 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | 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, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /stub-generator.js: -------------------------------------------------------------------------------- 1 | var fakeDatabase = {}; 2 | /* this is a map of entity name to an array of objects 3 | e.g. { food : [ 4 | { 5 | name: "apple", 6 | id : 21 7 | }, 8 | { 9 | name: "fries", 10 | id : 24 11 | }] 12 | } 13 | etc. */ 14 | 15 | equals = function(field, val, obj) { return obj[field] == val }; 16 | 17 | equalsId = equals.bind(undefined, "id"); 18 | 19 | log = function() { 20 | console.log(JSON.stringify(fakeDatabase)); 21 | }; 22 | 23 | exports.initializeEntity = function(name, objects) { 24 | if (!fakeDatabase[name]) 25 | fakeDatabase[name] = []; 26 | if (objects) 27 | fakeDatabase[name] = objects; 28 | }; 29 | 30 | exports.findAll = function(name) { 31 | return function(req, res) { 32 | res.send(fakeDatabase[name]); 33 | } 34 | }; 35 | 36 | exports.findById = function(name) { 37 | return function(req, res) { 38 | var id = req.params.id; 39 | console.log('Retrieving ' + name + ': ' + id); 40 | var found = fakeDatabase[name].filter(equalsId.bind(undefined, id))[0]; 41 | if (found) 42 | res.send(found); 43 | else { 44 | res.status(404); 45 | res.end(); 46 | } 47 | } 48 | }; 49 | 50 | exports.create = function(name) { 51 | return function(req, res) { 52 | var entity = req.body; 53 | var records = fakeDatabase[name]; 54 | entity.id = records.reduce(function(p, c) { 55 | return (c.id && c.id > p) ? c.id : p; 56 | }, 0) + 1; 57 | console.log('Adding ' + name + ': ' + JSON.stringify(entity)); 58 | records.push(entity); 59 | res.send(entity); 60 | } 61 | } 62 | 63 | exports.update = function(name) { 64 | return function(req, res) { 65 | var id = req.params.id; 66 | var entity = req.body; 67 | console.log('Updating ' + name + ': ' + id); 68 | console.log(JSON.stringify(entity)); 69 | var found = fakeDatabase[name].filter(equalsId.bind(undefined, id))[0]; 70 | for (var k in entity) 71 | found[k] = entity[k]; 72 | res.send(found); 73 | } 74 | } 75 | 76 | exports.delete = function(name) { 77 | return function(req, res) { 78 | var id = req.params.id; 79 | console.log('Deleting ' + name + ': ' + id); 80 | var records = fakeDatabase[name]; 81 | var found = records.filter(equalsId.bind(undefined, id))[0]; 82 | records.splice(records.indexOf(found),1); 83 | res.end(); 84 | } 85 | } -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | var express = require('express'), 2 | stubGenerator = require('./stub-generator'); 3 | 4 | var app = express(); 5 | 6 | app.configure(function () { 7 | app.use(allowCrossDomain); 8 | app.use(express.logger('dev')); 9 | app.use(express.bodyParser()); 10 | }); 11 | 12 | entities = [ 'contact' ]; 13 | entities.forEach(stub); 14 | 15 | function stub(name) { 16 | console.log("Stubbing: " + name); 17 | stubGenerator.initializeEntity(name); 18 | app.get('/' + name, stubGenerator.findAll(name)); 19 | app.get('/' + name + '/:id', stubGenerator.findById(name)); 20 | app.post('/' + name, stubGenerator.create(name)); 21 | 22 | var update = stubGenerator.update(name); 23 | app.post('/' + name + '/:id', update); 24 | app.put('/' + name + '/:id', update); 25 | app.delete('/' + name + '/:id', stubGenerator.delete(name)); 26 | entities.push(name); 27 | } 28 | 29 | app.get("/stub-*", function(req, res) { 30 | var ent = (req.path.substr(1).split("/")[0]).split("-")[1]; 31 | if (ent && entities.indexOf(ent)==-1 && ent.indexOf(".")==-1) { 32 | stub(ent); 33 | res.end(); 34 | } 35 | }); 36 | 37 | stubGenerator.initializeEntity("book", [{"title":"Great Expectations","id":2},{"title":"Bleak House","id":4}]); 38 | stub("book"); 39 | 40 | stubGenerator.initializeEntity("contact", [ 41 | {"first":"Joe","last":"Smith","email":"joe@gmail.com","id":1, 42 | company_id:"xyz", lotteryWinner : false }, 43 | {"first":"Joe2","last":"Smith2","email":"joe2@gmail.com","id":2, 44 | company_id:"abc", lotteryWinner : false }, 45 | {"first":"Joe3","last":"Smith3","email":"joe3@gmail.com","phone":"555-1212", 46 | "id":3, company_id:"xyz", lotteryWinner : false} 47 | ]); 48 | stub("contact"); 49 | 50 | app.listen(3000); 51 | console.log('Listening on port 3000...'); 52 | 53 | function allowCrossDomain (req, res, next) { 54 | res.header('Access-Control-Allow-Origin', '*'); 55 | res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS'); 56 | res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization, Content-Length, X-Requested-With'); 57 | 58 | // intercept OPTIONS method 59 | if ('OPTIONS' == req.method) { 60 | res.send(200); 61 | } 62 | else { 63 | next(); 64 | } 65 | } --------------------------------------------------------------------------------