diff --git a/.babelrc b/.babelrc index 76e3f90..21ec0ae 100644 --- a/.babelrc +++ b/.babelrc @@ -1,3 +1,3 @@ { - "plugins": ["transform-flow-comments"] + "plugins": ["transform-flow-comments", "transform-object-rest-spread"] } diff --git a/CHANGELOG.md b/CHANGELOG.md index a50a9db..4c30079 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +# 0.13.16 +* Explicitly handle too large kraken notification payloads +* Adds devMode utility +* Turn on flow in index.js files + # 0.13.15 * Bump hull-client version to 1.2.2 * Adds support for Account anonymous_id claim diff --git a/package.json b/package.json index 762e2b4..96a9be6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hull", - "version": "0.13.15", + "version": "0.13.16", "description": "A Node.js client for hull.io", "main": "lib", "repository": { @@ -35,6 +35,7 @@ "dependencies": { "JSONStream": "^1.1.2", "aws-sdk": "^2.81.0", + "babel-polyfill": "^6.26.0", "basic-auth": "^1.1.0", "batch-stream": "^0.1.3", "bluebird": "^3.4.7", @@ -54,26 +55,32 @@ "kue": "^0.11.5", "kue-ui": "^0.1.0", "lodash": "^4.17.5", - "newrelic": "^2.4.1", + "newrelic": "^4.1.4", "passport": "^0.3.2", + "progress-bar-webpack-plugin": "^1.11.0", "promise-streams": "^1.0.1", "raven": "^2.4.2", "raw-body": "^2.1.7", + "react-hot-loader": "^4.2.0", "request": "^2.72.0", "sns-validator": "^0.3.0", "sqs-consumer": "^3.6.1", "supply": "0.0.4", - "urijs": "^1.18.7" + "urijs": "^1.18.7", + "webpack": "^3.12.0", + "webpack-dev-middleware": "^2.0.6", + "webpack-hot-middleware": "^2.22.2" }, "peerDependencies": { - "express": "^4.16.2", - "newrelic": "^2.4.1" + "express": "^4.16.3", + "newrelic": "^4.1.4" }, "devDependencies": { "babel": "^6.5.2", "babel-cli": "^6.24.1", "babel-eslint": "^7.1.1", "babel-plugin-transform-flow-comments": "^6.22.0", + "babel-plugin-transform-object-rest-spread": "^6.26.0", "babel-register": "^6.9.0", "chai": "^3.5.0", "chai-http": "^3.0.0", @@ -90,12 +97,13 @@ "mkdirp": "^0.5.1", "mocha": "^3.0.0", "nock": "^9.2.3", - "node-mocks-http": "^1.6.1", + "node-mocks-http": "^1.7.0", "nyc": "^11.0.3", "rimraf": "^2.6.0", "sinon": "^2.2.0", "sinon-chai": "^2.10.0", "superagent": "^3.8.2", + "supertest": "^3.1.0", "updtr": "^1.0.0" }, "nodeBoilerplateOptions": { diff --git a/src/errors/index.js b/src/errors/index.js index 346c183..4ebc91b 100644 --- a/src/errors/index.js +++ b/src/errors/index.js @@ -1,3 +1,4 @@ +// @flow /* eslint-disable global-require */ /** diff --git a/src/utils/dev-mode.js b/src/utils/dev-mode.js new file mode 100644 index 0000000..e7ab6a8 --- /dev/null +++ b/src/utils/dev-mode.js @@ -0,0 +1,59 @@ +// @flow +import type { $Application } from "express"; + +const _ = require("lodash"); +const webpack = require("webpack"); +const webpackDevMiddleware = require("webpack-dev-middleware"); +const webpackHotMiddleware = require("webpack-hot-middleware"); +const ProgressBarPlugin = require("progress-bar-webpack-plugin"); + +function devMode(app: $Application, config: Object) { + const entry = _.reduce( + config.entry, + (m: Object, v, k: string) => { + m[k] = [ + require.resolve("babel-polyfill"), + require.resolve("react-hot-loader/patch"), + require.resolve("webpack-hot-middleware/client"), + v + ]; + return m; + }, + {} + ); + + const plugins = [ + new webpack.HotModuleReplacementPlugin(), + ...config.plugins, + new webpack.NamedModulesPlugin(), + new ProgressBarPlugin({ clear: false }), + new webpack.NoEmitOnErrorsPlugin() + ]; + + const compiler = webpack({ ...config, entry, plugins }); + + app.use( + webpackDevMiddleware(compiler, { + hot: true, + quiet: false, + overlay: false, + noInfo: false, + lazy: false, + clientLogLevel: "none", + watchContentBase: true, + stats: { colors: true }, + watchOptions: { + ignored: /node_modules/ + }, + historyApiFallback: { + disableDotRule: true + }, + + headers: { "Access-Control-Allow-Origin": "http://localhost" }, + publicPath: config.output.publicPath + }) + ); + app.use(webpackHotMiddleware(compiler)); +} + +module.exports = devMode; diff --git a/src/utils/index.js b/src/utils/index.js index 08654a1..6265988 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -1,3 +1,4 @@ +// @flow /** * General utilities * @namespace Utils @@ -24,5 +25,7 @@ module.exports.SmartNotifierResponse = require("./smart-notifier-response"); module.exports.PromiseReuser = require("./promise-reuser"); module.exports.superagentUrlTemplatePlugin = require("./superagent-url-template-plugin"); -module.exports.superagentInstrumentationPlugin = require("././superagent-intrumentation-plugin.js"); -module.exports.superagentErrorPlugin = require("././superagent-error-plugin.js"); +module.exports.superagentInstrumentationPlugin = require("./superagent-intrumentation-plugin.js"); +module.exports.superagentErrorPlugin = require("./superagent-error-plugin.js"); + +module.exports.devMode = require("./dev-mode"); diff --git a/src/utils/smart-notifier-middleware.js b/src/utils/smart-notifier-middleware.js index 9b3b75b..604cc35 100644 --- a/src/utils/smart-notifier-middleware.js +++ b/src/utils/smart-notifier-middleware.js @@ -37,7 +37,15 @@ module.exports = function smartNotifierMiddlewareFactory({ skipSignatureValidati } } - return bodyParser.json({ limit: "10mb" })(req, res, () => { + return bodyParser.json({ limit: "10mb" })(req, res, (err) => { + if (err !== undefined && err.type === "entity.too.large") { + Client.logger.error("connector.smartNotifierHandler.error", { error: err.toString() }); + return next(new SmartNotifierError("ENTITY_TOO_LARGE", "Payload size bigger than 10mb", err.statusCode, { + type: "retry", + size: 1 + })); + } + Client.logger.debug("connector.smartNotifierHandler", _.pick(req.body, "channel", "notification_id")); if (!smartNotifierValidator.validatePayload()) { diff --git a/test/unit/utils/smart-notifier-middleware-test.js b/test/unit/utils/smart-notifier-middleware-test.js index 3e488de..da31014 100644 --- a/test/unit/utils/smart-notifier-middleware-test.js +++ b/test/unit/utils/smart-notifier-middleware-test.js @@ -1,6 +1,7 @@ /* global describe, it */ -const { expect, should } = require("chai"); -const sinon = require("sinon"); +const { expect } = require("chai"); +const http = require("http"); +const request = require("supertest"); const smartNotifierMiddleware = require("../../../src/utils/smart-notifier-middleware"); @@ -10,4 +11,41 @@ describe("SmartNotifierMiddleware", () => { const testInstance = new smartNotifierMiddleware({}); expect(typeof testInstance).to.equal("function"); }); + + it("should handle notifications exceeding the json size limit", (done) => { + const testInstance = new smartNotifierMiddleware({ skipSignatureValidation: true }); + const server = http.createServer((req) => { + testInstance(req, {}, (err) => { + expect(err.code).to.equal("ENTITY_TOO_LARGE"); + expect(err.statusCode).to.equal(413); + expect(err.flowControl).to.eql({ + type: "retry", + size: 1 + }); + done(); + }); + }); + request(server) + .post("/") + .set("Content-Type", "application/json") + .set({ + "content-type": "application/json", + "x-hull-smart-notifier": "yes", + "x-hull-smart-notifier-signature": "singature", + "x-hull-smart-notifier-signature-version": "v1", + "x-hull-smart-notifier-signature-public-key-url": "url" + }) + .send({ + configuration: { + id: "12312312312" + }, + connector: {}, + messages: [{ + user: { + super_long_trait: String(".").repeat(10485760) // 10mb string + } + }] + }) + .end(() => {}); + }); });