Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

baucis

Package Overview
Dependencies
Maintainers
1
Versions
202
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

baucis - npm Package Compare versions

Comparing version 0.4.7 to 0.5.0

controller.js

94

index.js
// Dependencies
// ------------
var url = require('url');
var express = require('express');
var mongoose = require('mongoose');
var lingo = require('lingo');
var url = require('url');
var Controller = require('./Controller');
var exec = require('./middleware/exec');
var headers = require('./middleware/headers');
var configure = require('./middleware/configure');
var query = require('./middleware/query');
var send = require('./middleware/send');
var validation = require('./middleware/validation');
var documents = require('./middleware/documents');
// Private Members
// ---------------
var controllers = [];

@@ -26,2 +21,8 @@ // Module Definition

controllers.forEach(function (controller) {
var route = url.resolve('/', controller.get('plural'));
controller.initialize();
app.use(route, controller);
});
return app;

@@ -33,77 +34,8 @@ };

baucis.rest = function (options) {
if (!options.singular) throw new Error('Must provide the Mongoose schema name');
var controller = Controller(options);
var controller = express();
var basePathOption = options.basePath ? options.basePath.replace(/\/?$/, '/') : '/';
var basePath = url.resolve('/', basePathOption);
var basePathWithId = url.resolve(basePath, ':id');
var basePathWithOptionalId = url.resolve(basePath, ':id?');
controller.set('model', mongoose.model(options.singular));
controller.set('plural', options.plural || lingo.en.pluralize(options.singular));
controller.set('findBy', options.findBy || '_id');
controller.set('basePath', basePath);
controller.set('basePathWithId', basePathWithId);
controller.set('basePathWithOptionalId', basePathWithOptionalId);
Object.keys(options).forEach(function (key) {
controller.set(key, options[key]);
});
controller.use(express.json());
// Initialize baucis state
controller.all(basePathWithOptionalId, function (request, response, next) {
request.baucis = {};
next();
});
// Allow/Accept headers
controller.all(basePathWithId, headers.allow);
controller.all(basePathWithId, headers.accept);
controller.all(basePath, headers.allow);
controller.all(basePath, headers.accept);
if (options.configure) options.configure(controller);
// Set Link header if desired
if (options.relations === true) {
controller.head(basePathWithId, headers.link);
controller.get(basePathWithId, headers.link);
controller.post(basePathWithId, headers.link);
controller.put(basePathWithId, headers.link);
controller.head(basePath, headers.linkCollection);
controller.get(basePath, headers.linkCollection);
controller.post(basePath, headers.linkCollection);
controller.put(basePath, headers.linkCollection);
}
// Add all pre-query middleware
if (options.all) controller.all(basePathWithOptionalId, options.all);
if (options.head) controller.head(basePathWithOptionalId, options.head);
if (options.get) controller.get(basePathWithOptionalId, options.get);
if (options.post) controller.post(basePathWithOptionalId, options.post);
if (options.put) controller.put(basePathWithOptionalId, options.put);
if (options.del) controller.del(basePathWithOptionalId, options.del);
// Add routes for singular documents
if (options.head !== false) controller.head(basePathWithId, query.head, configure.controller, configure.query, exec.count, documents.lastModified, documents.send);
if (options.get !== false) controller.get(basePathWithId, query.get, configure.controller, configure.query, exec.exec, documents.lastModified, documents.send);
if (options.post !== false) controller.post(basePathWithId, query.post, configure.controller, configure.query, exec.exec, documents.lastModified, documents.send);
if (options.put !== false) controller.put(basePathWithId, query.put, configure.controller, configure.query, documents.lastModified, documents.send);
if (options.del !== false) controller.del(basePathWithId, query.del, configure.controller, configure.query, exec.exec, documents.lastModified, documents.send);
// Add routes for collections of documents
if (options.head !== false) controller.head(basePath, configure.conditions, query.headCollection, configure.controller, configure.query, exec.count, documents.lastModified, documents.send);
if (options.get !== false) controller.get(basePath, configure.conditions, query.getCollection, configure.controller, configure.query, exec.exec, documents.lastModified, documents.send);
if (options.post !== false) controller.post(basePath, query.postCollection, documents.lastModified, documents.send);
if (options.put !== false) controller.put(basePath, query.putCollection, configure.controller, configure.query, exec.exec, documents.lastModified, documents.send);
if (options.del !== false) controller.del(basePath, configure.conditions, query.delCollection, configure.controller, configure.query, exec.exec, documents.lastModified, documents.send);
// Publish unless told not to
if (options.publish !== false) app.use(url.resolve('/', controller.get('plural')), controller);
if (options.publish !== false) controllers.push(controller);
return controller;
};

