sqsd-handler
Version:
Express middleware for handling SQS messages in AWS ElasticBeanstalk worker-tier apps.
157 lines (142 loc) • 6.11 kB
JavaScript
(function (sqsdHandler) {
"use strict";
var _ = require("underscore");
var express = require("express");
var Promise = require("bluebird");
function create(defaultHandlerFunc) {
if (defaultHandlerFunc && (typeof defaultHandlerFunc !== "function")) {
throw new Error("The argument to handle() must be a function");
}
function messageProcessor (req, res, next) {
/*jshint validthis: true */
var config = this;
var message = req.msg;
var handlerFunc = config.defaultHandler;
if (message.taskName && config.tasks[message.taskName]) {
var taskHandlerFunc = config.tasks[message.taskName];
handlerFunc = taskHandlerFunc;
}
if (message.sns && config.snsHandler) {
handlerFunc = config.snsHandler;
}
if (handlerFunc == config.defaultHandler && config.filterHandlers.length > 0) {
config.filterHandlers.forEach(function (handler) {
if (_.isMatch(message.payload, handler.filter)) {
handlerFunc = handler.handlerFunc;
}
});
}
var handlePromise = Promise
.try(function () {
message.response = message.response || {};
message.response.status = message.response.status || 200;
message.response.text = message.response.text || "";
return handlerFunc(message) || message;
})
.catch(function (exception) {
message.response.status = 500;
if (config.errorHandler) {
var errorHandlerPromise = Promise.try(function () {
return config.errorHandler(message, exception) || message;
});
return errorHandlerPromise;
}
})
.finally(function () {
var responseStatus = message.response.status || 200;
var responseText = message.response.text.toString();
res.status(responseStatus).send(responseText);
if (config.logHandler) {
return config.logHandler(message);
}
});
}
function RouteConfig (defaultHandler) {
this.tasks = {};
this.filterHandlers = [];
this.snsHandler = null;
this.errorHandler = null;
this.logHandler = null;
this.defaultHandler = defaultHandler || function () {};
}
var routeConfig = new RouteConfig(defaultHandlerFunc);
var routeConfigMessageProcessor = messageProcessor.bind(routeConfig);
var router = express.Router();
var messageRouter = router.post("/", sqsdMessageParser, snsMessageParser, routeConfigMessageProcessor);
messageRouter.task = function (taskName, taskHandlerFunc) {
var config = this;
config.tasks[taskName] = taskHandlerFunc;
}.bind(routeConfig);
messageRouter.filter = function (filterObj, handlerFunc) {
var config = this;
config.filterHandlers.push({"filter": filterObj, "handlerFunc": handlerFunc});
}.bind(routeConfig);
messageRouter.sns = function (handlerFunc) {
var config = this;
config.snsHandler = handlerFunc;
}.bind(routeConfig);
messageRouter.error = function (errorHandlerFunc) {
var config = this;
config.errorHandler = errorHandlerFunc;
}.bind(routeConfig);
messageRouter.log = function (logHandlerFunc) {
var config = this;
config.logHandler = logHandlerFunc;
}.bind(routeConfig);
return messageRouter;
}
function sqsdMessageParser(req, res, next){
var userAgent = req.headers["user-agent"];
req.setEncoding("utf8");
req.msg = {
"body": "",
"msgid": req.get("x-aws-sqsd-msgid"),
"receiveCount": req.get("x-aws-sqsd-receive-count"),
"firstReceivedAt": req.get("x-aws-sqsd-first-received-at"),
"sentAt": req.get("x-aws-sqsd-sent-at"),
"scheduledAt": req.get("x-aws-sqsd-scheduled-at"),
"path": req.get("x-aws-sqsd-path"),
"senderId": req.get("x-aws-sqsd-sender-id"),
"queue": req.get("x-aws-sqsd-queue"),
"taskName": req.get("x-aws-sqsd-taskname"),
"userAgent": userAgent,
"response": {}
};
_.each(req.headers, function (value, key) {
if (key.indexOf("x-aws-sqsd-attr-") != -1) {
req.msg.attributes = req.msg.attributes || {};
var attribName = key.substr("x-aws-sqsd-attr-".length);
req.msg.attributes[attribName] = value;
}
});
req.on("data", function (chunk) {
req.msg.body += chunk;
});
req.on("end", function () {
if (req.is("application/json") && req.msg.body) {
try {
req.msg.payload = JSON.parse(req.msg.body);
next();
} catch (jsonParseError) {
next(jsonParseError);
}
} else {
next();
}
});
}
function snsMessageParser(req, res, next) {
var msg = req.msg;
if (msg && msg.payload && msg.payload.Type && msg.payload.Type === "Notification" && msg.payload.Message) {
var messageText = msg.payload.Message;
if (messageText) {
var sns = JSON.parse(messageText);
msg.sns = sns;
}
}
next();
}
sqsdHandler.sqsdMessageParser = sqsdMessageParser;
sqsdHandler.snsMessageParser = snsMessageParser;
sqsdHandler.create = create;
})(module.exports);