Comparing version 0.1.5 to 0.1.6
134
lib/api.js
@@ -1,28 +0,11 @@ | ||
var Async = require('async'); | ||
var Mongoose = require('mongoose'); | ||
var Express = require('express'); | ||
var BodyParser = require('body-parser'); | ||
var PathToRegexp = require('path-to-regexp'); | ||
var ModuleBuilder = require('./module-builder.js'); | ||
var Definition = require('./definition.js'); | ||
var SwaggerBuilder = require('./swagger-builder.js'); | ||
var MONGO_FIELDS = ['_id', '__v']; | ||
var SELECT = MONGO_FIELDS.map(function(f) {return '-' + f}).join(' '); | ||
var METHODS = ['get', 'patch', 'put', 'post', 'delete']; | ||
var removeMongoFields = function(obj) { | ||
if (typeof obj !== 'object') return obj; | ||
else if (Array.isArray(obj)) return obj.map(removeMongoFields); | ||
else { | ||
for (key in obj) { | ||
if (MONGO_FIELDS.indexOf(key) === -1) { | ||
obj[key] = removeMongoFields(obj[key]); | ||
} else { | ||
delete obj[key]; | ||
} | ||
} | ||
return obj; | ||
} | ||
} | ||
var setJamminDefaults = function(req, res, next) { | ||
@@ -51,2 +34,3 @@ if (!req.jammin) { | ||
self.router.use(BodyParser.json()); | ||
self.moduleBuilder = new ModuleBuilder(self.router); | ||
METHODS.concat(['use']).forEach(function(method) { | ||
@@ -72,3 +56,7 @@ var origFunc = self.router[method]; | ||
} | ||
API.prototype.module = require('./module.js'); | ||
API.prototype.module = function() { | ||
this.moduleBuilder.addRoutes.apply(this.moduleBuilder, arguments); | ||
} | ||
API.Schema = Mongoose.Schema; | ||
@@ -102,107 +90,1 @@ | ||
/** Definition **/ | ||
var Definition = function(api, label, schema) { | ||
this.api = api; | ||
this.label = label; | ||
this.db = api.mongoose.model(label, new Mongoose.Schema(schema)); | ||
} | ||
Definition.prototype.queryDB = function(method, many, query, doc, callback) { | ||
var self = this; | ||
if (method === 'get') { | ||
var find = many ? self.db.find : self.db.findOne; | ||
find.apply(self.db, [query, SELECT]).exec(callback); | ||
} else if (method === 'post') { | ||
var docs = many ? doc : [doc]; | ||
Async.parallel(docs.map(function(doc) { | ||
return function(callback) { | ||
var toAdd = new self.db(doc); | ||
toAdd.save(callback); | ||
} | ||
}), function(err) { | ||
callback(err, {success: true}); | ||
}) | ||
} else if (method === 'put') { | ||
var docs = many ? doc : [doc]; | ||
Async.parallel(docs.map(function(doc) { | ||
return function(callback) { | ||
self.db.findOneAndUpdate(query, doc, {upsert: true}).select(SELECT).exec(callback); | ||
} | ||
}), callback); | ||
} else if (method === 'patch') { | ||
if (many) { | ||
self.db.update(query, doc, {multi: true}).select(SELECT).exec(function(err) { | ||
callback(err, {success: true}); | ||
}) | ||
} else { | ||
self.db.findOneAndUpdate(query, doc, {new: true}).select(SELECT).exec(callback); | ||
} | ||
} else if (method === 'delete') { | ||
var remove = many ? self.db.remove : self.db.findOneAndRemove; | ||
remove.apply(self.db, [query]).exec(function(err, thing) { | ||
callback(err, thing); | ||
}) | ||
} else { | ||
throw new Error("Unsupported method:" + method); | ||
} | ||
} | ||
var getRouteFunction = function(method, many) { | ||
// Can be called with (path[, middleware...]) or (req, res, callback); | ||
return function() { | ||
var self = this; | ||
arguments = Array.prototype.slice.call(arguments); | ||
var isMiddleware = typeof arguments[0] !== 'string'; | ||
var sendResponse = !isMiddleware || arguments.length === 2; | ||
var options = sendResponse && typeof arguments[1] === 'object' ? arguments[1] : {}; | ||
var dbAction = function(req, res, next) { | ||
var useMethod = isMiddleware ? method : req.jammin.method; | ||
self.queryDB(useMethod, many, req.jammin.query, req.jammin.document, function(err, thing) { | ||
if (err) res.status(500).json({error: err.toString()}); | ||
else if (!thing) res.status(404).json({error: 'Not Found'}) | ||
else if (sendResponse && useMethod === 'get') { | ||
if (many) thing = thing.map(function(t) { return t.toObject({versionKey: false}) }); | ||
else thing = thing.toObject({versionKey: false}); | ||
if (options.mapItem) { | ||
if (many) thing = thing.map(options.mapItem); | ||
else thing = options.mapItem(thing); | ||
} | ||
removeMongoFields(thing); | ||
res.json(thing); | ||
} | ||
else if (sendResponse) res.json({success: true}) | ||
else next(thing); | ||
}) | ||
} | ||
// If we get (req, res, callback) instead of a path, just apply | ||
// dbAction. Otherwise delegate to the router. | ||
if (isMiddleware) return dbAction.apply(self, arguments); | ||
arguments = Array.prototype.slice.call(arguments); | ||
var expressPath = arguments[0]; | ||
var keys = []; | ||
PathToRegexp(expressPath, keys); | ||
var swaggerPath = expressPath; | ||
keys.forEach(function(key) { | ||
swaggerPath = swaggerPath.replace(':' + key.name, '{' + key.name + '}'); | ||
}); | ||
var middleware = arguments.splice(1); | ||
if (typeof middleware[0] === 'object') middleware.shift(); | ||
if (options.swagger) options.swagger = JSON.parse(JSON.stringify(options.swagger)); | ||
self.api.swaggerBuilder.addRoute({ | ||
method: method, | ||
path: swaggerPath, | ||
collection: self.label, | ||
many: many | ||
}, options.swagger); | ||
middleware.push(dbAction); | ||
middleware.unshift(expressPath); | ||
this.api.router[method].apply(this.api.router, middleware); | ||
} | ||
} | ||
METHODS.forEach(function(method) { | ||
Definition.prototype[method] = getRouteFunction(method); | ||
Definition.prototype[method + 'Many'] = getRouteFunction(method, true) | ||
}) |
@@ -5,8 +5,22 @@ var Request = require('request'); | ||
var Client = module.exports = function(options) { | ||
for (var key in options.module) { | ||
var fn = options.module[key]; | ||
var fnName = key; | ||
if (typeof fn === 'function') { | ||
var url = options.host + Path.join(options.basePath || '/', fnName); | ||
this[key] = getProxyFunction(key, url); | ||
this.options = options; | ||
this.addFunctionsFromObject(options.module, options.basePath || '/'); | ||
} | ||
Client.prototype.addFunctionsFromObject = function(obj, path, keys) { | ||
var self = this; | ||
keys = keys || []; | ||
for (var key in obj) { | ||
var fn = obj[key]; | ||
var newPath = Path.join(path, key); | ||
if (typeof fn === 'function' || fn === true) { | ||
var url = self.options.host + newPath; | ||
var container = this; | ||
keys.forEach(function(k) { | ||
container[k] = container[k] || {}; | ||
container = container[k]; | ||
}); | ||
container[key] = getProxyFunction(url); | ||
} else if (typeof fn === 'object' && !Array.isArray(fn)) { | ||
self.addFunctionsFromObject(fn, newPath, keys.concat(key)); | ||
} | ||
@@ -16,3 +30,3 @@ } | ||
var getProxyFunction = function(fnName, url) { | ||
var getProxyFunction = function(url) { | ||
return function() { | ||
@@ -27,3 +41,3 @@ arguments = Array.prototype.slice.call(arguments); | ||
if (err) callback(err); | ||
else if (resp.statusCode === 500) callback(body); | ||
else if (resp.statusCode === 500) callback(body.error); | ||
else if (resp.statusCode !== 200) callback({statusCode: resp.statusCode}); | ||
@@ -30,0 +44,0 @@ else callback(null, body); |
{ | ||
"name": "jammin", | ||
"version": "0.1.5", | ||
"version": "0.1.6", | ||
"description": "REST API Generator using Express and Mongoose", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -16,5 +16,29 @@ var Path = require('path'); | ||
var NestedModule = { | ||
foo: function() {return 'foo'}, | ||
nest: { | ||
bar: function() {return 'bar'}, | ||
baz: function() {return 'baz'} | ||
} | ||
} | ||
API.module('/nest', {module: NestedModule}); | ||
var ErrorModule = { | ||
throwError: function() { | ||
throw new Error("thrown"); | ||
}, | ||
callbackError: function(callback) { | ||
callback({message: "callback"}); | ||
} | ||
} | ||
API.module('/error', {module: ErrorModule, async: true}) | ||
var App = require('express')(); | ||
App.use(API.router); | ||
App.use(function(err, req, res, next) { | ||
if (err) console.log('Error found'); | ||
next(); | ||
}) | ||
var Server = null; | ||
@@ -21,0 +45,0 @@ module.exports.listen = function(port) { |
@@ -14,7 +14,17 @@ var Async = require('async'); | ||
module: require('fs') | ||
}) | ||
}); | ||
var NestedModule = new Jammin.Client({ | ||
basePath: '/nest', | ||
host: 'http://127.0.0.1:3333', | ||
module: {foo: true, nest: {bar: true, baz: true}} | ||
}); | ||
var ErrorModule = new Jammin.Client({ | ||
basePath: '/error', | ||
host: 'http://127.0.0.1:3333', | ||
module: {throwError: true, callbackError: true} | ||
}); | ||
var Server = require('./modules-server.js'); | ||
var Expect = require('chai').expect; | ||
var expect = require('chai').expect; | ||
@@ -47,4 +57,4 @@ var FILES = [{ | ||
Validator.isEmail('foo@bar.com', function(err, isEmail) { | ||
Expect(err).to.equal(null); | ||
Expect(isEmail).to.equal(true); | ||
expect(err).to.equal(null); | ||
expect(isEmail).to.equal(true); | ||
callback(); | ||
@@ -55,4 +65,4 @@ }); | ||
Validator.isEmail('foobar.com', function(err, isEmail) { | ||
Expect(err).to.equal(null); | ||
Expect(isEmail).to.equal(false); | ||
expect(err).to.equal(null); | ||
expect(isEmail).to.equal(false); | ||
callback(); | ||
@@ -72,3 +82,3 @@ }); | ||
RemoteFS.writeFile(FILES[0].in_filename, FILES[0].contents, function(err) { | ||
Expect(err).to.equal(null); | ||
expect(err).to.equal(null); | ||
done(); | ||
@@ -80,3 +90,3 @@ }); | ||
RemoteFS.writeFile(FILES[1].in_filename, FILES[1].contents, function(err) { | ||
Expect(err).to.equal(null); | ||
expect(err).to.equal(null); | ||
done(); | ||
@@ -95,3 +105,3 @@ }) | ||
}, function(err, resp, body) { | ||
Expect(err).to.equal(null); | ||
expect(err).to.equal(null); | ||
done(); | ||
@@ -105,4 +115,4 @@ }) | ||
RemoteFS.readFile(file.out_filename, 'utf8', function(err, contents) { | ||
Expect(err).to.equal(null); | ||
Expect(contents).to.equal(file.contents); | ||
expect(err).to.equal(null); | ||
expect(contents).to.equal(file.contents); | ||
callback(); | ||
@@ -114,3 +124,37 @@ }); | ||
}) | ||
}); | ||
}); | ||
describe('nested modules', function() { | ||
it('should have top level function', function(done) { | ||
NestedModule.foo(function(err, foo) { | ||
expect(err).to.equal(null); | ||
expect(foo).to.equal('foo'); | ||
done(); | ||
}); | ||
}); | ||
it('should have nested functions', function(done) { | ||
NestedModule.nest.bar(function(err, bar) { | ||
expect(err).to.equal(null); | ||
expect(bar).to.equal('bar'); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
describe('errors', function() { | ||
it('should be able to be thrown', function(done) { | ||
ErrorModule.throwError(function(err) { | ||
expect(err).to.equal("Error: thrown"); | ||
done(); | ||
}); | ||
}); | ||
it('should be able to be called back', function(done) { | ||
ErrorModule.callbackError(function(err) { | ||
expect(err).to.deep.equal({message: "callback"}) | ||
done(); | ||
}) | ||
}) | ||
}); | ||
}) |
@@ -7,3 +7,3 @@ var FS = require('fs'); | ||
var SWAGGER_GOLDEN_FILE = __dirname + '/golden/petstore.swagger.json'; | ||
var BASE_URL = 'http://127.0.0.1:3000/api'; | ||
var BASE_URL = 'http://127.0.0.1:3333/api'; | ||
@@ -64,3 +64,3 @@ var USER_1 = {username: 'user1', password: 'jabberwocky'} | ||
before(function(done) { | ||
Petstore.listen(3000); | ||
Petstore.listen(3333); | ||
Petstore.dropAllEntries(done); | ||
@@ -67,0 +67,0 @@ }); |
58226
18
1642