@@ -12,100 +12,106 @@ // Private Members

var middleware = module.exports = {
// Retrieve header for the addressed document
head: function (request, response, next) {
var Model = request.app.get('model');
request.baucis.noBody = true;
request.baucis.query = Model.findOne(getFindCondition(request));
next();
},
// Retrieve documents matching conditions
headCollection: function (request, response, next) {
var Model = request.app.get('model');
request.baucis.noBody = true;
request.baucis.query = Model.find(request.baucis.conditions);
next();
},
// Retrive the addressed document
get: function (request, response, next) {
var Model = request.app.get('model');
request.baucis.query = Model.findOne(getFindCondition(request));
next();
},
// Retrieve documents matching conditions
getCollection: function (request, response, next) {
var Model = request.app.get('model');
request.baucis.query = Model.find(request.baucis.conditions);
next();
},
// Treat the addressed document as a collection, and push
// the addressed object to it
post: function (request, response, next) {
response.send(405); // method not allowed (as of yet unimplemented)
},
// Create a new document and return its ID
postCollection: function (request, response, next) {
var body = request.body;
var Model = request.app.get('model');
instance: {
// Retrieve header for the addressed document
head: function (request, response, next) {
var Model = request.app.get('model');
request.baucis.noBody = true;
request.baucis.query = Model.findOne(getFindCondition(request));
next();
},
// Retrive the addressed document
get: function (request, response, next) {
var Model = request.app.get('model');
request.baucis.query = Model.findOne(getFindCondition(request));
next();
},
// Treat the addressed document as a collection, and push
// the addressed object to it
post: function (request, response, next) {
response.send(405); // method not allowed (as of yet unimplemented)
},
// Replace the addressed document, or create it if it doesn't exist
put: function (request, response, next) {
var Model = request.app.get('model');
var id = request.params.id;
// Must be an object or array
if (!body || typeof body !== 'object') {
return next(new Error('Must supply a document or array to POST'));
}
if (request.body._id && id !== request.body._id) {
return next(new Error('ID mismatch'));
}
// Make it an array if it wasn't already
if (!Array.isArray(body)) body = [ body ];
// Can't send id for update, even if unchanged
delete request.body._id;
// No empty arrays
if (body.length === 0) return next(new Error('Array was empty.'));
Model.findOne(getFindCondition(request), function (error, doc) {
if (error) return next(error);
if (!doc) return next(new Error('No document with that ID was found'));
response.status(201);
doc.set(request.body);
Model.create(body, function (error) {
if (error) return next(error);
var documents = Array.prototype.slice.apply(arguments).slice(1);
request.baucis.documents = documents.length === 1 ? documents[0] : documents;
doc.save(function (error, savedDoc) {
if (error) return next(error);
request.baucis.documents = savedDoc;
next();
});
});
},
// Delete the addressed object
del: function (request, response, next) {
var Model = request.app.get('model');
request.baucis.query = Model.remove(getFindCondition(request));
next();
});
}
},
// Replace the addressed document, or create it if it doesn't exist
put: function (request, response, next) {
var Model = request.app.get('model');
var id = request.params.id;
collection: {
// Retrieve documents matching conditions
head: function (request, response, next) {
var Model = request.app.get('model');
request.baucis.noBody = true;
request.baucis.query = Model.find(request.baucis.conditions);
next();
},
// Retrieve documents matching conditions
get: function (request, response, next) {
var Model = request.app.get('model');
request.baucis.query = Model.find(request.baucis.conditions);
next();
},
// Create a new document and return its ID
post: function (request, response, next) {
var body = request.body;
var Model = request.app.get('model');
if (request.body._id && id !== request.body._id) {
return next(new Error('ID mismatch'));
}
// Must be an object or array
if (!body || typeof body !== 'object') {
return next(new Error('Must supply a document or array to POST'));
}
// Can't send id for update, even if unchanged
delete request.body._id;
// Make it an array if it wasn't already
if (!Array.isArray(body)) body = [ body ];
Model.findOne(getFindCondition(request), function (error, doc) {
if (error) return next(error);
if (!doc) return next(new Error('No document with that ID was found'));
// No empty arrays
if (body.length === 0) return next(new Error('Array was empty.'));
doc.set(request.body);
response.status(201);
doc.save(function (error, savedDoc) {
Model.create(body, function (error) {
if (error) return next(error);
request.baucis.documents = savedDoc;
var documents = Array.prototype.slice.apply(arguments).slice(1);
request.baucis.documents = documents.length === 1 ? documents[0] : documents;
next();
});
});
},
// Replace all docs with given docs ...
putCollection: function (request, response, next) {
response.send(405); // method not allowed (as of yet unimplemented)
},
// Delete the addressed object
del: function (request, response, next) {
var Model = request.app.get('model');
request.baucis.query = Model.remove(getFindCondition(request));
next();
},
// Delete all documents matching conditions
delCollection: function (request, response, next) {
var Model = request.app.get('model');
request.baucis.query = Model.remove(request.baucis.conditions);
next();
},
// Replace all docs with given docs ...
put: function (request, response, next) {
response.send(405); // method not allowed (as of yet unimplemented)
},
// Delete all documents matching conditions
del: function (request, response, next) {
var Model = request.app.get('model');
request.baucis.query = Model.remove(request.baucis.conditions);
next();
}
}
};
{
"name": "baucis",
"version": "0.4.7",
"version": "0.5.0",
"main": "index.js",

@@ -5,0 +5,0 @@ "scripts": {

@@ -1,2 +0,2 @@

baucis v0.4.7
baucis v0.5.0
===============

@@ -8,8 +8,4 @@

Those versions published to npm represent release versions. Those versions not published to npm are development releases.
Baucis uses [semver](http://semver.org).
Relase versions of baucis can be considered stable. Baucis uses [semver](http://semver.org).
Please report issues on GitHub if bugs are encountered.
![David Rjckaert III - Philemon and Baucis Giving Hospitality to Jupiter and Mercury](http://github.com/wprl/baucis/raw/master/david_rijckaert_iii-philemon_and_baucis.jpg "Hermes is like: 'Hey Baucis, don't kill that goose. And thanks for the REST.'")

@@ -104,22 +100,2 @@

Use plain old Connect/Express middleware, including pre-existing modules like `passport`. For example, set the `all` option to add middleware to be called before all the model's API routes.
baucis.rest({
singular: 'vegetable',
all: function (request, response, next) {
if (request.isAuthenticated()) return next();
return response.send(401);
}
});
Or, set some middleware for specific HTTP verbs or disable verbs completely:
baucis.rest({
singular: 'vegetable',
get: [middleware1, middleware2],
post: middleware3,
del: false,
put: false
});
`baucis.rest` returns an instance of the controller created to handle the schema's API routes.

@@ -132,14 +108,69 @@

var controller = baucis.rest({
singular: 'robot',
configure: function (controller) {
// Add middleware before all other rotues in the controller
controller.use(express.cookieParser());
}
singular: 'robot'
});
// Add middleware after default controller routes
// Add middleware before API routes
controller.use(function () { ... });
// Do other stuff...
controller.set('some option name', 'value');
controller.listen(3000);
Customize them with plain old Express/Connect middleware, including pre-existing modules like `passport`. Middleware can be registered like so:
controller.request(function (request, response, next) {
if (request.isAuthenticated()) return next();
return response.send(401);
});
Baucis adds middleware registration functions for three stages of the request cycle:
| Name | Description |
| ---- | ----------- |
| request | This stage of middleware will be called after baucis applies defaults based on the request, but before the Mongoose query is generated |
| query | This stage of middleware will be called after baucis applies defaults to the Mongoose query object, but before the documents or count is retrieved from the databased. The query can be accessed in your custom middleware via `request.baucis.query`. |
| documents | This stage of middleware will be called after baucis executes the query, but before the documents or count are sent in the response. The documents/count can be accessed in your custom middleware via `request.baucis.documents`. |
Each of these functions has three forms:
The first form is the most specific. The first argument lets you specify whether the middleware applies to document instances (paths like `/foos/:id`) or to collection requests (paths like `/foos`). The second argument is a space-delimted list of HTTP verbs that the middleware should be applied to. The third argument is the middleware function to add or an array of middleware functions.
controller.request('instance', 'head get del', middleware);
controller.request('collection', 'post', middleware);
To add middleware that applies to both document instances and collections, the first argument is omitted:
controller.query('post put', function (request, response, next) {
// do something with request.baucis.query
next();
});
To apply middleware to all API routes, just pass the function or array:
controller.request(function (request, response, next) {
if (request.isAuthenticated()) return next();
return response.send(401);
});
controller.documents(function (request, response, next) {
var ok = true;
if (typeof request.baucis.documents === 'number') return next();
[].concat(request.baucis.documents).forEach(function (doc) {
if (!ok) return;
if (doc.owner !== request.user.id) {
ok = false;
next(new Error('User does not own this.'));
}
});
if (ok) next();
});
To disable verbs completely:
baucis.rest({
singular: 'vegetable',
del: false,
put: false
});
Controller Options

@@ -154,33 +185,34 @@ ------------------

| publish | Set to `false` to not publish the controller's endpoints when `baucis()` is called. |
| select | Select or deselect fields for all queries |
| select | Select or deselect fields for all queries e.g. `'foo +bar -password'` |
| findBy | Use another field besides `_id` for entity queries. |
| lastModified | Set the `Last-Modified` HTTP header useing the given field. Currently this field must be a `Date`. |
| restrict | Alter the query based on request parameters. |
| configure | Add middleware to the controller before generated paths. |
An example of embedding a controller within another controller
var subcontroller = baucis.rest({
singular: 'bar',
basePath: '/:fooId/bars',
publish: false,
select: 'foo +bar -password',
findBy: 'baz',
lastModified: 'modifiedDate',
restrict: function (query, request) {
// Only retrieve bars that are children of the given foo
query.where('parent', request.params.fooId);
}
publish: false
});
subcontroller.query(function (request, response, next) {
// Only retrieve bars that are children of the given foo
request.baucis.query.where('parent', request.params.fooId);
next();
});
// Didn't publish, so have to manually initialize
subcontroller.initialize();
var controller = baucis.rest({
singular: 'foo',
configure: function (controller) {
// Embed the subcontroller at /foos/:fooId/bars
controller.use(subcontroller);
singular: 'foo'
});
// Embed arbitrary middleware at /foos/qux
controller.use('/qux', function (request, response, next) {
// Do something cool…
next();
});
}
// Embed the subcontroller at /foos/:fooId/bars
controller.use(subcontroller);
// Embed arbitrary middleware at /foos/qux
controller.use('/qux', function (request, response, next) {
// Do something cool…
next();
});

@@ -187,0 +219,0 @@

controller.request('put', [ midleware, ... ]);
controller.query('get', [ ... ]);
controller.document('delete', [ ... ]);
controller.doc('delete', [ ... ]);
controller.queryDocument
controller.queryCollection
controller.query('document', 'get put', [ ... ]);
controller.query('collection', 'get put', [ ... ]);
controller.document([ ... ]); // all verbs
controller.document('put get delete', [ ... ]); // some verbs
controller.on('baucis:document', 'post put', [ ... ]);
controller.on('query', 'post put', [ ... ]);
controller.register('baucis:document', 'post put', [ ... ]);
controller.register('query', 'post put', [ ... ]);
controller.baucis('query', 'post put', [ ... ]);
var controller = baucis.rest({
request: {
all: middleware1
},
query: {
post: [],
put: [],
get: [],
delete: [],
head: []
},
document: {
post: [],
put: [],
get: []
}
})
// TODO make baucis.rest call controllers' initialze() fn if it hasn't been called already by dev?
// ---

@@ -9,0 +42,0 @@

@@ -7,2 +7,3 @@ var mongoose = require('mongoose');

var server;
var controller;

@@ -34,11 +35,12 @@ var fixture = module.exports = {

baucis.rest({
controller = baucis.rest({
singular: 'vegetable',
lastModified: 'lastModified',
all: function (request, response, next) {
if (request.query.block === "true") return response.send(401);
next();
}
lastModified: 'lastModified'
});
controller.request(function (request, response, next) {
if (request.query.block === 'true') return response.send(401);
next();
});
app = express();

@@ -45,0 +47,0 @@ app.use('/api/v1', baucis());

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc