fmbaucis
Version:
Build scalable REST APIs using the open source tools and standards you already know.
89 lines (86 loc) • 3.61 kB
JavaScript
// __Dependencies__
var es = require('event-stream');
var util = require('util');
var BaucisError = require('../../BaucisError');
// __Module Definition__
var decorator = module.exports = function (options, protect) {
var controller = this;
controller.query('post', function (request, response, next) {
var url = request.originalUrl || request.url;
var findBy = controller.findBy();
var pipeline = protect.pipeline();
var parser;
// Add trailing slash to URL if needed.
if (url.lastIndexOf('/') === (url.length - 1)) url = url.slice(0, url.length - 1);
// Set the status to 201 (Created).
response.status(201);
// Check if the body was parsed by some external middleware e.g. `express.json`.
// If so, create a stream from the POST'd document or documents.
if (request.body) {
pipeline(es.readArray([].concat(request.body)));
}
// Otherwise, stream and parse the request.
else {
parser = request.baucis.api.parser(request.get('content-type'));
if (!parser) return next(BaucisError.UnsupportedMediaType());
pipeline(request);
pipeline(parser);
}
// Create the stream context.
pipeline(function (incoming, callback) {
callback(null, { incoming: incoming, doc: null });
});
// Process the incoming document or documents.
pipeline(request.baucis.incoming());
// Map function to create a document from incoming JSON and update the context.
pipeline(function (context, callback) {
var transformed = { incoming: context.incoming };
var Model = controller.model();
var type = context.incoming.__t;
var Discriminator = type ? Model.discriminators[type] : undefined;
if (type && !Discriminator) {
callback(BaucisError.BadRequest("A document's type did not match any known discriminators for this resource"));
return;
}
// Create the document using either the model or child model.
if (type) transformed.doc = new Discriminator();
else transformed.doc = new Model();
// Transformation complete.
callback(null, transformed);
});
// Update the new Mongoose document with the incoming data.
pipeline(function (context, callback) {
context.doc.set(context.incoming);
callback(null, context);
});
// Save each document.
pipeline(function (context, callback) {
context.doc.save(function (error, doc) {
if (error) return callback(error);
callback(null, { incoming: context.incoming, doc: doc });
});
});
// Map the saved documents to document IDs.
pipeline(function (context, callback) {
callback(null, context.doc.get(findBy));
});
// Write the IDs to an array and process them.
var s = pipeline();
s.on('error', next);
s.pipe(es.writeArray(function (error, ids) {
if (error) return next(error);
// URL location of newly created document or documents.
var location;
// Set the conditions used to build `request.baucis.query`.
var conditions = request.baucis.conditions[findBy] = { $in: ids };
// Check for at least one document.
if (ids.length === 0) return next(BaucisError.BadRequest('The request body must contain at least one document'));
// Set the `Location` header if at least one document was sent.
if (ids.length === 1) location = url + '/' + ids[0];
else location = util.format('%s?conditions={ "%s": %s }', url, findBy, JSON.stringify(conditions));
response.set('Location', location);
next();
}));
s.resume();
});
};