From dec045b0a38cce8acd114c6c510e8db239cc574e Mon Sep 17 00:00:00 2001 From: Piotr Leszczynski Date: Thu, 29 Aug 2019 10:20:58 +0200 Subject: [PATCH 01/62] Chore(Support multiply types in a single topics): Changing producer to use type-topicName as a subject for avro schema --- lib/kafka-producer.js | 19 +- package-lock.json | 2358 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 2366 insertions(+), 11 deletions(-) create mode 100644 package-lock.json diff --git a/lib/kafka-producer.js b/lib/kafka-producer.js index 397b6d2..54a96e4 100644 --- a/lib/kafka-producer.js +++ b/lib/kafka-producer.js @@ -70,23 +70,22 @@ Producer.prototype.getProducer = Promise.method(function (opts, topts) { * @private */ Producer.prototype._serializeType = function (topicName, isKey, data) { - var schema = isKey === false - ? this.sr.valueSchemas[topicName] - : this.sr.keySchemas[topicName]; + // implementing io.confluent.kafka.serializers.subject.TopicRecordNameStrategy + const subject = `${data.constructor.name}-${topicName}`; - var schemaType = isKey === false - ? 'value' - : 'key'; + const schema = isKey === false + ? this.sr.valueSchemas[subject] + : this.sr.keySchemas[subject]; if (!schema) { - log.warn('_produceWrapper() :: Warning, did not find topic on SR for ' + schemaType + ' schema:', + log.warn(`_produceWrapper() :: Warning, did not find topic on SR for ${subject} schema:`, topicName); return (typeof data === 'object') ? new Buffer(JSON.stringify(data)) : new Buffer(data); } else { - var schemaId = this.sr.schemaIds[topicName + '-' + schemaType]; + const schemaId = this.sr.schemaIds[subject]; return this.serialize(schema, schemaId, data); } @@ -128,7 +127,5 @@ Producer.prototype._produceWrapper = function (producerInstance, topicName, * @private */ Producer.prototype.serialize = function (type, schemaId, value) { - var bufValue = magicByte.toMessageBuffer(value, type, schemaId); - - return bufValue; + return magicByte.toMessageBuffer(value, type, schemaId); }; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..ee6ef21 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2358 @@ +{ + "name": "kafka-avro", + "version": "1.2.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@sinonjs/commons": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.6.0.tgz", + "integrity": "sha512-w4/WHG7C4WWFyE5geCieFJF6MZkbW4VAriol5KlmQXpAQdxvV0p26sqNZOW6Qyw6Y0l9K4g+cHvvczR2sEEpqg==", + "dev": true, + "requires": { + "type-detect": "4.0.8" + }, + "dependencies": { + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true + } + } + }, + "@sinonjs/formatio": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-2.0.0.tgz", + "integrity": "sha512-ls6CAMA6/5gG+O/IdsBcblvnd8qcO/l1TYoNeAzp3wcISOxlPXQEus0mLcdwazEkWjaBdaJ3TaxmNgCLWwvWzg==", + "dev": true, + "requires": { + "samsam": "1.3.0" + } + }, + "@sinonjs/samsam": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-3.3.3.tgz", + "integrity": "sha512-bKCMKZvWIjYD0BLGnNrxVuw4dkWCYsLqFOUWw8VgKF/+5Y+mE7LfHWPIYoDXowH+3a9LsWDMo0uAP8YDosPvHQ==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.3.0", + "array-from": "^2.1.1", + "lodash": "^4.17.15" + } + }, + "@sinonjs/text-encoding": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz", + "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", + "dev": true + }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true + }, + "acorn": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", + "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==", + "dev": true + }, + "acorn-jsx": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", + "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", + "dev": true, + "requires": { + "acorn": "^3.0.4" + }, + "dependencies": { + "acorn": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", + "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", + "dev": true + } + } + }, + "ajv": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", + "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", + "dev": true, + "requires": { + "co": "^4.6.0", + "json-stable-stringify": "^1.0.1" + } + }, + "ajv-keywords": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-1.5.1.tgz", + "integrity": "sha1-MU3QpLM2j609/NxU7eYXG4htrzw=", + "dev": true + }, + "ansi-escapes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz", + "integrity": "sha1-06ioOzGapneTZisT52HHkRQiMG4=", + "dev": true + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "ansicolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.2.1.tgz", + "integrity": "sha1-vgiVmQl7dKXJxKhKDNvNtivYeu8=" + }, + "ansistyles": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/ansistyles/-/ansistyles-0.1.3.tgz", + "integrity": "sha1-XeYEFb2gcbs3EnhUyGT0GyMlRTk=" + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", + "dev": true + }, + "array-from": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/array-from/-/array-from-2.1.1.tgz", + "integrity": "sha1-z+nYwmYoudxa7MYqn12PHzUsEZU=", + "dev": true + }, + "assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true + }, + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", + "dev": true + }, + "avsc": { + "version": "5.4.13", + "resolved": "https://registry.npmjs.org/avsc/-/avsc-5.4.13.tgz", + "integrity": "sha512-OLS+OPfvBIJA+Vn4JpjDiMt4UNvw0v54TBKp3oR0YvnDyjBqAdm3V2t/SfdUk6rwUZxBPDoykOG59lXMxNllbg==" + }, + "axios": { + "version": "0.15.3", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.15.3.tgz", + "integrity": "sha1-LJ1jiy4ZGgjqHWzJiOrda6W9wFM=", + "requires": { + "follow-redirects": "1.0.0" + } + }, + "babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "esutils": "^2.0.2", + "js-tokens": "^3.0.2" + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "requires": { + "file-uri-to-path": "1.0.0" + } + }, + "bluebird": { + "version": "3.5.5", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.5.tgz", + "integrity": "sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "bunyan": { + "version": "1.8.12", + "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.12.tgz", + "integrity": "sha1-8VDw9nSKvdcq6uhPBEA74u8RN5c=", + "requires": { + "dtrace-provider": "~0.8", + "moment": "^2.10.6", + "mv": "~2", + "safe-json-stringify": "~1" + } + }, + "bunyan-format": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/bunyan-format/-/bunyan-format-0.2.1.tgz", + "integrity": "sha1-pLOw2ABwqGUnlBcmnj8A/wL7y0c=", + "requires": { + "ansicolors": "~0.2.1", + "ansistyles": "~0.1.1", + "xtend": "~2.1.1" + } + }, + "caller-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", + "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", + "dev": true, + "requires": { + "callsites": "^0.2.0" + } + }, + "callsites": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", + "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", + "dev": true + }, + "camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", + "dev": true + }, + "camelcase-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", + "dev": true, + "requires": { + "camelcase": "^2.0.0", + "map-obj": "^1.0.0" + } + }, + "chai": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-3.5.0.tgz", + "integrity": "sha1-TQJjewZ/6Vi9v906QOxW/vc3Mkc=", + "dev": true, + "requires": { + "assertion-error": "^1.0.1", + "deep-eql": "^0.1.3", + "type-detect": "^1.0.0" + } + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "cip": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cip/-/cip-1.0.1.tgz", + "integrity": "sha1-FzAzSiIvhWXgl/YMM5BxiUsPva8=" + }, + "circular-json": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", + "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", + "dev": true + }, + "cli-cursor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", + "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", + "dev": true, + "requires": { + "restore-cursor": "^1.0.1" + } + }, + "cli-width": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", + "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", + "dev": true + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true + }, + "coffeescript": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/coffeescript/-/coffeescript-1.10.0.tgz", + "integrity": "sha1-56qDAZF+9iGzXYo580jc3R234z4=", + "dev": true + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "colors": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", + "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", + "dev": true + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", + "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", + "dev": true + }, + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "cookiejar": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.0.6.tgz", + "integrity": "sha1-Cr81atANHFohnYjURRgEbdAmrP4=", + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "dev": true, + "requires": { + "array-find-index": "^1.0.1" + } + }, + "d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "dev": true, + "requires": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, + "dateformat": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz", + "integrity": "sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk=", + "dev": true, + "requires": { + "get-stdin": "^4.0.1", + "meow": "^3.3.0" + } + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "deep-eql": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz", + "integrity": "sha1-71WKyrjeJSBs1xOQbXTlaTDrafI=", + "dev": true, + "requires": { + "type-detect": "0.1.1" + }, + "dependencies": { + "type-detect": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz", + "integrity": "sha1-C6XsKohWQORw6k6FBZcZANrFiCI=", + "dev": true + } + } + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true + }, + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true + }, + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "dtrace-provider": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.8.tgz", + "integrity": "sha512-b7Z7cNtHPhH9EJhNNbbeqTcXB8LGFFZhq1PGgEvpeHlzd36bhbdTWoE/Ba/YguqpBSlAPKnARWhVlhunCMwfxg==", + "optional": true, + "requires": { + "nan": "^2.14.0" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es5-ext": { + "version": "0.10.50", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.50.tgz", + "integrity": "sha512-KMzZTPBkeQV/JcSQhI5/z6d9VWJ3EnQ194USTUwIYZ2ZbpN8+SGXQKt1h68EX44+qt+Fzr8DO17vnxrw7c3agw==", + "dev": true, + "requires": { + "es6-iterator": "~2.0.3", + "es6-symbol": "~3.1.1", + "next-tick": "^1.0.0" + } + }, + "es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "es6-map": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.5.tgz", + "integrity": "sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA=", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "~0.10.14", + "es6-iterator": "~2.0.1", + "es6-set": "~0.1.5", + "es6-symbol": "~3.1.1", + "event-emitter": "~0.3.5" + } + }, + "es6-set": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz", + "integrity": "sha1-0rPsXU2ADO2BjbU40ol02wpzzLE=", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "~0.10.14", + "es6-iterator": "~2.0.1", + "es6-symbol": "3.1.1", + "event-emitter": "~0.3.5" + } + }, + "es6-symbol": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", + "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, + "es6-weak-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", + "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "^0.10.46", + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.1" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "escope": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/escope/-/escope-3.6.0.tgz", + "integrity": "sha1-4Bl16BJ4GhY6ba392AOY3GTIicM=", + "dev": true, + "requires": { + "es6-map": "^0.1.3", + "es6-weak-map": "^2.0.1", + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "eslint": { + "version": "3.19.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-3.19.0.tgz", + "integrity": "sha1-yPxiAcf0DdCJQbh8CFdnOGpnmsw=", + "dev": true, + "requires": { + "babel-code-frame": "^6.16.0", + "chalk": "^1.1.3", + "concat-stream": "^1.5.2", + "debug": "^2.1.1", + "doctrine": "^2.0.0", + "escope": "^3.6.0", + "espree": "^3.4.0", + "esquery": "^1.0.0", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "file-entry-cache": "^2.0.0", + "glob": "^7.0.3", + "globals": "^9.14.0", + "ignore": "^3.2.0", + "imurmurhash": "^0.1.4", + "inquirer": "^0.12.0", + "is-my-json-valid": "^2.10.0", + "is-resolvable": "^1.0.0", + "js-yaml": "^3.5.1", + "json-stable-stringify": "^1.0.0", + "levn": "^0.3.0", + "lodash": "^4.0.0", + "mkdirp": "^0.5.0", + "natural-compare": "^1.4.0", + "optionator": "^0.8.2", + "path-is-inside": "^1.0.1", + "pluralize": "^1.2.1", + "progress": "^1.1.8", + "require-uncached": "^1.0.2", + "shelljs": "^0.7.5", + "strip-bom": "^3.0.0", + "strip-json-comments": "~2.0.1", + "table": "^3.7.8", + "text-table": "~0.2.0", + "user-home": "^2.0.0" + }, + "dependencies": { + "glob": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", + "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } + } + }, + "espree": { + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.4.tgz", + "integrity": "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==", + "dev": true, + "requires": { + "acorn": "^5.5.0", + "acorn-jsx": "^3.0.0" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esquery": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", + "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", + "dev": true, + "requires": { + "estraverse": "^4.0.0" + } + }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "dev": true, + "requires": { + "estraverse": "^4.1.0" + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, + "eventemitter2": { + "version": "0.4.14", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz", + "integrity": "sha1-j2G3XN4BKy6esoTUVFWDtWQ7Yas=", + "dev": true + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "dev": true + }, + "exit-hook": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz", + "integrity": "sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g=", + "dev": true + }, + "extend": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.0.tgz", + "integrity": "sha1-WkdDU7nzNT3dgXbf03uRyDpG8dQ=", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "figures": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", + "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5", + "object-assign": "^4.1.0" + } + }, + "file-entry-cache": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", + "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", + "dev": true, + "requires": { + "flat-cache": "^1.2.1", + "object-assign": "^4.0.1" + } + }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" + }, + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "findup-sync": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.3.0.tgz", + "integrity": "sha1-N5MKpdgWt3fANEXhlmzGeQpMCxY=", + "dev": true, + "requires": { + "glob": "~5.0.0" + }, + "dependencies": { + "glob": { + "version": "5.0.15", + "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", + "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", + "dev": true, + "requires": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } + } + }, + "flat-cache": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.4.tgz", + "integrity": "sha512-VwyB3Lkgacfik2vhqR4uv2rvebqmDvFu4jlN/C1RzWoJEo8I7z4Q404oiqYCkq41mni8EzQnm95emU9seckwtg==", + "dev": true, + "requires": { + "circular-json": "^0.3.1", + "graceful-fs": "^4.1.2", + "rimraf": "~2.6.2", + "write": "^0.2.1" + }, + "dependencies": { + "glob": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", + "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "follow-redirects": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.0.0.tgz", + "integrity": "sha1-jjQpjL0uF28lTv/sdaHHjMhJ/Tc=", + "requires": { + "debug": "^2.2.0" + } + }, + "form-data": { + "version": "1.0.0-rc3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-1.0.0-rc3.tgz", + "integrity": "sha1-01vGLn+8KTeuePlIqqDTjZBgdXc=", + "dev": true, + "requires": { + "async": "^1.4.0", + "combined-stream": "^1.0.5", + "mime-types": "^2.1.3" + } + }, + "formidable": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.0.16.tgz", + "integrity": "sha1-SRbP38TL7QILJXpqlQWpqzjCzQ4=", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "generate-function": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", + "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", + "dev": true, + "requires": { + "is-property": "^1.0.2" + } + }, + "generate-object-property": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", + "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=", + "dev": true, + "requires": { + "is-property": "^1.0.0" + } + }, + "get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", + "dev": true + }, + "getobject": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/getobject/-/getobject-0.1.0.tgz", + "integrity": "sha1-BHpEl4n6Fg0Bj1SG7ZEyC27HiFw=", + "dev": true + }, + "glob": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", + "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", + "optional": true, + "requires": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "globals": { + "version": "9.18.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", + "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", + "dev": true + }, + "graceful-fs": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz", + "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==", + "dev": true + }, + "growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true + }, + "grunt": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grunt/-/grunt-1.0.4.tgz", + "integrity": "sha512-PYsMOrOC+MsdGEkFVwMaMyc6Ob7pKmq+deg1Sjr+vvMWp35sztfwKE7qoN51V+UEtHsyNuMcGdgMLFkBHvMxHQ==", + "dev": true, + "requires": { + "coffeescript": "~1.10.0", + "dateformat": "~1.0.12", + "eventemitter2": "~0.4.13", + "exit": "~0.1.1", + "findup-sync": "~0.3.0", + "glob": "~7.0.0", + "grunt-cli": "~1.2.0", + "grunt-known-options": "~1.1.0", + "grunt-legacy-log": "~2.0.0", + "grunt-legacy-util": "~1.1.1", + "iconv-lite": "~0.4.13", + "js-yaml": "~3.13.0", + "minimatch": "~3.0.2", + "mkdirp": "~0.5.1", + "nopt": "~3.0.6", + "path-is-absolute": "~1.0.0", + "rimraf": "~2.6.2" + }, + "dependencies": { + "glob": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.0.6.tgz", + "integrity": "sha1-IRuvr0nlJbjNkyYNFKsTYVKz9Xo=", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.2", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "grunt-cli": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/grunt-cli/-/grunt-cli-1.2.0.tgz", + "integrity": "sha1-VisRnrsGndtGSs4oRVAb6Xs1tqg=", + "dev": true, + "requires": { + "findup-sync": "~0.3.0", + "grunt-known-options": "~1.1.0", + "nopt": "~3.0.6", + "resolve": "~1.1.0" + } + }, + "resolve": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", + "dev": true + }, + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + }, + "dependencies": { + "glob": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", + "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } + } + } + } + }, + "grunt-known-options": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/grunt-known-options/-/grunt-known-options-1.1.1.tgz", + "integrity": "sha512-cHwsLqoighpu7TuYj5RonnEuxGVFnztcUqTqp5rXFGYL4OuPFofwC4Ycg7n9fYwvK6F5WbYgeVOwph9Crs2fsQ==", + "dev": true + }, + "grunt-legacy-log": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-2.0.0.tgz", + "integrity": "sha512-1m3+5QvDYfR1ltr8hjiaiNjddxGdQWcH0rw1iKKiQnF0+xtgTazirSTGu68RchPyh1OBng1bBUjLmX8q9NpoCw==", + "dev": true, + "requires": { + "colors": "~1.1.2", + "grunt-legacy-log-utils": "~2.0.0", + "hooker": "~0.2.3", + "lodash": "~4.17.5" + } + }, + "grunt-legacy-log-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/grunt-legacy-log-utils/-/grunt-legacy-log-utils-2.0.1.tgz", + "integrity": "sha512-o7uHyO/J+i2tXG8r2bZNlVk20vlIFJ9IEYyHMCQGfWYru8Jv3wTqKZzvV30YW9rWEjq0eP3cflQ1qWojIe9VFA==", + "dev": true, + "requires": { + "chalk": "~2.4.1", + "lodash": "~4.17.10" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "grunt-legacy-util": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-1.1.1.tgz", + "integrity": "sha512-9zyA29w/fBe6BIfjGENndwoe1Uy31BIXxTH3s8mga0Z5Bz2Sp4UCjkeyv2tI449ymkx3x26B+46FV4fXEddl5A==", + "dev": true, + "requires": { + "async": "~1.5.2", + "exit": "~0.1.1", + "getobject": "~0.1.0", + "hooker": "~0.2.3", + "lodash": "~4.17.10", + "underscore.string": "~3.3.4", + "which": "~1.3.0" + } + }, + "grunt-release": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/grunt-release/-/grunt-release-0.14.0.tgz", + "integrity": "sha1-uNYAiJVhlm1g/9zAYGfrAVWX940=", + "dev": true, + "requires": { + "q": "^1.4.1", + "semver": "^5.1.0", + "shelljs": "^0.7.0", + "superagent": "^1.8.3" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "he": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", + "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", + "dev": true + }, + "hooker": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz", + "integrity": "sha1-uDT3I8xKJCqmWWNFnfbZhMXT2Vk=", + "dev": true + }, + "hosted-git-info": { + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.4.tgz", + "integrity": "sha512-pzXIvANXEFrc5oFFXRMkbLPQ2rXRoDERwDLyrcUxGhaZhgP54BBSl9Oheh7Vv0T090cszWBxPjkQQ5Sq1PbBRQ==", + "dev": true + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ignore": { + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", + "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", + "dev": true + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "indent-string": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "dev": true, + "requires": { + "repeating": "^2.0.0" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "inquirer": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-0.12.0.tgz", + "integrity": "sha1-HvK/1jUE3wvHV4X/+MLEHfEvB34=", + "dev": true, + "requires": { + "ansi-escapes": "^1.1.0", + "ansi-regex": "^2.0.0", + "chalk": "^1.0.0", + "cli-cursor": "^1.0.1", + "cli-width": "^2.0.0", + "figures": "^1.3.5", + "lodash": "^4.3.0", + "readline2": "^1.0.1", + "run-async": "^0.1.0", + "rx-lite": "^3.1.2", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.0", + "through": "^2.3.6" + } + }, + "interpret": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz", + "integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==", + "dev": true + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-finite": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", + "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-my-ip-valid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-my-ip-valid/-/is-my-ip-valid-1.0.0.tgz", + "integrity": "sha512-gmh/eWXROncUzRnIa1Ubrt5b8ep/MGSnfAUI3aRp+sqTCs1tv1Isl8d8F6JmkN3dXKc3ehZMrtiPN9eL03NuaQ==", + "dev": true + }, + "is-my-json-valid": { + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.20.0.tgz", + "integrity": "sha512-XTHBZSIIxNsIsZXg7XB5l8z/OBFosl1Wao4tXLpeC7eKU4Vm/kdop2azkPqULwnfGQjmeDIyey9g7afMMtdWAA==", + "dev": true, + "requires": { + "generate-function": "^2.0.0", + "generate-object-property": "^1.1.0", + "is-my-ip-valid": "^1.0.0", + "jsonpointer": "^4.0.0", + "xtend": "^4.0.0" + }, + "dependencies": { + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true + } + } + }, + "is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=", + "dev": true + }, + "is-resolvable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", + "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", + "dev": true + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", + "dev": true + }, + "js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "json-stable-stringify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", + "dev": true, + "requires": { + "jsonify": "~0.0.0" + } + }, + "jsonify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", + "dev": true + }, + "jsonpointer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz", + "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=", + "dev": true + }, + "just-extend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.0.2.tgz", + "integrity": "sha512-FrLwOgm+iXrPV+5zDU6Jqu4gCRXbWEQg2O3SKONsWE4w7AXFRkryS53bpWdaL9cNol+AmR3AEYz6kn+o0fCPnw==", + "dev": true + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + }, + "dependencies": { + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "^0.2.0" + } + } + } + }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "dev": true + }, + "lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", + "dev": true + }, + "lolex": { + "version": "2.7.5", + "resolved": "https://registry.npmjs.org/lolex/-/lolex-2.7.5.tgz", + "integrity": "sha512-l9x0+1offnKKIzYVjyXU2SiwhXDLekRzKyhnbyldPHvC7BvLPVpdNUNR2KeMAiCN2D/kLNttZgQD5WjSxuBx3Q==", + "dev": true + }, + "loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", + "dev": true, + "requires": { + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.0.0" + } + }, + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "dev": true + }, + "meow": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", + "dev": true, + "requires": { + "camelcase-keys": "^2.0.0", + "decamelize": "^1.1.2", + "loud-rejection": "^1.0.0", + "map-obj": "^1.0.1", + "minimist": "^1.1.3", + "normalize-package-data": "^2.3.4", + "object-assign": "^4.0.1", + "read-pkg-up": "^1.0.1", + "redent": "^1.0.0", + "trim-newlines": "^1.0.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "dev": true + }, + "mime": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.3.4.tgz", + "integrity": "sha1-EV+eO2s9rylZmDyzjxSaLUDrXVM=", + "dev": true + }, + "mime-db": { + "version": "1.40.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", + "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==", + "dev": true + }, + "mime-types": { + "version": "2.1.24", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", + "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", + "dev": true, + "requires": { + "mime-db": "1.40.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "0.0.8" + } + }, + "mocha": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz", + "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==", + "dev": true, + "requires": { + "browser-stdout": "1.3.1", + "commander": "2.15.1", + "debug": "3.1.0", + "diff": "3.5.0", + "escape-string-regexp": "1.0.5", + "glob": "7.1.2", + "growl": "1.10.5", + "he": "1.1.1", + "minimatch": "3.0.4", + "mkdirp": "0.5.1", + "supports-color": "5.4.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "supports-color": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", + "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "moment": { + "version": "2.24.0", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", + "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==", + "optional": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "mute-stream": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.5.tgz", + "integrity": "sha1-j7+rsKmKJT0xhDMfno3rc3L6xsA=", + "dev": true + }, + "mv": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", + "integrity": "sha1-rmzg1vbV4KT32JN5jQPB6pVZtqI=", + "optional": true, + "requires": { + "mkdirp": "~0.5.1", + "ncp": "~2.0.0", + "rimraf": "~2.4.0" + } + }, + "nan": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", + "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==" + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "ncp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", + "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=", + "optional": true + }, + "next-tick": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", + "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", + "dev": true + }, + "nise": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/nise/-/nise-1.5.1.tgz", + "integrity": "sha512-edFWm0fsFG2n318rfEnKlTZTkjlbVOFF9XIA+fj+Ed+Qz1laYW2lobwavWoMzGrYDHH1EpiNJgDfvGnkZztR/g==", + "dev": true, + "requires": { + "@sinonjs/formatio": "^3.2.1", + "@sinonjs/text-encoding": "^0.7.1", + "just-extend": "^4.0.2", + "lolex": "^4.1.0", + "path-to-regexp": "^1.7.0" + }, + "dependencies": { + "@sinonjs/formatio": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-3.2.1.tgz", + "integrity": "sha512-tsHvOB24rvyvV2+zKMmPkZ7dXX6LSLKZ7aOtXY6Edklp0uRcgGpOsQTTGTcWViFyx4uhWc6GV8QdnALbIbIdeQ==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1", + "@sinonjs/samsam": "^3.1.0" + } + }, + "lolex": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lolex/-/lolex-4.2.0.tgz", + "integrity": "sha512-gKO5uExCXvSm6zbF562EvM+rd1kQDnB9AZBbiQVzf1ZmdDpxUSvpnAaVOP83N/31mRK8Ml8/VE8DMvsAZQ+7wg==", + "dev": true + } + } + }, + "node-rdkafka": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/node-rdkafka/-/node-rdkafka-2.3.4.tgz", + "integrity": "sha512-ilaAOrEpDF3TGTlItsxU5pQXG+qjN1gKbhSvs9CoLXZaItt2EN6oU+kEdO6UkRQLKO6/Kv4m296cBrr0JCmiTw==", + "requires": { + "bindings": "1.x", + "nan": "2.x" + } + }, + "nopt": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", + "dev": true, + "requires": { + "abbrev": "1" + } + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "object-keys": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz", + "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", + "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", + "dev": true + }, + "optionator": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", + "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", + "dev": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.4", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "wordwrap": "~1.0.0" + } + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "dev": true + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "requires": { + "pinkie-promise": "^2.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "path-to-regexp": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz", + "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=", + "dev": true, + "requires": { + "isarray": "0.0.1" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + } + } + }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "^2.0.0" + } + }, + "pluralize": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-1.2.1.tgz", + "integrity": "sha1-0aIUg/0iu0HlihL6NCGCMUCJfEU=", + "dev": true + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "progress": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz", + "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=", + "dev": true + }, + "q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", + "dev": true + }, + "qs": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-2.3.3.tgz", + "integrity": "sha1-6eha2+ddoLvkyOBHaghikPhjtAQ=", + "dev": true + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "dev": true, + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "dev": true, + "requires": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + } + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "readline2": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/readline2/-/readline2-1.0.1.tgz", + "integrity": "sha1-QQWWCP/BVHV7cV2ZidGZ/783LjU=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "mute-stream": "0.0.5" + } + }, + "rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", + "dev": true, + "requires": { + "resolve": "^1.1.6" + } + }, + "redent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", + "dev": true, + "requires": { + "indent-string": "^2.1.0", + "strip-indent": "^1.0.1" + } + }, + "reduce-component": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/reduce-component/-/reduce-component-1.0.1.tgz", + "integrity": "sha1-4Mk1QsV0UhvqE98PlIjtgqt3xdo=", + "dev": true + }, + "repeating": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "dev": true, + "requires": { + "is-finite": "^1.0.0" + } + }, + "require-uncached": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", + "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", + "dev": true, + "requires": { + "caller-path": "^0.1.0", + "resolve-from": "^1.0.0" + } + }, + "resolve": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz", + "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + }, + "resolve-from": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", + "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=", + "dev": true + }, + "restore-cursor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", + "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", + "dev": true, + "requires": { + "exit-hook": "^1.0.0", + "onetime": "^1.0.0" + } + }, + "rimraf": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", + "integrity": "sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto=", + "optional": true, + "requires": { + "glob": "^6.0.1" + } + }, + "run-async": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-0.1.0.tgz", + "integrity": "sha1-yK1KXhEGYeQCp9IbUw4AnyX444k=", + "dev": true, + "requires": { + "once": "^1.3.0" + } + }, + "rx-lite": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-3.1.2.tgz", + "integrity": "sha1-Gc5QLKVyZl87ZHsQk5+X/RYV8QI=", + "dev": true + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "safe-json-stringify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz", + "integrity": "sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg==", + "optional": true + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "samsam": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.3.0.tgz", + "integrity": "sha512-1HwIYD/8UlOtFS3QO3w7ey+SdSDFE4HRNLZoZRYVQefrOY3l17epswImeB1ijgJFQJodIaHcwkp3r/myBjFVbg==", + "dev": true + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "shelljs": { + "version": "0.7.8", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.7.8.tgz", + "integrity": "sha1-3svPh0sNHl+3LhSxZKloMEjprLM=", + "dev": true, + "requires": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + }, + "dependencies": { + "glob": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", + "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } + } + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true + }, + "sinon": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-4.5.0.tgz", + "integrity": "sha512-trdx+mB0VBBgoYucy6a9L7/jfQOmvGeaKZT4OOJ+lPAtI8623xyGr8wLiE4eojzBS8G9yXbhx42GHUOVLr4X2w==", + "dev": true, + "requires": { + "@sinonjs/formatio": "^2.0.0", + "diff": "^3.1.0", + "lodash.get": "^4.4.2", + "lolex": "^2.2.0", + "nise": "^1.2.0", + "supports-color": "^5.1.0", + "type-detect": "^4.0.5" + }, + "dependencies": { + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true + } + } + }, + "slice-ansi": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz", + "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=", + "dev": true + }, + "spdx-correct": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", + "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", + "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", + "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", + "dev": true + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, + "strip-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", + "dev": true, + "requires": { + "get-stdin": "^4.0.1" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + }, + "superagent": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-1.8.5.tgz", + "integrity": "sha1-HA3cOvMOgOuE68BcshItqP6UC1U=", + "dev": true, + "requires": { + "component-emitter": "~1.2.0", + "cookiejar": "2.0.6", + "debug": "2", + "extend": "3.0.0", + "form-data": "1.0.0-rc3", + "formidable": "~1.0.14", + "methods": "~1.1.1", + "mime": "1.3.4", + "qs": "2.3.3", + "readable-stream": "1.0.27-1", + "reduce-component": "1.0.1" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "readable-stream": { + "version": "1.0.27-1", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.27-1.tgz", + "integrity": "sha1-a2eYPCA1fO/QfwFlABoW1xDZEHg=", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + } + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + }, + "table": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/table/-/table-3.8.3.tgz", + "integrity": "sha1-K7xULw/amGGnVdOUf+/Ys/UThV8=", + "dev": true, + "requires": { + "ajv": "^4.7.0", + "ajv-keywords": "^1.0.0", + "chalk": "^1.1.1", + "lodash": "^4.0.0", + "slice-ansi": "0.0.4", + "string-width": "^2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "trim-newlines": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", + "dev": true + }, + "type": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/type/-/type-1.0.3.tgz", + "integrity": "sha512-51IMtNfVcee8+9GJvj0spSuFcZHe9vSib6Xtgsny1Km9ugyz2mbS08I3rsUIRYgJohFRFU1160sgRodYz378Hg==", + "dev": true + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + }, + "type-detect": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-1.0.0.tgz", + "integrity": "sha1-diIXzAbbJY7EiQihKY6LlRIejqI=", + "dev": true + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "underscore.string": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-3.3.5.tgz", + "integrity": "sha512-g+dpmgn+XBneLmXXo+sGlW5xQEt4ErkS3mgeN2GFbremYeMBSJKr9Wf2KJplQVaiPY/f7FN6atosWYNm9ovrYg==", + "dev": true, + "requires": { + "sprintf-js": "^1.0.3", + "util-deprecate": "^1.0.2" + } + }, + "user-home": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/user-home/-/user-home-2.0.0.tgz", + "integrity": "sha1-nHC/2Babwdy/SGBODwS4tJzenp8=", + "dev": true, + "requires": { + "os-homedir": "^1.0.0" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "dev": true + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "write": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", + "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", + "dev": true, + "requires": { + "mkdirp": "^0.5.1" + } + }, + "xtend": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz", + "integrity": "sha1-bv7MKk2tjmlixJAbM3znuoe10os=", + "requires": { + "object-keys": "~0.4.0" + } + } + } +} From 02682d0a83ff7f00e9ea4dff8a3799b11c5601b3 Mon Sep 17 00:00:00 2001 From: Piotr Leszczynski Date: Wed, 4 Sep 2019 16:14:19 +0200 Subject: [PATCH 02/62] Chore(Support multiply types in a single topics): Reading and producing multiple types on one topic. --- lib/kafka-consumer.js | 45 ++++++++++++++++++++----------------------- lib/kafka-producer.js | 6 +++++- lib/magic-byte.js | 20 ++++++++----------- 3 files changed, 34 insertions(+), 37 deletions(-) diff --git a/lib/kafka-consumer.js b/lib/kafka-consumer.js index e26467c..7603b55 100644 --- a/lib/kafka-consumer.js +++ b/lib/kafka-consumer.js @@ -35,11 +35,11 @@ Consumer.prototype.getConsumer = Promise.method(function (opts, topts) { this._consumers.push(consumer); - consumer.on('disconnect', function(arg) { + consumer.on('disconnect', function (arg) { log.warn('getConsumer() :: Consumer disconnected. Args:', arg); }); - consumer.on('error', function(err) { + consumer.on('error', function (err) { log.error('getConsumer() :: Consumer Error event fired:', err); }); @@ -67,15 +67,15 @@ Consumer.prototype.getConsumerStream = Promise.method(function (opts, topts, sop log.info('getConsumerStream() :: Starting Consumer Stream with opts:', opts); - var consumer = new kafka.KafkaConsumer.createReadStream(opts, topts, sopts); + const consumer = new kafka.KafkaConsumer.createReadStream(opts, topts, sopts); this._consumersStream.push(consumer); - consumer.on('disconnect', function(arg) { + consumer.on('disconnect', function (arg) { log.warn('getConsumerStream() :: Consumer disconnected. Args:', arg); }); - consumer.on('error', function(err) { + consumer.on('error', function (err) { log.error('getConsumerStream() :: Consumer Error event fired:', err); }); @@ -101,8 +101,12 @@ Consumer.prototype._onWrapper = function (consumerInstance, eventName, cb) { return consumerInstance.__kafkaAvro_on(eventName, cb); } - return consumerInstance.__kafkaAvro_on('data', function(message) { - if (!this.sr.keySchemas[message.topic]) { + return consumerInstance.__kafkaAvro_on('data', function (message) { + const keyDecoded = magicByte.fromMessageBuffer(message.key, this.sr); + const valueDecoded = magicByte.fromMessageBuffer(message.value, this.sr); + + + if (!keyDecoded) { log.warn('_onWrapper() :: Warning, consumer did not find topic on SR for key schema:', message.topic); @@ -122,9 +126,12 @@ Consumer.prototype._onWrapper = function (consumerInstance, eventName, cb) { } } } + } else { + message.parsedKey = keyDecoded.value; + message.schemaIdKey = keyDecoded.schemaId; } - if (!this.sr.valueSchemas[message.topic]) { + if (!valueDecoded) { log.warn('_onWrapper() :: Warning, consumer did not find topic on SR for value schema:', message.topic); @@ -144,22 +151,12 @@ Consumer.prototype._onWrapper = function (consumerInstance, eventName, cb) { } } } - cb(message); return; } - var decodedValue = this._deserializeType(this.sr.valueSchemas[message.topic], message, false); - - message.parsed = decodedValue.value; - message.schemaId = decodedValue.schemaId; - - if (message.key) { - var decodedKey = this._deserializeType(this.sr.keySchemas[message.topic], message, true); - - message.parsedKey = decodedKey.value; - message.schemaIdKey = decodedKey.schemaId; - } + message.parsed = valueDecoded.value; + message.schemaId = valueDecoded.schemaId; cb(message); }.bind(this)); @@ -184,10 +181,10 @@ Consumer.prototype.deserialize = function (type, message, isKey) { deserializeType, this.sr ); - } catch(ex) { - log.warn(`deserialize() :: Error deserializing on topic ${ message.topic }`, - 'Raw value:', message.value, `Partition: ${ message.partition } Offset:`, - `${ message.offset } Key: ${ message.key } Exception:`, ex); + } catch (ex) { + log.warn(`deserialize() :: Error deserializing on topic ${message.topic}`, + 'Raw value:', message.value, `Partition: ${message.partition} Offset:`, + `${message.offset} Key: ${message.key} Exception:`, ex); return null; } diff --git a/lib/kafka-producer.js b/lib/kafka-producer.js index 54a96e4..330097a 100644 --- a/lib/kafka-producer.js +++ b/lib/kafka-producer.js @@ -77,6 +77,10 @@ Producer.prototype._serializeType = function (topicName, isKey, data) { ? this.sr.valueSchemas[subject] : this.sr.keySchemas[subject]; + var schemaType = isKey === false + ? 'value' + : 'key'; + if (!schema) { log.warn(`_produceWrapper() :: Warning, did not find topic on SR for ${subject} schema:`, topicName); @@ -85,7 +89,7 @@ Producer.prototype._serializeType = function (topicName, isKey, data) { ? new Buffer(JSON.stringify(data)) : new Buffer(data); } else { - const schemaId = this.sr.schemaIds[subject]; + const schemaId = this.sr.schemaIds[subject + '-' + schemaType]; return this.serialize(schema, schemaId, data); } diff --git a/lib/magic-byte.js b/lib/magic-byte.js index 50dda7e..568b256 100644 --- a/lib/magic-byte.js +++ b/lib/magic-byte.js @@ -45,24 +45,20 @@ magicByte.toMessageBuffer = function (val, type, schemaId, optLength) { * @param {number} schemaId The schema id. * @param {Object} value The decoded avro value. */ -magicByte.fromMessageBuffer = function (type, encodedMessage, sr) { - if (encodedMessage[0] !== MAGIC_BYTE) { - throw new TypeError('Message not serialized with magic byte'); +magicByte.fromMessageBuffer = function (encodedMessage, sr) { + if (!encodedMessage || encodedMessage[0] !== MAGIC_BYTE) { + return null; } - var schemaId = encodedMessage.readInt32BE(1); + const schemaId = encodedMessage.readInt32BE(1); + const schemaType = sr.schemaTypeById[('schema-' + schemaId)]; - var schemaKey = 'schema-' + schemaId; - var decoded; - if (!sr.schemaTypeById[schemaKey]) { - // use default type - decoded = type.decode(encodedMessage, 5); - } else { - decoded = sr.schemaTypeById[schemaKey].decode(encodedMessage, 5); + if (!schemaType) { + return null; } return { - value: decoded.value, + value: schemaType.decode(encodedMessage, 5).value, schemaId: schemaId, }; }; From 759719423aa770e6b83ea8c13004a28e4258418c Mon Sep 17 00:00:00 2001 From: Piotr Leszczynski Date: Thu, 5 Sep 2019 11:31:21 +0200 Subject: [PATCH 03/62] Chore(Support multiply types in a single topics): Fixing the base tests. --- lib/kafka-consumer.js | 14 +++++------ lib/kafka-producer.js | 1 + lib/schema-registry.js | 5 ++-- test/lib/test.lib.js | 12 +++++----- test/spec/magic-byte.test.js | 23 +++++++++++------- test/spec/producer.test.js | 39 ++++++++++++++++--------------- test/spec/schema-registry.test.js | 12 +++++----- 7 files changed, 57 insertions(+), 49 deletions(-) diff --git a/lib/kafka-consumer.js b/lib/kafka-consumer.js index 7603b55..807b8a4 100644 --- a/lib/kafka-consumer.js +++ b/lib/kafka-consumer.js @@ -111,18 +111,18 @@ Consumer.prototype._onWrapper = function (consumerInstance, eventName, cb) { message.topic); message.parsedKey = null; + const key = message.key; - if (message.key) { + if (key) { try { - message.parsedKey = Buffer.isBuffer(message.key) - ? JSON.parse(message.key.toString('utf-8')) - : message.key; + message.parsedKey = Buffer.isBuffer(key) + ? JSON.parse(key.toString('utf-8')) + : key; } catch (ex) { if (ex instanceof SyntaxError) { - message.parsedKey = message.key.toString('utf-8'); + message.parsedKey = key.toString('utf-8'); } else { - log.warn('_onWrapper() :: Error parsing key:', message.key, - 'Error:', ex); + log.warn({ex}, '_onWrapper() :: Error parsing key', {key}); } } } diff --git a/lib/kafka-producer.js b/lib/kafka-producer.js index 330097a..7b5eb90 100644 --- a/lib/kafka-producer.js +++ b/lib/kafka-producer.js @@ -71,6 +71,7 @@ Producer.prototype.getProducer = Promise.method(function (opts, topts) { */ Producer.prototype._serializeType = function (topicName, isKey, data) { // implementing io.confluent.kafka.serializers.subject.TopicRecordNameStrategy + // FIXME: This will break if this code gets minimized const subject = `${data.constructor.name}-${topicName}`; const schema = isKey === false diff --git a/lib/schema-registry.js b/lib/schema-registry.js index 4cfd331..f5824f6 100644 --- a/lib/schema-registry.js +++ b/lib/schema-registry.js @@ -356,9 +356,7 @@ SchemaRegistry.prototype._registerSchema = Promise.method(function (schemaObj) { log.debug('_registerSchema() :: Registered schema:', schemaObj.topic); - if (schemaObj.schemaType.toLowerCase() === 'value') { - this.schemaTypeById['schema-' + schemaObj.responseRaw.id] = schemaObj.type; - } + this.schemaTypeById['schema-' + schemaObj.responseRaw.id] = schemaObj.type; return schemaObj; }); @@ -390,6 +388,7 @@ SchemaRegistry.prototype._registerSchemaLatest = Promise.method(function (schema this.valueSchemas[schemaObj.topic] = schemaObj.type; this.schemaIds[schemaObj.topic + '-value'] = schemaObj.responseRaw.id; } else { + this.schemaTypeById['schema-' + schemaObj.responseRaw.id] = schemaObj.type; this.keySchemas[schemaObj.topic] = schemaObj.type; this.schemaIds[schemaObj.topic + '-key'] = schemaObj.responseRaw.id; } diff --git a/test/lib/test.lib.js b/test/lib/test.lib.js index 41ea5f1..04987f6 100644 --- a/test/lib/test.lib.js +++ b/test/lib/test.lib.js @@ -63,13 +63,13 @@ testLib.init = function() { this.timeout(180000); // wait up to 3' for the SR to come up return Promise.all([ - testLib.registerSchema(testLib.topic, schemaFix, 'value'), - testLib.registerSchema(testLib.topicTwo, schemaTwoFix, 'value'), - testLib.registerSchema(testLib.topicThreeWithDuplicateSchema, schemaFix, 'value'), + testLib.registerSchema(`Object-${testLib.topic}`, schemaFix, 'value'), + testLib.registerSchema(`Object-${testLib.topicTwo}`, schemaTwoFix, 'value'), + testLib.registerSchema(`Object-${testLib.topicThreeWithDuplicateSchema}`, schemaFix, 'value'), - testLib.registerSchema(testLib.topic, keySchemaFix, 'key'), - testLib.registerSchema(testLib.topicTwo, keySchemaFix, 'key'), - testLib.registerSchema(testLib.topicThreeWithDuplicateSchema, keySchemaFix, 'key'), + testLib.registerSchema(`String-${testLib.topic}`, keySchemaFix, 'key'), + testLib.registerSchema(`Object-${testLib.topicTwo}`, keySchemaFix, 'key'), + testLib.registerSchema(`Object-${testLib.topicThreeWithDuplicateSchema}`, keySchemaFix, 'key'), ]); }); diff --git a/test/spec/magic-byte.test.js b/test/spec/magic-byte.test.js index f89a448..eb8374e 100644 --- a/test/spec/magic-byte.test.js +++ b/test/spec/magic-byte.test.js @@ -13,31 +13,38 @@ var schemaFix = require('../fixtures/schema.fix'); describe('Magic Byte', function() { it('should encode a large message', function() { - var message = { + const message = { name: new Array(40000).join('0'), long: 540, }; - var type = avro.parse(schemaFix, {wrapUnions: true}); + const type = avro.parse(schemaFix, {wrapUnions: true}); magicByte.toMessageBuffer(message, type, 109); }); it('should extract schemaId from encoded message', function() { - var message = { + const message = { name: new Array(40).join('0'), long: 540, }; - var schemaId = 109; + const schemaId = 109; - var type = avro.parse(schemaFix, {wrapUnions: true}); + const type = avro.parse(schemaFix, {wrapUnions: true}); - var encoded = magicByte.toMessageBuffer(message, type, schemaId); + const encoded = magicByte.toMessageBuffer(message, type, schemaId); - var decoded = magicByte.fromMessageBuffer(type, encoded, { + const decoded = magicByte.fromMessageBuffer(encoded, { schemaTypeById: { - schemaId: schemaId + 'schema-109': { + // eslint-disable-next-line no-unused-vars + 'decode': function (buf, pos, resolver) { + return { + 'value': message + }; + } + } } }); diff --git a/test/spec/producer.test.js b/test/spec/producer.test.js index bbf1664..093397e 100644 --- a/test/spec/producer.test.js +++ b/test/spec/producer.test.js @@ -1,10 +1,10 @@ /** * @fileOverview Test produce and consume messages using kafka-avro. */ -var chai = require('chai'); -var expect = chai.expect; +const chai = require('chai'); +const expect = chai.expect; -var testLib = require('../lib/test.lib'); +const testLib = require('../lib/test.lib'); describe('Produce', function() { testLib.init(); @@ -39,17 +39,19 @@ describe('Produce', function() { }); it('should produce a message with serialized key', function(done) { - var message = { + const message = { name: 'Thanasis', long: 540, }; - var key = 'key'; - + const key = 'key'; + const that = this; this.producer.on('delivery-report', function(err, report) { - var parsedKey = Buffer.from(report.key).slice(6); // MAGIC_BYTE(1) | SCHEMA_ID(4) | KEY + const schemaId = report.key.readInt32BE(1); + const schemaType = that.sr.schemaTypeById[('schema-' + schemaId)]; + const parsedKey = schemaType.decode(report.key, 5).value; expect(err).to.equal(null); - expect(parsedKey.toString('utf8')).to.equal(key); + expect(parsedKey).to.equal(key); expect(report.opaque).to.equal(undefined); this.gotReceipt = true; }); @@ -57,7 +59,7 @@ describe('Produce', function() { this.producer.produce(this.topicName, -1, message, key); //need to keep polling for a while to ensure the delivery reports are received - var pollLoop = setInterval(() => { + const pollLoop = setInterval(() => { this.producer.poll(); if (this.gotReceipt) { clearInterval(pollLoop); @@ -66,15 +68,14 @@ describe('Produce', function() { }, 1000); }); it('should produce a message with an opaque value in delivery report', function(done) { - var message = { + const message = { name: 'Thanasis', long: 540, }; - var key = 'key'; - - var eventTime = Date.now(); - var opaqueRef = 'my-opaque-ref'; + const key = 'key'; + const eventTime = Date.now(); + const opaqueRef = 'my-opaque-ref'; this.producer.on('delivery-report', function(err, report) { expect(err).to.equal(null); @@ -85,7 +86,7 @@ describe('Produce', function() { this.producer.produce(this.topicName, -1, message, key, eventTime, opaqueRef); //need to keep polling for a while to ensure the delivery reports are received - var pollLoop = setInterval(() => { + const pollLoop = setInterval(() => { this.producer.poll(); if (this.gotReceipt) { clearInterval(pollLoop); @@ -94,22 +95,22 @@ describe('Produce', function() { }, 1000); }); it('should not allow invalid type', function() { - var message = { + const message = { name: 'Thanasis', long: '540', }; - var binded = this.producer.produce.bind(this.producer, this.topicName, + const binded = this.producer.produce.bind(this.producer, this.topicName, -1, message, 'key'); expect(binded).to.throw(Error); }); it('should not allow less attributes', function() { - var message = { + const message = { name: 'Thanasis', }; - var binded = this.producer.produce.bind(this.producer, this.topicName, -1, message, 'key'); + const binded = this.producer.produce.bind(this.producer, this.topicName, -1, message, 'key'); expect(binded).to.throw(Error); }); diff --git a/test/spec/schema-registry.test.js b/test/spec/schema-registry.test.js index 12f9724..c58df8c 100644 --- a/test/spec/schema-registry.test.js +++ b/test/spec/schema-registry.test.js @@ -1,18 +1,18 @@ /** * @fileOverview Test initialization of the KafkaAvro lib, SR related. */ -var chai = require('chai'); -var expect = chai.expect; +const chai = require('chai'); +const expect = chai.expect; const testLib = require('../lib/test.lib'); -var SchemaRegistry = require('../../lib/schema-registry'); +const SchemaRegistry = require('../../lib/schema-registry'); -var srUrl = 'http://localhost:8081'; +const srUrl = 'http://localhost:8081'; describe('Initialization of SR', function() { testLib.init(); it('should initialize properly', function() { - var sr = new SchemaRegistry({ schemaRegistryUrl: srUrl }); + const sr = new SchemaRegistry({schemaRegistryUrl: srUrl}); return sr.init() .map((res) => { @@ -37,7 +37,7 @@ describe('Initialization of SR', function() { }); }); it('SR instance should contain expected values after init', function() { - var sr = new SchemaRegistry({ schemaRegistryUrl: srUrl }); + const sr = new SchemaRegistry({schemaRegistryUrl: srUrl}); return sr.init() .map((res) => { From 2f65ca463c0994739798527078cd659ec9fdc561 Mon Sep 17 00:00:00 2001 From: Piotr Leszczynski Date: Thu, 5 Sep 2019 12:44:29 +0200 Subject: [PATCH 04/62] refactoring(Support multiply types in a single topics): * Using let/const instead of var * Standardizing logging message format * Using template strings * Using standard nodejs err keyword * Reformatting code with default intellij formatter for js --- lib/kafka-avro.js | 68 ++++++++++++++--------------- lib/kafka-consumer.js | 84 +++++++++++++++++------------------ lib/kafka-producer.js | 40 +++++++++-------- lib/log.lib.js | 26 ++++++----- lib/magic-byte.js | 11 +++-- lib/schema-registry.js | 99 ++++++++++++++++++++++-------------------- 6 files changed, 165 insertions(+), 163 deletions(-) diff --git a/lib/kafka-avro.js b/lib/kafka-avro.js index 2b4b4d5..ea6cb92 100644 --- a/lib/kafka-avro.js +++ b/lib/kafka-avro.js @@ -6,29 +6,27 @@ * Copyright © Waldo, Inc. * Licensed under the MIT license. */ -var EventEmitter = require('events').EventEmitter; - -var Promise = require('bluebird'); -var cip = require('cip'); -var Kafka = require('node-rdkafka'); - -var rootLog = require('./log.lib'); -var log = rootLog.getChild(__filename); - -var SchemaRegistry = require('./schema-registry'); +const EventEmitter = require('events').EventEmitter; +const Promise = require('bluebird'); +const cip = require('cip'); +const Kafka = require('node-rdkafka'); +const rootLog = require('./log.lib'); +const log = rootLog.getChild(__filename); +const SchemaRegistry = require('./schema-registry'); // // Mixins // -var Producer = require('./kafka-producer'); -var Consumer = require('./kafka-consumer'); +const Producer = require('./kafka-producer'); +const Consumer = require('./kafka-consumer'); -var CeventEmitter = cip.cast(EventEmitter); +const CeventEmitter = cip.cast(EventEmitter); -function noop() {} +function noop() { +} /** - * @fileOverview bootstrap and master exporing module. + * @fileOverview bootstrap and master exporting module. */ /** @@ -37,11 +35,11 @@ function noop() {} * @param {Object} opts The options. * @constructor */ -var KafkaAvro = module.exports = CeventEmitter.extend(function(opts) { +const KafkaAvro = module.exports = CeventEmitter.extend(function (opts) { /** @type {string} The SR url */ this.kafkaBrokerUrl = opts.kafkaBroker; - var srOpts = { + const srOpts = { schemaRegistryUrl: opts.schemaRegistry, selectedTopics: opts.topics || null, fetchAllVersions: opts.fetchAllVersions || false, @@ -50,14 +48,14 @@ var KafkaAvro = module.exports = CeventEmitter.extend(function(opts) { httpsAgent: opts.httpsAgent }; - /** @type {kafka-avro.SchemaRegistry} Instanciated SR. */ + /** @type {kafka-avro.SchemaRegistry} Instantiated SR. */ this.sr = new SchemaRegistry(srOpts); - /** @type {Array.} Instanciated producers. */ + /** @type {Array.} Instantiated producers. */ this._producers = []; - /** @type {Array.} Instanciated consumers. */ + /** @type {Array.} Instantiated consumers. */ this._consumers = []; - /** @type {Array.} Instanciated consumers. */ + /** @type {Array.} Instantiated consumers. */ this._consumersStream = []; }); @@ -100,27 +98,29 @@ KafkaAvro.prototype.init = Promise.method(function () { * @return {Promise} A Promise. */ KafkaAvro.prototype.dispose = Promise.method(function () { + const disconnectPromises = []; - var disconnectPromises = []; - - log.info('dispose() :: Disposing kafka-avro instance. Total consumers:', - this._consumers.length, 'Total producers:', this._producers.length); + log.info('dispose() :: Disposing kafka-avro instance. Total consumers: noOfConsumers, Total producers: noOfProducers', + { + noOfConsumers: this._consumers.length, + noOfProducers: this._producers.length + }); - this._consumers.forEach(function(consumer) { - var discon = Promise.promisify(consumer.disconnect.bind(consumer)); - var disconProm = discon().catch(noop); + this._consumers.forEach(function (consumer) { + const discon = Promise.promisify(consumer.disconnect.bind(consumer)); + const disconProm = discon().catch(noop); disconnectPromises.push(disconProm); }); - this._producers.forEach(function(producer) { - var discon = Promise.promisify(producer.disconnect.bind(producer)); - var disconProm = discon().catch(noop); + this._producers.forEach(function (producer) { + const discon = Promise.promisify(producer.disconnect.bind(producer)); + const disconProm = discon().catch(noop); disconnectPromises.push(disconProm); }); return Promise.all(disconnectPromises) - .then( () => { - if(this.sr._refreshHandle) - clearInterval(this.sr._refreshHandle); + .then(() => { + if (this.sr._refreshHandle) + clearInterval(this.sr._refreshHandle); }); }); diff --git a/lib/kafka-consumer.js b/lib/kafka-consumer.js index 807b8a4..ba2a3f4 100644 --- a/lib/kafka-consumer.js +++ b/lib/kafka-consumer.js @@ -1,46 +1,44 @@ /** * @fileOverview Wrapper for node-rdkafka Consumer Ctor, a mixin. */ - -var Promise = require('bluebird'); -var cip = require('cip'); -var kafka = require('node-rdkafka'); - -var magicByte = require('./magic-byte'); -var log = require('./log.lib').getChild(__filename); +const Promise = require('bluebird'); +const cip = require('cip'); +const kafka = require('node-rdkafka'); +const magicByte = require('./magic-byte'); +const log = require('./log.lib').getChild(__filename); /** * Wrapper for node-rdkafka Consumer Ctor, a mixin. * * @constructor */ -var Consumer = module.exports = cip.extend(); +const Consumer = module.exports = cip.extend(); /** * The wrapper of the node-rdkafka package Consumer Ctor. * * @param {Object} opts Consumer general options. - * @param {Object} topts Topic specific options. + * @param {Object} topicOpts Topic specific options. * @see https://github.com/edenhill/librdkafka/blob/2213fb29f98a7a73f22da21ef85e0783f6fd67c4/CONFIGURATION.md * @return {Promise(kafka.Consumer)} A Promise with the consumer. */ -Consumer.prototype.getConsumer = Promise.method(function (opts, topts) { +Consumer.prototype.getConsumer = Promise.method(function (opts, topicOpts) { if (!opts['metadata.broker.list']) { opts['metadata.broker.list'] = this.kafkaBrokerUrl; } - log.info('getConsumer() :: Starting Consumer with opts:', opts); + log.info('getConsumer() :: Starting Consumer with opts', {opts}); - var consumer = new kafka.KafkaConsumer(opts, topts); + const consumer = new kafka.KafkaConsumer(opts, topicOpts); this._consumers.push(consumer); - consumer.on('disconnect', function (arg) { - log.warn('getConsumer() :: Consumer disconnected. Args:', arg); + consumer.on('disconnect', function (args) { + log.warn('getConsumer() :: Consumer disconnected. args', {args}); }); consumer.on('error', function (err) { - log.error('getConsumer() :: Consumer Error event fired:', err); + log.error({err}, 'getConsumer() :: Consumer Error event fired'); }); // hack node-rdkafka @@ -54,29 +52,29 @@ Consumer.prototype.getConsumer = Promise.method(function (opts, topts) { * The wrapper of the node-rdkafka package KafkaConsumerStream. * * @param {Object} opts Consumer general options. - * @param {Object} topts Topic specific options. - * @param {Object} sopts node-rdkafka ConsumerStream options + * @param {Object} topicOpts Topic specific options. + * @param {Object} streamOpts node-rdkafka ConsumerStream options * @see https://github.com/edenhill/librdkafka/blob/2213fb29f98a7a73f22da21ef85e0783f6fd67c4/CONFIGURATION.md * @see https://blizzard.github.io/node-rdkafka/current/KafkaConsumerStream.html * @return {Promise(kafka.ConsumerStream)} A Promise with the consumer stream. */ -Consumer.prototype.getConsumerStream = Promise.method(function (opts, topts, sopts) { +Consumer.prototype.getConsumerStream = Promise.method(function (opts, topicOpts, streamOpts) { if (!opts['metadata.broker.list']) { opts['metadata.broker.list'] = this.kafkaBrokerUrl; } - log.info('getConsumerStream() :: Starting Consumer Stream with opts:', opts); + log.info('getConsumerStream() :: Starting Consumer Stream with opts', {opts}); - const consumer = new kafka.KafkaConsumer.createReadStream(opts, topts, sopts); + const consumer = new kafka.KafkaConsumer.createReadStream(opts, topicOpts, streamOpts); this._consumersStream.push(consumer); consumer.on('disconnect', function (arg) { - log.warn('getConsumerStream() :: Consumer disconnected. Args:', arg); + log.warn('getConsumerStream() :: Consumer disconnected. arg', {arg}); }); consumer.on('error', function (err) { - log.error('getConsumerStream() :: Consumer Error event fired:', err); + log.error({err}, 'getConsumerStream() :: Consumer Error event fired'); }); // hack node-rdkafka @@ -105,10 +103,9 @@ Consumer.prototype._onWrapper = function (consumerInstance, eventName, cb) { const keyDecoded = magicByte.fromMessageBuffer(message.key, this.sr); const valueDecoded = magicByte.fromMessageBuffer(message.value, this.sr); - if (!keyDecoded) { - log.warn('_onWrapper() :: Warning, consumer did not find topic on SR for key schema:', - message.topic); + log.warn('_onWrapper() :: Warning, consumer did not find topic on SR for key schema', + {topic: message.topic}); message.parsedKey = null; const key = message.key; @@ -116,13 +113,13 @@ Consumer.prototype._onWrapper = function (consumerInstance, eventName, cb) { if (key) { try { message.parsedKey = Buffer.isBuffer(key) - ? JSON.parse(key.toString('utf-8')) + ? JSON.parse(key.toString()) : key; - } catch (ex) { - if (ex instanceof SyntaxError) { - message.parsedKey = key.toString('utf-8'); + } catch (err) { + if (err instanceof SyntaxError) { + message.parsedKey = key.toString(); } else { - log.warn({ex}, '_onWrapper() :: Error parsing key', {key}); + log.warn({err}, '_onWrapper() :: Error parsing key', {key}); } } } @@ -132,22 +129,21 @@ Consumer.prototype._onWrapper = function (consumerInstance, eventName, cb) { } if (!valueDecoded) { - log.warn('_onWrapper() :: Warning, consumer did not find topic on SR for value schema:', - message.topic); + log.warn('_onWrapper() :: Warning, consumer did not find topic on SR for value schema', + {topic: message.topic}); message.parsed = null; if (message.value) { try { message.parsed = Buffer.isBuffer(message.value) - ? JSON.parse(message.value.toString('utf-8')) + ? JSON.parse(message.value.toString()) : message.value; - } catch (ex) { - if (ex instanceof SyntaxError) { - message.parsed = message.value.toString('utf-8'); + } catch (err) { + if (err instanceof SyntaxError) { + message.parsed = message.value.toString(); } else { - log.warn('_onWrapper() :: Error parsing value:', message.value, - 'Error:', ex); + log.warn({err}, '_onWrapper() :: Error parsing value', {value: message.value}); } } } @@ -172,23 +168,21 @@ Consumer.prototype._onWrapper = function (consumerInstance, eventName, cb) { */ Consumer.prototype.deserialize = function (type, message, isKey) { try { - var deserializeType = isKey === true + const deserializeType = isKey === true ? message.key : message.value; - var parsed = magicByte.fromMessageBuffer( + return magicByte.fromMessageBuffer( type, deserializeType, this.sr ); - } catch (ex) { + } catch (err) { log.warn(`deserialize() :: Error deserializing on topic ${message.topic}`, 'Raw value:', message.value, `Partition: ${message.partition} Offset:`, - `${message.offset} Key: ${message.key} Exception:`, ex); + `${message.offset} Key: ${message.key} Exception:`, err); return null; } - - return parsed; }; /** @@ -201,7 +195,7 @@ Consumer.prototype.deserialize = function (type, message, isKey) { * @private */ Consumer.prototype._deserializeType = function (type, data, isKey) { - var decoded = this.deserialize(type, data, isKey); + const decoded = this.deserialize(type, data, isKey); return { value: !decoded ? null : decoded.value, diff --git a/lib/kafka-producer.js b/lib/kafka-producer.js index 7b5eb90..a4df04c 100644 --- a/lib/kafka-producer.js +++ b/lib/kafka-producer.js @@ -1,12 +1,11 @@ /** * @fileOverview Wrapper for node-rdkafka Producer Ctor, a mixin. */ -var Promise = require('bluebird'); -var cip = require('cip'); -var kafka = require('node-rdkafka'); - -var magicByte = require('./magic-byte'); -var log = require('./log.lib').getChild(__filename); +const Promise = require('bluebird'); +const cip = require('cip'); +const kafka = require('node-rdkafka'); +const magicByte = require('./magic-byte'); +const log = require('./log.lib').getChild(__filename); /** * Wrapper for node-rdkafka Produce Ctor, a mixin. @@ -19,11 +18,11 @@ var Producer = module.exports = cip.extend(); * The wrapper of the node-rdkafka package Producer Ctor. * * @param {Object} opts Producer general options. - * @param {Object=} topts Producer topic options. + * @param {Object=} topicOptions Producer topic options. * @see https://github.com/edenhill/librdkafka/blob/2213fb29f98a7a73f22da21ef85e0783f6fd67c4/CONFIGURATION.md * @return {Promise(kafka.Producer)} A Promise. */ -Producer.prototype.getProducer = Promise.method(function (opts, topts) { +Producer.prototype.getProducer = Promise.method(function (opts, topicOptions) { if (!opts) { opts = {}; } @@ -34,7 +33,7 @@ Producer.prototype.getProducer = Promise.method(function (opts, topts) { log.info('getProducer() :: Starting producer with options:', opts); - var producer = new kafka.Producer(opts, topts); + const producer = new kafka.Producer(opts, topicOptions); this._producers.push(producer); @@ -42,15 +41,15 @@ Producer.prototype.getProducer = Promise.method(function (opts, topts) { producer.__kafkaAvro_produce = producer.produce; producer.produce = this._produceWrapper.bind(this, producer); - return new Promise(function(resolve, reject) { - producer.on('ready', function() { + return new Promise(function (resolve, reject) { + producer.on('ready', function () { log.debug('getProducer() :: Got "ready" event.'); resolve(producer); }); - producer.connect({}, function(err) { + producer.connect({}, function (err) { if (err) { - log.error('getProducer() :: Connect failed:', err); + log.error({err}, 'getProducer() :: Connect failed:'); reject(err); return; } @@ -78,13 +77,16 @@ Producer.prototype._serializeType = function (topicName, isKey, data) { ? this.sr.valueSchemas[subject] : this.sr.keySchemas[subject]; - var schemaType = isKey === false + const schemaType = isKey === false ? 'value' : 'key'; if (!schema) { - log.warn(`_produceWrapper() :: Warning, did not find topic on SR for ${subject} schema:`, - topicName); + log.warn('_produceWrapper() :: Warning, did not find topic on SR for subject schema:', + { + topic: topicName, + subject: subject + }); return (typeof data === 'object') ? new Buffer(JSON.stringify(data)) @@ -104,17 +106,17 @@ Producer.prototype._serializeType = function (topicName, isKey, data) { * @param {string} topicName The topic name. * @param {number} partition The partition to produce on. * @param {Object} value The message. - * @param {string|number} key The partioning key. + * @param {string|number} key The partitioning key. * @param {number} timestamp The create time value. * @param {*=} optOpaque Pass vars to receipt handler. */ Producer.prototype._produceWrapper = function (producerInstance, topicName, partition, value, key, timestamp, optOpaque) { - var sendKey = key + const sendKey = key ? this._serializeType(topicName, true, key) : null; - var sendValue = value + const sendValue = value ? this._serializeType(topicName, false, value) : null; diff --git a/lib/log.lib.js b/lib/log.lib.js index 5eae663..d8f899c 100644 --- a/lib/log.lib.js +++ b/lib/log.lib.js @@ -1,16 +1,20 @@ /** * @fileOverview Provides a Bunyan logger. */ -var bunyan = require('bunyan'); -var fmt = require('bunyan-format'); +const bunyan = require('bunyan'); +const fmt = require('bunyan-format'); -var shouldLog = !!process.env.KAFKA_AVRO_LOG_LEVEL; -var logLevel = process.env.KAFKA_AVRO_LOG_LEVEL || 'info'; -var noColors = !!process.env.KAFKA_AVRO_LOG_NO_COLORS; +const shouldLog = !!process.env.KAFKA_AVRO_LOG_LEVEL; +const logLevel = process.env.KAFKA_AVRO_LOG_LEVEL || 'info'; +const noColors = !!process.env.KAFKA_AVRO_LOG_NO_COLORS; + + +// FIXME: The users of this lib should be bale to inject their own loggers // default outstream mutes -var outStream = { - write: function() {} +let outStream = { + write: function () { + } }; if (shouldLog) { @@ -24,7 +28,7 @@ if (shouldLog) { /** * Create a singleton bunyan logger and expose it. */ -var logger = module.exports = bunyan.createLogger({ +const logger = module.exports = bunyan.createLogger({ name: 'KafkaAvro', level: logLevel, stream: outStream, @@ -35,11 +39,11 @@ var logger = module.exports = bunyan.createLogger({ * * Usage: var log = require('./util/logger').getChild(__filename); * - * @param {string} modulePath The full modulepath. + * @param {string} modulePath The full module path. * @return {bunyan.Child} A child logger to use. */ -logger.getChild = function(modulePath) { - var cleanModulePath = modulePath.split('kafka-avro/lib').pop(); +logger.getChild = function (modulePath) { + const cleanModulePath = modulePath.split('kafka-avro/lib').pop(); return logger.child({module: cleanModulePath}); }; diff --git a/lib/magic-byte.js b/lib/magic-byte.js index 568b256..a7f368b 100644 --- a/lib/magic-byte.js +++ b/lib/magic-byte.js @@ -2,11 +2,10 @@ * @fileOverview Encode and decode an Avro message for confluent SR * with magic byte. */ - -var magicByte = module.exports = {}; +const magicByte = module.exports = {}; /** @const {string} The magic byte value */ -var MAGIC_BYTE = 0; +const MAGIC_BYTE = 0; /** * Encode and decode an Avro value into a message, as expected by @@ -20,13 +19,13 @@ var MAGIC_BYTE = 0; * @return {Byte} Serialized value. */ magicByte.toMessageBuffer = function (val, type, schemaId, optLength) { - var length = optLength || 1024; - var buf = new Buffer(length); + const length = optLength || 1024; + const buf = new Buffer(length); buf[0] = MAGIC_BYTE; // Magic byte. buf.writeInt32BE(schemaId, 1); - var pos = type.encode(val, buf, 5); + const pos = type.encode(val, buf, 5); if (pos < 0) { // The buffer was too short, we need to resize. diff --git a/lib/schema-registry.js b/lib/schema-registry.js index f5824f6..113c0eb 100644 --- a/lib/schema-registry.js +++ b/lib/schema-registry.js @@ -1,15 +1,15 @@ /** * @fileOverview Queries the Schema Registry for all schemas and evaluates them. */ -var URL = require('url').URL; +const URL = require('url').URL; -var Promise = require('bluebird'); -var cip = require('cip'); -var axios = require('axios'); -var avro = require('avsc'); -var instance = axios; -var rootLog = require('./log.lib'); -var log = rootLog.getChild(__filename); +const Promise = require('bluebird'); +const cip = require('cip'); +const axios = require('axios'); +const avro = require('avsc'); +let instance = axios; +const rootLog = require('./log.lib'); +const log = rootLog.getChild(__filename); /** * Queries the Schema Registry for all schemas and evaluates them. @@ -21,7 +21,7 @@ var log = rootLog.getChild(__filename); * @param {?Object} parseOptions Schema parsing options to pass to avro.parse(). * @constructor */ -var SchemaRegistry = module.exports = cip.extend(function(opts) { +const SchemaRegistry = module.exports = cip.extend(function (opts) { if (opts.httpsAgent) { instance = axios.create({httpsAgent: opts.httpsAgent}); } @@ -98,10 +98,10 @@ var SchemaRegistry = module.exports = cip.extend(function(opts) { * @return {RecordType} An avro RecordType representing the parsed avro schema. */ function typeFromSchemaResponse(schema, parseOptions) { - var schemaType = avro.parse(schema); + const schemaType = avro.parse(schema); - //check if the schema has been previouisly parsed and added to the registry - if(typeof parseOptions.registry === 'object' && typeof parseOptions.registry[schemaType.name] !== 'undefined'){ + //check if the schema has been previously parsed and added to the registry + if (typeof parseOptions.registry === 'object' && typeof parseOptions.registry[schemaType.name] !== 'undefined') { return parseOptions.registry[schemaType.name]; } @@ -156,7 +156,7 @@ SchemaRegistry.prototype._checkForAllVersions = Promise.method( * @private */ SchemaRegistry.prototype._flatenResults = Promise.method(function (results) { - var flattenedResults = []; + const flattenedResults = []; results.map(function (schemaVersions) { schemaVersions.forEach(function (schemaVersion) { flattenedResults.push(schemaVersion); @@ -170,7 +170,7 @@ SchemaRegistry.prototype._flatenResults = Promise.method(function (results) { * A master wrapper method to determine if all topics or just specific ones * need to be fetched. * - * @return {Promise(Array.)} A Promise with an arrray of string topics. + * @return {Promise(Array.)} A Promise with an array of string topics. * @private */ SchemaRegistry.prototype._fetchTopics = Promise.method(function () { @@ -202,7 +202,7 @@ SchemaRegistry.prototype._storeTopics = Promise.method(function (schemaTopics) { * @private */ SchemaRegistry.prototype._processSelectedTopics = Promise.method(function () { - var topics = []; + const topics = []; this.selectedTopics.forEach(function (selectedTopic) { topics.push(selectedTopic + '-value'); @@ -215,12 +215,11 @@ SchemaRegistry.prototype._processSelectedTopics = Promise.method(function () { /** * Fetch all registered schema topics from SR. * - * @return {Promise(Array.)} A Promise with an arrray of string topics. + * @return {Promise(Array.)} A Promise with an array of string topics. * @private */ SchemaRegistry.prototype._fetchAllSchemaTopics = Promise.method(function () { - - var fetchAllTopicsUrl = new URL('subjects', this.schemaRegistryUrl).toString(); + const fetchAllTopicsUrl = new URL('subjects', this.schemaRegistryUrl).toString(); log.debug('_fetchAllSchemaTopics() :: Fetching all schemas using url:', fetchAllTopicsUrl); @@ -238,13 +237,13 @@ SchemaRegistry.prototype._fetchAllSchemaTopics = Promise.method(function () { /** * Fetch just the latest version for a topic from the SR. * - * @param {string} schemaTopic The topic to fetch the latest versionf for. + * @param {string} schemaTopic The topic to fetch the latest version for. * @return {Promise(Object)} A Promise with an object containing the * topic name and version number. * @private */ SchemaRegistry.prototype._fetchLatestVersion = Promise.method(function (schemaTopic) { - var fetchLatestVersionUrl = new URL( + const fetchLatestVersionUrl = new URL( 'subjects/' + schemaTopic + '/versions/latest', this.schemaRegistryUrl ).toString(); @@ -274,7 +273,7 @@ SchemaRegistry.prototype._fetchLatestVersion = Promise.method(function (schemaTo * @private */ SchemaRegistry.prototype._fetchAllSchemaVersions = Promise.method(function (schemaTopic) { - var fetchVersionsUrl = new URL( + const fetchVersionsUrl = new URL( 'subjects/' + schemaTopic + '/versions', this.schemaRegistryUrl ).toString(); @@ -304,11 +303,11 @@ SchemaRegistry.prototype._fetchAllSchemaVersions = Promise.method(function (sche * @private */ SchemaRegistry.prototype._fetchSchema = Promise.method(function (topicMeta) { - var schemaTopic = topicMeta.schemaTopic; - var version = topicMeta.version; - var parts = schemaTopic.split('-'); - var schemaType = parts.pop(); - var topic = parts.join('-'); + const schemaTopic = topicMeta.schemaTopic; + const version = topicMeta.version; + const parts = schemaTopic.split('-'); + const schemaType = parts.pop(); + const topic = parts.join('-'); var fetchSchemaUrl = new URL( 'subjects/' + schemaTopic + '/versions/' + version, @@ -343,20 +342,19 @@ SchemaRegistry.prototype._fetchSchema = Promise.method(function (topicMeta) { * @private */ SchemaRegistry.prototype._registerSchema = Promise.method(function (schemaObj) { - log.debug('_registerSchema() :: Registering schema:', - schemaObj.topic); + log.debug('_registerSchema() :: Registering schema', {schema: schemaObj}); try { schemaObj.type = typeFromSchemaResponse(schemaObj.responseRaw.schema, this.parseOptions); - } catch(ex) { - log.warn('_registerSchema() :: Error parsing schema:', - schemaObj.schemaTopicRaw, 'Error:', ex.message, 'Moving on...'); + } catch (err) { + log.warn({err}, '_registerSchema() :: Error parsing schema, moving on...', + {schema: schemaObj}); return schemaObj; } - log.debug('_registerSchema() :: Registered schema:', schemaObj.topic); + log.debug('_registerSchema() :: Registered schema', {schema: schemaObj}); - this.schemaTypeById['schema-' + schemaObj.responseRaw.id] = schemaObj.type; + this.schemaTypeById[`schema-${schemaObj.responseRaw.id}`] = schemaObj.type; return schemaObj; }); @@ -370,27 +368,27 @@ SchemaRegistry.prototype._registerSchema = Promise.method(function (schemaObj) { * @private */ SchemaRegistry.prototype._registerSchemaLatest = Promise.method(function (schemaObj) { - log.debug('_registerSchemaLatest() :: Registering schema:', - schemaObj.topic); + log.debug('_registerSchemaLatest() :: Registering schema', + {schema: schemaObj}); try { schemaObj.type = typeFromSchemaResponse(schemaObj.responseRaw.schema, this.parseOptions); - } catch (ex) { - log.warn('_registerSchemaLatest() :: Error parsing schema:', - schemaObj.schemaTopicRaw, 'Error:', ex.message, 'Moving on...'); + } catch (err) { + log.warn({err}, '_registerSchemaLatest() :: Error parsing schema, moving on...', + {schema: schemaObj}); return schemaObj; } log.debug('_registerSchemaLatest() :: Registered schema:', schemaObj.topic); if (schemaObj.schemaType.toLowerCase() === 'value') { - this.schemaTypeById['schema-' + schemaObj.responseRaw.id] = schemaObj.type; + this.schemaTypeById[`schema-${schemaObj.responseRaw.id}`] = schemaObj.type; this.valueSchemas[schemaObj.topic] = schemaObj.type; - this.schemaIds[schemaObj.topic + '-value'] = schemaObj.responseRaw.id; + this.schemaIds[`${schemaObj.topic}-value`] = schemaObj.responseRaw.id; } else { - this.schemaTypeById['schema-' + schemaObj.responseRaw.id] = schemaObj.type; + this.schemaTypeById[`schema-${schemaObj.responseRaw.id}`] = schemaObj.type; this.keySchemas[schemaObj.topic] = schemaObj.type; - this.schemaIds[schemaObj.topic + '-key'] = schemaObj.responseRaw.id; + this.schemaIds[`${schemaObj.topic}-key`] = schemaObj.responseRaw.id; } this.schemaMeta[schemaObj.topic] = schemaObj.responseRaw; return schemaObj; @@ -409,8 +407,10 @@ SchemaRegistry.prototype._handleAxiosError = function (err) { throw err; } - log.warn('_handleAxiosError() :: http error:', err.message, - 'Url:', err.config.url); + log.warn({err}, '_handleAxiosError() :: http error: message, url', { + message: err.message, + url: err.config.url + }); throw err; }; @@ -430,8 +430,11 @@ SchemaRegistry.prototype._suppressAxiosError = function (err) { throw err; } - log.debug('_suppressAxiosError() :: http error, will continue operation.', - 'Error:', err.message, 'Url:', err.config.url); + log.debug({err}, '_suppressAxiosError() :: http error for url, will continue operation.', + { + error: err.message, + url: err.config.url + }); return null; }; @@ -447,9 +450,9 @@ SchemaRegistry.prototype._fetchSchemas = function () { return this._fetchTopics() .bind(this) .then(this._storeTopics) - .map(this._fetchLatestVersion, { concurrency: 10 }) + .map(this._fetchLatestVersion, {concurrency: 10}) .filter(Boolean) - .map(this._fetchSchema, { concurrency: 10 }) + .map(this._fetchSchema, {concurrency: 10}) .map(this._registerSchemaLatest) .then(this._checkForAllVersions); }; From 8db6159cac35640188101032c1a0cb8f8f01f5d0 Mon Sep 17 00:00:00 2001 From: Piotr Leszczynski Date: Thu, 5 Sep 2019 13:04:27 +0200 Subject: [PATCH 05/62] refactoring(Support multiply types in a single topics): * Using let/const instead of var * Updating circleCi to 2.0 --- README.md | 22 ++--- circle.yml | 27 +++--- lib/kafka-producer.js | 2 +- lib/log.lib.js | 2 +- lib/schema-registry.js | 2 +- test/lib/test.lib.js | 46 +++++----- test/spec/consumer.test.js | 173 ++++++++++++++++++----------------- test/spec/magic-byte.test.js | 18 ++-- test/spec/producer.test.js | 30 +++--- 9 files changed, 159 insertions(+), 163 deletions(-) diff --git a/README.md b/README.md index 2a0404f..8e2f85b 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ You are highly encouraged to read the ["node-rdkafka" documentation](https://bli The `Kafka.CODES` enumeration of constant values provided by the "node-rdkafka" library is also available as a static var at: ```js -var KafkaAvro = require('kafka-avro'); +const KafkaAvro = require('kafka-avro'); console.log(KafkaAvro.CODES); ``` @@ -39,9 +39,9 @@ console.log(KafkaAvro.CODES); ### Initialize kafka-avro ```js -var KafkaAvro = require('kafka-avro'); +const KafkaAvro = require('kafka-avro'); -var kafkaAvro = new KafkaAvro({ +const kafkaAvro = new KafkaAvro({ kafkaBroker: 'localhost:9092', schemaRegistry: 'http://localhost:8081', }); @@ -78,17 +78,17 @@ kafkaAvro.getProducer({ }) // "getProducer()" returns a Bluebird Promise. .then(function(producer) { - var topicName = 'test'; + const topicName = 'test'; producer.on('disconnected', function(arg) { console.log('producer disconnected. ' + JSON.stringify(arg)); }); - var value = {name:'John'}; - var key = 'key'; + const value = {name:'John'}; + const key = 'key'; // if partition is set to -1, librdkafka will use the default partitioner - var partition = -1; + const partition = -1; producer.produce(topicName, partition, value, key); }) ``` @@ -133,7 +133,7 @@ kafkaAvro.getConsumer({ }) .then(function(consumer) { // Subscribe and consume. - var topicName = 'test'; + const topicName = 'test'; consumer.subscribe([topicName]); consumer.consume(); consumer.on('data', function(rawData) { @@ -218,10 +218,10 @@ The Kafka Avro library logs messages using the [Bunyan logger](https://github.co **Returns** {Bunyan.Logger} [Bunyan logger](https://github.com/trentm/node-bunyan/) instance. ```js -var KafkaAvro = require('kafka-avro'); -var fmt = require('bunyan-format'); +const KafkaAvro = require('kafka-avro'); +const fmt = require('bunyan-format'); -var kafkaLog = KafkaAvro.getLogger(); +const kafkaLog = KafkaAvro.getLogger(); kafkaLog.addStream({ type: 'stream', diff --git a/circle.yml b/circle.yml index 5004e6d..c5e6600 100644 --- a/circle.yml +++ b/circle.yml @@ -1,16 +1,11 @@ -machine: - pre: - - curl -sSL https://s3.amazonaws.com/circle-downloads/install-circleci-docker.sh | bash -s -- 1.10.0 - - pip install docker-compose - services: - - docker - node: - version: 8.2.0 - hosts: - kafka: 127.0.0.1 - -dependencies: - pre: - - docker-compose up -d - - sudo apt-get update; - - sudo apt-get -y install libsasl2-dev libssl-dev +version: 2 + jobs: + build: + docker: + - image: circleci/node + steps: + - docker-compose up -d + - sudo apt-get update; + - sudo apt-get -y install libsasl2-dev libssl-dev + - checkout + - run: npm test diff --git a/lib/kafka-producer.js b/lib/kafka-producer.js index a4df04c..38392d7 100644 --- a/lib/kafka-producer.js +++ b/lib/kafka-producer.js @@ -12,7 +12,7 @@ const log = require('./log.lib').getChild(__filename); * * @constructor */ -var Producer = module.exports = cip.extend(); +const Producer = module.exports = cip.extend(); /** * The wrapper of the node-rdkafka package Producer Ctor. diff --git a/lib/log.lib.js b/lib/log.lib.js index d8f899c..7f82473 100644 --- a/lib/log.lib.js +++ b/lib/log.lib.js @@ -37,7 +37,7 @@ const logger = module.exports = bunyan.createLogger({ /** * Get a child logger with a relative path to the provided full module path. * - * Usage: var log = require('./util/logger').getChild(__filename); + * Usage: const log = require('./util/logger').getChild(__filename); * * @param {string} modulePath The full module path. * @return {bunyan.Child} A child logger to use. diff --git a/lib/schema-registry.js b/lib/schema-registry.js index 113c0eb..07ead86 100644 --- a/lib/schema-registry.js +++ b/lib/schema-registry.js @@ -309,7 +309,7 @@ SchemaRegistry.prototype._fetchSchema = Promise.method(function (topicMeta) { const schemaType = parts.pop(); const topic = parts.join('-'); - var fetchSchemaUrl = new URL( + const fetchSchemaUrl = new URL( 'subjects/' + schemaTopic + '/versions/' + version, this.schemaRegistryUrl ).toString(); diff --git a/test/lib/test.lib.js b/test/lib/test.lib.js index 04987f6..7c6c9c8 100644 --- a/test/lib/test.lib.js +++ b/test/lib/test.lib.js @@ -1,24 +1,24 @@ /* * @fileOverview Main testing helper lib. */ -var axios = require('axios'); -var Promise = require('bluebird'); -var bunyan = require('bunyan'); +const axios = require('axios'); +const Promise = require('bluebird'); +const bunyan = require('bunyan'); -var fmt = require('bunyan-format'); +const fmt = require('bunyan-format'); // override to enable logging process.env.KAFKA_AVRO_LOG_LEVEL = 'debug'; -var KafkaAvro = require('../..'); +const KafkaAvro = require('../..'); -var schemaFix = require('../fixtures/schema.fix'); +const schemaFix = require('../fixtures/schema.fix'); -var schemaTwoFix = require('../fixtures/schema-two.fix'); +const schemaTwoFix = require('../fixtures/schema-two.fix'); -var keySchemaFix = require('../fixtures/key-schema.fix'); +const keySchemaFix = require('../fixtures/key-schema.fix'); -var testLib = module.exports = {}; +const testLib = module.exports = {}; testLib.log = bunyan.createLogger({ name: 'KafkaAvroTest', @@ -36,14 +36,14 @@ testLib.topic = schemaFix.name; testLib.topicTwo = schemaTwoFix.name; testLib.topicThreeWithDuplicateSchema = schemaFix.name + '-duplicateSchema'; -var testBoot = false; +let testBoot = false; /** * Require from all test scripts, prepares kafka for testing. * */ -testLib.init = function() { - beforeEach(function() { +testLib.init = function () { + beforeEach(function () { if (testBoot) { return; } @@ -73,7 +73,7 @@ testLib.init = function() { ]); }); - beforeEach(function() { + beforeEach(function () { let kafkaAvro = new KafkaAvro({ kafkaBroker: testLib.KAFKA_BROKER_URL, schemaRegistry: testLib.KAFKA_SCHEMA_REGISTRY_URL, @@ -98,17 +98,17 @@ testLib.init = function() { * @param {number=} retries how many times has retried. * @return {Promise} A Promise. */ -testLib.registerSchema = Promise.method(function(topic, schema, type, retries) { - var schemaCreateUrl = testLib.KAFKA_SCHEMA_REGISTRY_URL + +testLib.registerSchema = Promise.method(function (topic, schema, type, retries) { + const schemaCreateUrl = testLib.KAFKA_SCHEMA_REGISTRY_URL + '/subjects/' + topic + '-' + type + '/versions'; - var data = { + const data = { schema: JSON.stringify(schema), }; retries = retries || 0; - testLib.log.info('TEST :: Registering schema:', topic, 'on SR:', schemaCreateUrl); + testLib.log.info('TEST :: Registering schema subject in sr', {topic, sr: schemaCreateUrl}); return axios({ url: schemaCreateUrl, @@ -118,11 +118,11 @@ testLib.registerSchema = Promise.method(function(topic, schema, type, retries) { }, data: data, }) - .catch(function(err) { - testLib.log.error('Axios SR creation failed:', retries, err.message); + .catch(function (err) { + testLib.log.error({err}, 'Axios SR creation failed after noOfRetries', {noOfRetries: retries}); retries++; - return new Promise(function(resolve) { - setTimeout(function() { + return new Promise(function (resolve) { + setTimeout(function () { testLib.registerSchema(topic, schema, type, retries) .then(resolve); }, 1000); @@ -136,8 +136,8 @@ testLib.registerSchema = Promise.method(function(topic, schema, type, retries) { * @param {number} seconds cooldown in seconds. * @return {Function} use is beforeEach(). */ -testLib.cooldown = function(seconds) { - return function(done) { +testLib.cooldown = function (seconds) { + return function (done) { setTimeout(done, seconds); }; }; diff --git a/test/spec/consumer.test.js b/test/spec/consumer.test.js index b11d0e9..2b4ab7a 100644 --- a/test/spec/consumer.test.js +++ b/test/spec/consumer.test.js @@ -1,20 +1,21 @@ /** * @fileOverview Test produce and consume messages using kafka-avro. */ -var crypto = require('crypto'); +const crypto = require('crypto'); -var Promise = require('bluebird'); -var chai = require('chai'); -var expect = chai.expect; +const Promise = require('bluebird'); +const chai = require('chai'); +const expect = chai.expect; -var testLib = require('../lib/test.lib'); +const testLib = require('../lib/test.lib'); -function noop () {} +function noop() { +} -describe('Consume', function() { +describe('Consume', function () { testLib.init(); - beforeEach(function() { + beforeEach(function () { this.consOpts = { 'group.id': 'testKafkaAvro' + crypto.randomBytes(20).toString('hex'), 'enable.auto.commit': true, @@ -29,7 +30,7 @@ describe('Consume', function() { }); }); - beforeEach(function() { + beforeEach(function () { testLib.log.info('beforeEach 2 on Consume'); return this.kafkaAvro.getProducer({ 'dr_cb': true, @@ -39,16 +40,16 @@ describe('Consume', function() { testLib.log.info('beforeEach 2 on Consume: Got producer'); this.producer = producer; - producer.on('event.log', function(log) { + producer.on('event.log', function (log) { testLib.log.info('producer log:', log); }); //logging all errors - producer.on('error', function(err) { + producer.on('error', function (err) { testLib.log.error('Error from producer:', err); }); - producer.on('delivery-report', function(err, report) { + producer.on('delivery-report', function (err, report) { testLib.log.info('delivery-report:' + JSON.stringify(report)); this.gotReceipt = true; }.bind(this)); @@ -58,24 +59,24 @@ describe('Consume', function() { }); }); - afterEach(function() { + afterEach(function () { testLib.log.info('afterEach 1 on Consume: Disposing...'); return this.kafkaAvro.dispose() - .then(function() { + .then(function () { testLib.log.info('afterEach 1 on Consume: Disposed'); }); }); - describe('Consumer direct "on"', function() { + describe('Consumer direct "on"', function () { - beforeEach(function() { + beforeEach(function () { return new Promise(function (resolve, reject) { - this.consumer.on('ready', function() { + this.consumer.on('ready', function () { testLib.log.debug('getConsumer() :: Got "ready" event.'); resolve(); }); - this.consumer.connect({}, function(err) { + this.consumer.connect({}, function (err) { if (err) { testLib.log.error('getConsumer() :: Connect failed:', err); reject(err); @@ -87,24 +88,24 @@ describe('Consume', function() { }.bind(this)); }); - it('should produce and consume a message using consume "on"', function(done) { - var produceTime = 0; + it('should produce and consume a message using consume "on"', function (done) { + let produceTime = 0; - var message = { + const message = { name: 'Thanasis', long: 540, }; - var key = 'test-key'; + const key = 'test-key'; // //start consuming messages this.consumer.subscribe([testLib.topic]); this.consumer.consume(); - this.consumer.on('data', function(rawData) { - var dataValue = rawData.parsed; - var dataKey = rawData.parsedKey; - var diff = Date.now() - produceTime; + this.consumer.on('data', function (rawData) { + const dataValue = rawData.parsed; + const dataKey = rawData.parsedKey; + const diff = Date.now() - produceTime; testLib.log.info('Produce to consume time in ms:', diff); expect(dataValue).to.have.keys([ 'name', @@ -124,10 +125,10 @@ describe('Consume', function() { }, 10000); }); - it('should produce and consume a message using consume "on" with timestamp when provided', function(done) { - var produceTime = Date.parse('04 Dec 2015 00:12:00 GMT'); //use date in the past to guarantee we don't get Date.now() + it('should produce and consume a message using consume "on" with timestamp when provided', function (done) { + let produceTime = Date.parse('04 Dec 2015 00:12:00 GMT'); //use date in the past to guarantee we don't get Date.now() - var message = { + const message = { name: 'Thanasis', long: 540, }; @@ -136,7 +137,7 @@ describe('Consume', function() { this.consumer.subscribe([testLib.topic]); this.consumer.consume(); - this.consumer.on('data', function(rawData) { + this.consumer.on('data', function (rawData) { expect(rawData.timestamp).to.equal(produceTime); done(); }.bind(this)); @@ -147,26 +148,26 @@ describe('Consume', function() { }, 10000); }); - it('should produce and consume a message using consume "on", on a non Schema Registry topic', function(done) { - var produceTime = 0; + it('should produce and consume a message using consume "on", on a non Schema Registry topic', function (done) { + let produceTime = 0; - var topicName = 'testKafkaAvro' + crypto.randomBytes(20).toString('hex'); + const topicName = 'testKafkaAvro' + crypto.randomBytes(20).toString('hex'); - var message = { + const message = { name: 'Thanasis', long: 540, }; - var key = 'no-schema-key'; + const key = 'no-schema-key'; // //start consuming messages this.consumer.subscribe([topicName]); this.consumer.consume(); - this.consumer.on('data', function(rawData) { - var dataValue = rawData.parsed; - var dataKey = rawData.parsedKey; - var diff = Date.now() - produceTime; + this.consumer.on('data', function (rawData) { + const dataValue = rawData.parsed; + const dataKey = rawData.parsedKey; + const diff = Date.now() - produceTime; testLib.log.info('Produce to consume time in ms:', diff); expect(dataValue).to.have.keys([ 'name', @@ -187,15 +188,15 @@ describe('Consume', function() { }, 10000); }); - it('should produce and consume on two topics using a single consumer', function(done) { - var produceTime = 0; + it('should produce and consume on two topics using a single consumer', function (done) { + let produceTime = 0; - var message = { + const message = { name: 'Thanasis', long: 540, }; - var key = 'two-topics'; + const key = 'two-topics'; // //start consuming messages this.consumer.subscribe([ @@ -204,19 +205,19 @@ describe('Consume', function() { ]); this.consumer.consume(); - var receivedOne = false; - var receivedTwo = false; + let receivedOne = false; + let receivedTwo = false; - this.consumer.on('data', function(rawData) { + this.consumer.on('data', function (rawData) { if (rawData.topic === testLib.topic) { receivedOne = true; } else { receivedTwo = true; } - var dataValue = rawData.parsed; - var dataKey = rawData.parsedKey; - var diff = Date.now() - produceTime; + const dataValue = rawData.parsed; + const dataKey = rawData.parsedKey; + const diff = Date.now() - produceTime; testLib.log.info('Produce to consume time in ms:', diff); expect(dataValue).to.have.keys([ 'name', @@ -239,44 +240,44 @@ describe('Consume', function() { }); }); - describe('Consume using Streams', function() { - it('should produce and consume a message using streams on two topics', function(done) { - var produceTime = 0; + describe('Consume using Streams', function () { + it('should produce and consume a message using streams on two topics', function (done) { + let produceTime = 0; - var message = { + const message = { name: 'Thanasis', long: 540, }; - var key = 'key-stream'; + const key = 'key-stream'; - var isDone = false; + let isDone = false; - this.kafkaAvro.getConsumerStream(this.consOpts, { 'enable.auto.commit': true }, { topics: [ testLib.topic, testLib.topicTwo ] }) - .then(function (consumerStream) { - consumerStream.on('error', noop); + this.kafkaAvro.getConsumerStream(this.consOpts, {'enable.auto.commit': true}, {topics: [testLib.topic, testLib.topicTwo]}) + .then(function (consumerStream) { + consumerStream.on('error', noop); - consumerStream.on('data', function(dataRaw) { - var dataValue = dataRaw.parsed; - var dataKey = dataRaw.parsedKey; - var diff = Date.now() - produceTime; - testLib.log.info('Produce to consume time in ms:', diff); - expect(dataValue).to.have.keys([ - 'name', - 'long', - ]); + consumerStream.on('data', function (dataRaw) { + const dataValue = dataRaw.parsed; + const dataKey = dataRaw.parsedKey; + const diff = Date.now() - produceTime; + testLib.log.info('Produce to consume time in ms:', diff); + expect(dataValue).to.have.keys([ + 'name', + 'long', + ]); - expect(dataValue.name).to.equal(message.name); - expect(dataValue.long).to.equal(message.long); - expect(dataKey).to.equal(key); + expect(dataValue.name).to.equal(message.name); + expect(dataValue.long).to.equal(message.long); + expect(dataKey).to.equal(key); - if (!isDone) { - consumerStream.consumer.disconnect(); - done(); - } - isDone = true; + if (!isDone) { + consumerStream.consumer.disconnect(); + done(); + } + isDone = true; + }); }); - }); setTimeout(() => { produceTime = Date.now(); @@ -285,26 +286,26 @@ describe('Consume', function() { }, 10000); }); - it('should produce and consume a message using streams on a not SR topic', function(done) { - var produceTime = 0; + it('should produce and consume a message using streams on a not SR topic', function (done) { + let produceTime = 0; - var topicName = 'testKafkaAvro' + crypto.randomBytes(20).toString('hex'); + const topicName = 'testKafkaAvro' + crypto.randomBytes(20).toString('hex'); - var message = { + const message = { name: 'Thanasis', long: 540, }; - var key = 'not-sr-key'; + const key = 'not-sr-key'; - this.kafkaAvro.getConsumerStream(this.consOpts, { 'enable.auto.commit': true }, { topics: topicName }) + this.kafkaAvro.getConsumerStream(this.consOpts, {'enable.auto.commit': true}, {topics: topicName}) .then(function (consumerStream) { consumerStream.on('error', noop); - consumerStream.on('data', function(dataRaw) { - var dataValue = dataRaw.parsed; - var dataKey = dataRaw.parsedKey; - var diff = Date.now() - produceTime; + consumerStream.on('data', function (dataRaw) { + const dataValue = dataRaw.parsed; + const dataKey = dataRaw.parsedKey; + const diff = Date.now() - produceTime; testLib.log.info('Produce to consume time in ms:', diff); expect(dataValue).to.have.keys([ 'name', diff --git a/test/spec/magic-byte.test.js b/test/spec/magic-byte.test.js index eb8374e..c0d22e6 100644 --- a/test/spec/magic-byte.test.js +++ b/test/spec/magic-byte.test.js @@ -1,18 +1,18 @@ /** * @fileOverview Test Magic Byte implementation. */ -var chai = require('chai'); -var expect = chai.expect; -var avro = require('avsc'); +const chai = require('chai'); +const expect = chai.expect; +const avro = require('avsc'); -// var testLib = require('../lib/test.lib'); -var magicByte = require('../../lib/magic-byte'); +// const testLib = require('../lib/test.lib'); +const magicByte = require('../../lib/magic-byte'); -var schemaFix = require('../fixtures/schema.fix'); +const schemaFix = require('../fixtures/schema.fix'); -describe('Magic Byte', function() { +describe('Magic Byte', function () { - it('should encode a large message', function() { + it('should encode a large message', function () { const message = { name: new Array(40000).join('0'), long: 540, @@ -23,7 +23,7 @@ describe('Magic Byte', function() { magicByte.toMessageBuffer(message, type, 109); }); - it('should extract schemaId from encoded message', function() { + it('should extract schemaId from encoded message', function () { const message = { name: new Array(40).join('0'), long: 540, diff --git a/test/spec/producer.test.js b/test/spec/producer.test.js index 093397e..a418f7b 100644 --- a/test/spec/producer.test.js +++ b/test/spec/producer.test.js @@ -6,46 +6,46 @@ const expect = chai.expect; const testLib = require('../lib/test.lib'); -describe('Produce', function() { +describe('Produce', function () { testLib.init(); - beforeEach(function() { + beforeEach(function () { return this.kafkaAvro.getProducer({ 'dr_cb': true, }) .then(function (producer) { this.producer = producer; - producer.on('event.log', function(log) { + producer.on('event.log', function (log) { testLib.log.info('producer log:', log); }); //logging all errors - producer.on('error', function(err) { + producer.on('error', function (err) { testLib.log.error('Error from producer:', err); }); - producer.on('delivery-report', function() { + producer.on('delivery-report', function () { this.gotReceipt = true; }.bind(this)); this.topicName = testLib.topic; }.bind(this)); }); - afterEach(function(done) { - this.producer.disconnect(function(err) { + afterEach(function (done) { + this.producer.disconnect(function (err) { done(err); }); }); - it('should produce a message with serialized key', function(done) { + it('should produce a message with serialized key', function (done) { const message = { name: 'Thanasis', long: 540, }; const key = 'key'; const that = this; - this.producer.on('delivery-report', function(err, report) { + this.producer.on('delivery-report', function (err, report) { const schemaId = report.key.readInt32BE(1); const schemaType = that.sr.schemaTypeById[('schema-' + schemaId)]; const parsedKey = schemaType.decode(report.key, 5).value; @@ -67,7 +67,7 @@ describe('Produce', function() { } }, 1000); }); - it('should produce a message with an opaque value in delivery report', function(done) { + it('should produce a message with an opaque value in delivery report', function (done) { const message = { name: 'Thanasis', long: 540, @@ -77,7 +77,7 @@ describe('Produce', function() { const eventTime = Date.now(); const opaqueRef = 'my-opaque-ref'; - this.producer.on('delivery-report', function(err, report) { + this.producer.on('delivery-report', function (err, report) { expect(err).to.equal(null); expect(report.opaque).to.equal(opaqueRef); this.gotReceipt = true; @@ -94,7 +94,7 @@ describe('Produce', function() { } }, 1000); }); - it('should not allow invalid type', function() { + it('should not allow invalid type', function () { const message = { name: 'Thanasis', long: '540', @@ -105,14 +105,14 @@ describe('Produce', function() { expect(binded).to.throw(Error); }); - it('should not allow less attributes', function() { + it('should not allow less attributes', function () { const message = { name: 'Thanasis', }; - const binded = this.producer.produce.bind(this.producer, this.topicName, -1, message, 'key'); + const banded = this.producer.produce.bind(this.producer, this.topicName, -1, message, 'key'); - expect(binded).to.throw(Error); + expect(banded).to.throw(Error); }); }); From 2e4fd932d362ecb3300dba7af3fb4f00986d27e2 Mon Sep 17 00:00:00 2001 From: Piotr Leszczynski Date: Thu, 5 Sep 2019 13:09:31 +0200 Subject: [PATCH 06/62] chor(Support multiply types in a single topics): Enable circleCi --- README.md | 2 +- circle.yml | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 8e2f85b..a5fcf51 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ > Node.js bindings for librdkafka with Avro schema serialization. -[![CircleCI](https://circleci.com/gh/waldophotos/kafka-avro/tree/master.svg?style=svg)](https://circleci.com/gh/waldophotos/kafka-avro/tree/master) +[![CircleCI](https://circleci.com/gh/pleszczy/workflows/kafka-avro/tree/master.svg?style=svg)](https://circleci.com/gh/pleszczy/workflows/kafka-avro/tree/master) The kafka-avro library is a wrapper that combines the [node-rdkafka][node-rdkafka] and [avsc][avsc] libraries to allow for Production and Consumption of messages on kafka validated and serialized by Avro. diff --git a/circle.yml b/circle.yml index c5e6600..ccbf5a0 100644 --- a/circle.yml +++ b/circle.yml @@ -1,11 +1,11 @@ version: 2 - jobs: - build: - docker: - - image: circleci/node - steps: - - docker-compose up -d - - sudo apt-get update; - - sudo apt-get -y install libsasl2-dev libssl-dev - - checkout - - run: npm test +jobs: + build: + docker: + - image: circleci/node + steps: + - docker-compose up -d + - sudo apt-get update; + - sudo apt-get -y install libsasl2-dev libssl-dev + - checkout + - run: npm test From 7204e4c465d76b5792489839ad08d1a5fbf840fa Mon Sep 17 00:00:00 2001 From: Piotr Leszczynski Date: Thu, 5 Sep 2019 13:13:21 +0200 Subject: [PATCH 07/62] chor(Support multiply types in a single topics): Enable circleCi --- circle.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/circle.yml b/circle.yml index ccbf5a0..11529b8 100644 --- a/circle.yml +++ b/circle.yml @@ -4,6 +4,10 @@ jobs: docker: - image: circleci/node steps: + - pip install docker-compose +# - sudo curl -L "https://github.com/docker/compose/releases/download/1.23.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose +# - sudo chmod +x /usr/local/bin/docker-compose + - docker-compose --version - docker-compose up -d - sudo apt-get update; - sudo apt-get -y install libsasl2-dev libssl-dev From a18d64404eb073f5c826f5618dd4dc32c63db125 Mon Sep 17 00:00:00 2001 From: Piotr Leszczynski Date: Thu, 5 Sep 2019 13:13:54 +0200 Subject: [PATCH 08/62] chor(Support multiply types in a single topics): Enable circleCi --- circle.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/circle.yml b/circle.yml index 11529b8..50a9997 100644 --- a/circle.yml +++ b/circle.yml @@ -4,9 +4,8 @@ jobs: docker: - image: circleci/node steps: - - pip install docker-compose -# - sudo curl -L "https://github.com/docker/compose/releases/download/1.23.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose -# - sudo chmod +x /usr/local/bin/docker-compose + - sudo curl -L "https://github.com/docker/compose/releases/download/1.23.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose + - sudo chmod +x /usr/local/bin/docker-compose - docker-compose --version - docker-compose up -d - sudo apt-get update; From 70992bcc7b4e14724ea7e8107710143eceafd677 Mon Sep 17 00:00:00 2001 From: Piotr Leszczynski Date: Thu, 5 Sep 2019 13:27:56 +0200 Subject: [PATCH 09/62] chor(Support multiply types in a single topics): Enable circleCi --- circle.yml | 44 +- package-lock.json | 1116 +++++++++++++++++++++++++++++++++++++++++++++ package.json | 4 +- 3 files changed, 1157 insertions(+), 7 deletions(-) diff --git a/circle.yml b/circle.yml index 50a9997..303ac09 100644 --- a/circle.yml +++ b/circle.yml @@ -4,11 +4,43 @@ jobs: docker: - image: circleci/node steps: - - sudo curl -L "https://github.com/docker/compose/releases/download/1.23.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose - - sudo chmod +x /usr/local/bin/docker-compose - - docker-compose --version - - docker-compose up -d - - sudo apt-get update; - - sudo apt-get -y install libsasl2-dev libssl-dev - checkout + - run: + name: Install Docker Compose + command: | + curl -L https://github.com/docker/compose/releases/download/1.19.0/docker-compose-`uname -s`-`uname -m` > ~/docker-compose + chmod +x ~/docker-compose + sudo mv ~/docker-compose /usr/local/bin/docker-compose + - run: + name: Verify docker-compose was installed + command: 'docker-compose --version' + - run: + name: Update npm + command: 'sudo npm install -g npm@latest' + - restore_cache: + key: dependency-cache-{{ checksum "package.json" }} + - save_cache: + key: dependency-cache-{{ checksum "package.json" }} + paths: + - node_modules + test: + docker: + - image: circleci/node + steps: + - checkout + - run: + name: Start kafka and schema-registry + command: docker-compose up -d + - run: + name: Test + command: npm test + - run: + name: Generate code coverage + command: './node_modules/.bin/nyc report --reporter=text-lcov' + - store_artifacts: + path: test-results.xml + prefix: tests + - store_artifacts: + path: coverage + prefix: coverage - run: npm test diff --git a/package-lock.json b/package-lock.json index ee6ef21..ad5ece9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,6 +4,173 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@babel/code-frame": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", + "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", + "dev": true, + "requires": { + "@babel/highlight": "^7.0.0" + } + }, + "@babel/generator": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.5.5.tgz", + "integrity": "sha512-ETI/4vyTSxTzGnU2c49XHv2zhExkv9JHLTwDAFz85kmcwuShvYG2H08FwgIguQf4JC75CBnXAUM5PqeF4fj0nQ==", + "dev": true, + "requires": { + "@babel/types": "^7.5.5", + "jsesc": "^2.5.1", + "lodash": "^4.17.13", + "source-map": "^0.5.0", + "trim-right": "^1.0.1" + } + }, + "@babel/helper-function-name": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz", + "integrity": "sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.0.0", + "@babel/template": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz", + "integrity": "sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz", + "integrity": "sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q==", + "dev": true, + "requires": { + "@babel/types": "^7.4.4" + } + }, + "@babel/highlight": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.5.0.tgz", + "integrity": "sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ==", + "dev": true, + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@babel/parser": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.5.5.tgz", + "integrity": "sha512-E5BN68cqR7dhKan1SfqgPGhQ178bkVKpXTPEXnFJBrEt8/DKRZlybmy+IgYLTeN7tp1R5Ccmbm2rBk17sHYU3g==", + "dev": true + }, + "@babel/template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.4.4.tgz", + "integrity": "sha512-CiGzLN9KgAvgZsnivND7rkA+AeJ9JB0ciPOD4U59GKbQP2iQl+olF1l76kJOupqidozfZ32ghwBEJDhnk9MEcw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/parser": "^7.4.4", + "@babel/types": "^7.4.4" + } + }, + "@babel/traverse": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.5.5.tgz", + "integrity": "sha512-MqB0782whsfffYfSjH4TM+LMjrJnhCNEDMDIjeTpl+ASaUvxcjoiVCo/sM1GhS1pHOXYfWVCYneLjMckuUxDaQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.5.5", + "@babel/generator": "^7.5.5", + "@babel/helper-function-name": "^7.1.0", + "@babel/helper-split-export-declaration": "^7.4.4", + "@babel/parser": "^7.5.5", + "@babel/types": "^7.5.5", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.13" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "@babel/types": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.5.5.tgz", + "integrity": "sha512-s63F9nJioLqOlW3UkyMd+BYhXt44YuaFm/VV0VwuteqjYwRrObkU7ra9pY4wAJR3oXi8hJrMcrcJdO/HH33vtw==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + } + }, "@sinonjs/commons": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.6.0.tgz", @@ -120,6 +287,21 @@ "resolved": "https://registry.npmjs.org/ansistyles/-/ansistyles-0.1.3.tgz", "integrity": "sha1-XeYEFb2gcbs3EnhUyGT0GyMlRTk=" }, + "append-transform": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-1.0.0.tgz", + "integrity": "sha512-P009oYkeHyU742iSZJzZZywj4QRJdnTWffaKuJQLablCZ1uz6/cW4yaRgcDaoQ+uwOxxnt0gRUcwfsNP2ri0gw==", + "dev": true, + "requires": { + "default-require-extensions": "^2.0.0" + } + }, + "archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", + "dev": true + }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -237,6 +419,18 @@ "xtend": "~2.1.1" } }, + "caching-transform": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-3.0.2.tgz", + "integrity": "sha512-Mtgcv3lh3U0zRii/6qVgQODdPA4G3zhG+jtbCWj39RXuUFTMzH0vcdMtaJS1jPowd+It2Pqr6y3NJMQqOqCE2w==", + "dev": true, + "requires": { + "hasha": "^3.0.0", + "make-dir": "^2.0.0", + "package-hash": "^3.0.0", + "write-file-atomic": "^2.4.2" + } + }, "caller-path": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", @@ -318,6 +512,51 @@ "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", "dev": true }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, "co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -372,6 +611,12 @@ "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", "dev": true }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, "component-emitter": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", @@ -395,6 +640,15 @@ "typedarray": "^0.0.6" } }, + "convert-source-map": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz", + "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, "cookiejar": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.0.6.tgz", @@ -407,6 +661,37 @@ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "dev": true }, + "cp-file": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/cp-file/-/cp-file-6.2.0.tgz", + "integrity": "sha512-fmvV4caBnofhPe8kOcitBwSn2f39QLjnAnGq3gO9dfd75mUytzKNZB1hde6QHunW2Rt+OwuBOMc3i1tNElbszA==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "make-dir": "^2.0.0", + "nested-error-stacks": "^2.0.0", + "pify": "^4.0.1", + "safe-buffer": "^5.0.1" + }, + "dependencies": { + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + } + } + }, + "cross-spawn": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz", + "integrity": "sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE=", + "dev": true, + "requires": { + "lru-cache": "^4.0.1", + "which": "^1.2.9" + } + }, "currently-unhandled": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", @@ -473,6 +758,15 @@ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", "dev": true }, + "default-require-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-2.0.0.tgz", + "integrity": "sha1-9fj7sYp9bVCyH2QfZJ67Uiz+JPc=", + "dev": true, + "requires": { + "strip-bom": "^3.0.0" + } + }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -503,6 +797,12 @@ "nan": "^2.14.0" } }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, "error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -523,6 +823,12 @@ "next-tick": "^1.0.0" } }, + "es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true + }, "es6-iterator": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", @@ -771,6 +1077,17 @@ "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" }, + "find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + } + }, "find-up": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", @@ -850,6 +1167,16 @@ "debug": "^2.2.0" } }, + "foreground-child": { + "version": "1.5.6", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-1.5.6.tgz", + "integrity": "sha1-T9ca0t/elnibmApcCilZN8svXOk=", + "dev": true, + "requires": { + "cross-spawn": "^4", + "signal-exit": "^3.0.0" + } + }, "form-data": { "version": "1.0.0-rc3", "resolved": "https://registry.npmjs.org/form-data/-/form-data-1.0.0-rc3.tgz", @@ -891,6 +1218,12 @@ "is-property": "^1.0.0" } }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, "get-stdin": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", @@ -1104,6 +1437,26 @@ "superagent": "^1.8.3" } }, + "handlebars": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.2.0.tgz", + "integrity": "sha512-Kb4xn5Qh1cxAKvQnzNWZ512DhABzyFNmsaJf3OAkWNa4NkaqWcNI8Tao8Tasi0/F4JD9oyG0YxuFyvyR57d+Gw==", + "dev": true, + "requires": { + "neo-async": "^2.6.0", + "optimist": "^0.6.1", + "source-map": "^0.6.1", + "uglify-js": "^3.1.4" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, "has-ansi": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", @@ -1119,6 +1472,15 @@ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, + "hasha": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hasha/-/hasha-3.0.0.tgz", + "integrity": "sha1-UqMvq4Vp1BymmmH/GiFPjrfIvTk=", + "dev": true, + "requires": { + "is-stream": "^1.0.1" + } + }, "he": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", @@ -1271,6 +1633,12 @@ "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", "dev": true }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + }, "is-utf8": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", @@ -1289,6 +1657,134 @@ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, + "istanbul-lib-coverage": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", + "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", + "dev": true + }, + "istanbul-lib-hook": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-2.0.7.tgz", + "integrity": "sha512-vrRztU9VRRFDyC+aklfLoeXyNdTfga2EI3udDGn4cZ6fpSXpHLV9X6CHvfoMCPtggg8zvDDmC4b9xfu0z6/llA==", + "dev": true, + "requires": { + "append-transform": "^1.0.0" + } + }, + "istanbul-lib-instrument": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz", + "integrity": "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==", + "dev": true, + "requires": { + "@babel/generator": "^7.4.0", + "@babel/parser": "^7.4.3", + "@babel/template": "^7.4.0", + "@babel/traverse": "^7.4.3", + "@babel/types": "^7.4.0", + "istanbul-lib-coverage": "^2.0.5", + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "istanbul-lib-report": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz", + "integrity": "sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ==", + "dev": true, + "requires": { + "istanbul-lib-coverage": "^2.0.5", + "make-dir": "^2.1.0", + "supports-color": "^6.1.0" + }, + "dependencies": { + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "istanbul-lib-source-maps": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz", + "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^2.0.5", + "make-dir": "^2.1.0", + "rimraf": "^2.6.3", + "source-map": "^0.6.1" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "glob": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", + "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "istanbul-reports": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.2.6.tgz", + "integrity": "sha512-SKi4rnMyLBKe0Jy2uUdx28h8oG7ph2PPuQPvIAh31d+Ci+lSiEu4C+h3oBPuJ9+mPKhOyW0M8gY4U5NM1WLeXA==", + "dev": true, + "requires": { + "handlebars": "^4.1.2" + } + }, "js-tokens": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", @@ -1305,6 +1801,18 @@ "esprima": "^4.0.0" } }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, "json-stable-stringify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", @@ -1366,12 +1874,36 @@ } } }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "dependencies": { + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + } + } + }, "lodash": { "version": "4.17.15", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", "dev": true }, + "lodash.flattendeep": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", + "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", + "dev": true + }, "lodash.get": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", @@ -1394,6 +1926,34 @@ "signal-exit": "^3.0.0" } }, + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "dependencies": { + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + } + } + }, "map-obj": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", @@ -1426,6 +1986,23 @@ } } }, + "merge-source-map": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz", + "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==", + "dev": true, + "requires": { + "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, "methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", @@ -1572,6 +2149,18 @@ "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=", "optional": true }, + "neo-async": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz", + "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==", + "dev": true + }, + "nested-error-stacks": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/nested-error-stacks/-/nested-error-stacks-2.1.0.tgz", + "integrity": "sha512-AO81vsIO1k1sM4Zrd6Hu7regmJN1NSiAja10gc4bX3F0wd+9rQmcuHQaHVQCYIEC8iFXnE+mavh23GOt7wBgug==", + "dev": true + }, "next-tick": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", @@ -1645,6 +2234,79 @@ "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", "dev": true }, + "nyc": { + "version": "14.1.1", + "resolved": "https://registry.npmjs.org/nyc/-/nyc-14.1.1.tgz", + "integrity": "sha512-OI0vm6ZGUnoGZv/tLdZ2esSVzDwUC88SNs+6JoSOMVxA+gKMB8Tk7jBwgemLx4O40lhhvZCVw1C+OYLOBOPXWw==", + "dev": true, + "requires": { + "archy": "^1.0.0", + "caching-transform": "^3.0.2", + "convert-source-map": "^1.6.0", + "cp-file": "^6.2.0", + "find-cache-dir": "^2.1.0", + "find-up": "^3.0.0", + "foreground-child": "^1.5.6", + "glob": "^7.1.3", + "istanbul-lib-coverage": "^2.0.5", + "istanbul-lib-hook": "^2.0.7", + "istanbul-lib-instrument": "^3.3.0", + "istanbul-lib-report": "^2.0.8", + "istanbul-lib-source-maps": "^3.0.6", + "istanbul-reports": "^2.2.4", + "js-yaml": "^3.13.1", + "make-dir": "^2.1.0", + "merge-source-map": "^1.1.0", + "resolve-from": "^4.0.0", + "rimraf": "^2.6.3", + "signal-exit": "^3.0.2", + "spawn-wrap": "^1.4.2", + "test-exclude": "^5.2.3", + "uuid": "^3.3.2", + "yargs": "^13.2.2", + "yargs-parser": "^13.0.0" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "glob": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", + "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -1670,6 +2332,24 @@ "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", "dev": true }, + "optimist": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", + "dev": true, + "requires": { + "minimist": "~0.0.1", + "wordwrap": "~0.0.2" + }, + "dependencies": { + "wordwrap": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", + "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", + "dev": true + } + } + }, "optionator": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", @@ -1690,6 +2370,42 @@ "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "dev": true }, + "p-limit": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", + "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "package-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-3.0.0.tgz", + "integrity": "sha512-lOtmukMDVvtkL84rJHI7dpTYq+0rli8N2wlnqUcBuDWCfVhRUfOmnR9SsoHFMLpACvEV60dX7rd0rFaYDZI+FA==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.15", + "hasha": "^3.0.0", + "lodash.flattendeep": "^4.4.0", + "release-zalgo": "^1.0.0" + } + }, "parse-json": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", @@ -1774,6 +2490,26 @@ "pinkie": "^2.0.0" } }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "requires": { + "find-up": "^3.0.0" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + } + } + }, "pluralize": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-1.2.1.tgz", @@ -1798,6 +2534,12 @@ "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=", "dev": true }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "dev": true + }, "q": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", @@ -1882,6 +2624,15 @@ "integrity": "sha1-4Mk1QsV0UhvqE98PlIjtgqt3xdo=", "dev": true }, + "release-zalgo": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", + "integrity": "sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA=", + "dev": true, + "requires": { + "es6-error": "^4.0.1" + } + }, "repeating": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", @@ -1891,6 +2642,18 @@ "is-finite": "^1.0.0" } }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, "require-uncached": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", @@ -1980,6 +2743,12 @@ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, "shelljs": { "version": "0.7.8", "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.7.8.tgz", @@ -2051,6 +2820,51 @@ "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=", "dev": true }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "spawn-wrap": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-1.4.3.tgz", + "integrity": "sha512-IgB8md0QW/+tWqcavuFgKYR/qIRvJkRLPJDFaoXtLLUaVcCDK0+HeFTkmQHj3eprcYhc+gOl0aEA1w7qZlYezw==", + "dev": true, + "requires": { + "foreground-child": "^1.5.6", + "mkdirp": "^0.5.0", + "os-homedir": "^1.0.1", + "rimraf": "^2.6.2", + "signal-exit": "^3.0.2", + "which": "^1.3.0" + }, + "dependencies": { + "glob": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", + "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, "spdx-correct": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", @@ -2237,6 +3051,101 @@ } } }, + "test-exclude": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.2.3.tgz", + "integrity": "sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g==", + "dev": true, + "requires": { + "glob": "^7.1.3", + "minimatch": "^3.0.4", + "read-pkg-up": "^4.0.0", + "require-main-filename": "^2.0.0" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "glob": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", + "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dev": true, + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + } + }, + "read-pkg-up": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz", + "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==", + "dev": true, + "requires": { + "find-up": "^3.0.0", + "read-pkg": "^3.0.0" + } + } + } + }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -2249,12 +3158,24 @@ "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", "dev": true }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + }, "trim-newlines": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", "dev": true }, + "trim-right": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", + "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", + "dev": true + }, "type": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/type/-/type-1.0.3.tgz", @@ -2282,6 +3203,33 @@ "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", "dev": true }, + "uglify-js": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.0.tgz", + "integrity": "sha512-W+jrUHJr3DXKhrsS7NUVxn3zqMOFn0hL/Ei6v0anCIMoKC93TjcflTagwIHLW7SfMFfiQuktQyFVCFHGUE0+yg==", + "dev": true, + "optional": true, + "requires": { + "commander": "~2.20.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "commander": { + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", + "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==", + "dev": true, + "optional": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true + } + } + }, "underscore.string": { "version": "3.3.5", "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-3.3.5.tgz", @@ -2307,6 +3255,12 @@ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true }, + "uuid": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", + "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==", + "dev": true + }, "validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", @@ -2326,12 +3280,72 @@ "isexe": "^2.0.0" } }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, "wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", "dev": true }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -2346,6 +3360,17 @@ "mkdirp": "^0.5.1" } }, + "write-file-atomic": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", + "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.2" + } + }, "xtend": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz", @@ -2353,6 +3378,97 @@ "requires": { "object-keys": "~0.4.0" } + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "dev": true + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + }, + "yargs": { + "version": "13.3.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz", + "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==", + "dev": true, + "requires": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.1" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "yargs-parser": { + "version": "13.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz", + "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "dependencies": { + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + } + } } } } diff --git a/package.json b/package.json index 3438567..089df35 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,8 @@ "node": ">=0.12" }, "scripts": { - "test": "eslint lib && mocha -b test/spec && eslint lib/**" + "test": "eslint lib && mocha -b test/spec && eslint lib/**", + "coverage": "nyc npm run test" }, "dependencies": { "avsc": "^5.2.3", @@ -41,6 +42,7 @@ "grunt": "^1.0.1", "grunt-release": "^0.14.0", "mocha": "^5.0.5", + "nyc": "^14.1.1", "sinon": "^4.4.9" } } From de4a90a80ab7dd7a6f9908c887d3199e0d883c2f Mon Sep 17 00:00:00 2001 From: Piotr Leszczynski Date: Thu, 5 Sep 2019 13:29:32 +0200 Subject: [PATCH 10/62] chore(Setting up circleCi) --- circle.yml | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/circle.yml b/circle.yml index 303ac09..7ad2f19 100644 --- a/circle.yml +++ b/circle.yml @@ -2,7 +2,7 @@ version: 2 jobs: build: docker: - - image: circleci/node + - image: circleci/node steps: - checkout - run: @@ -44,3 +44,14 @@ jobs: path: coverage prefix: coverage - run: npm test + workflows: + version: 2 + build_and_test: + jobs: + - build + - test: + requires: + - build + filters: + branches: + only: master From e58d81b6b03ca93171b324f2625f532aeb4f7126 Mon Sep 17 00:00:00 2001 From: Piotr Leszczynski Date: Thu, 5 Sep 2019 13:31:32 +0200 Subject: [PATCH 11/62] chore(Setting up circleCi) --- circle.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/circle.yml b/circle.yml index 7ad2f19..1485a88 100644 --- a/circle.yml +++ b/circle.yml @@ -52,6 +52,3 @@ jobs: - test: requires: - build - filters: - branches: - only: master From 3102dc6f69be1fd9165ccca1a54bf2c09046a0c8 Mon Sep 17 00:00:00 2001 From: Piotr Leszczynski Date: Thu, 5 Sep 2019 13:40:08 +0200 Subject: [PATCH 12/62] chore(Setting up circleCi) --- circle.yml | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/circle.yml b/circle.yml index 1485a88..3c13ea9 100644 --- a/circle.yml +++ b/circle.yml @@ -44,11 +44,16 @@ jobs: path: coverage prefix: coverage - run: npm test - workflows: - version: 2 - build_and_test: - jobs: - - build - - test: - requires: - - build + verifyWorkflowIsWorking: + docker: + - image: circleci/node + steps: + - run: echo "workflows are enabled" + workflows: + version: 2 + build_and_test: + jobs: + - build + - test: + requires: + - build From 920b5d57e6510d9c41e1fe2349efa0f763029966 Mon Sep 17 00:00:00 2001 From: Piotr Leszczynski Date: Thu, 5 Sep 2019 13:41:51 +0200 Subject: [PATCH 13/62] chore(Setting up circleCi) --- circle.yml | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/circle.yml b/circle.yml index 3c13ea9..d4e9497 100644 --- a/circle.yml +++ b/circle.yml @@ -49,11 +49,12 @@ jobs: - image: circleci/node steps: - run: echo "workflows are enabled" - workflows: - version: 2 - build_and_test: - jobs: - - build - - test: - requires: - - build +workflows: + version: 2 + build_and_test: + jobs: + - verifyWorkflowIsWorking + - build + - test: + requires: + - build From fe779dc6a16adbb9c16bec38b7ff07cfde123ca2 Mon Sep 17 00:00:00 2001 From: Piotr Leszczynski Date: Thu, 5 Sep 2019 13:43:36 +0200 Subject: [PATCH 14/62] Enable docker --- circle.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/circle.yml b/circle.yml index d4e9497..0c96e55 100644 --- a/circle.yml +++ b/circle.yml @@ -14,6 +14,7 @@ jobs: - run: name: Verify docker-compose was installed command: 'docker-compose --version' + - setup_remote_docker - run: name: Update npm command: 'sudo npm install -g npm@latest' From 807455fb22841510e30f5fa57ae35dcae00b42a2 Mon Sep 17 00:00:00 2001 From: Piotr Leszczynski Date: Thu, 5 Sep 2019 13:45:10 +0200 Subject: [PATCH 15/62] Enable docker --- circle.yml | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/circle.yml b/circle.yml index 0c96e55..f4915ac 100644 --- a/circle.yml +++ b/circle.yml @@ -11,10 +11,6 @@ jobs: curl -L https://github.com/docker/compose/releases/download/1.19.0/docker-compose-`uname -s`-`uname -m` > ~/docker-compose chmod +x ~/docker-compose sudo mv ~/docker-compose /usr/local/bin/docker-compose - - run: - name: Verify docker-compose was installed - command: 'docker-compose --version' - - setup_remote_docker - run: name: Update npm command: 'sudo npm install -g npm@latest' @@ -29,6 +25,7 @@ jobs: - image: circleci/node steps: - checkout + - setup_remote_docker - run: name: Start kafka and schema-registry command: docker-compose up -d @@ -45,16 +42,10 @@ jobs: path: coverage prefix: coverage - run: npm test - verifyWorkflowIsWorking: - docker: - - image: circleci/node - steps: - - run: echo "workflows are enabled" workflows: version: 2 build_and_test: jobs: - - verifyWorkflowIsWorking - build - test: requires: From 3d4a4fa7d30f1843b91052b5d0eb0b16e3478c07 Mon Sep 17 00:00:00 2001 From: Piotr Leszczynski Date: Thu, 5 Sep 2019 13:51:02 +0200 Subject: [PATCH 16/62] configuration(Setting up tests) --- circle.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/circle.yml b/circle.yml index f4915ac..e4a1833 100644 --- a/circle.yml +++ b/circle.yml @@ -20,11 +20,14 @@ jobs: key: dependency-cache-{{ checksum "package.json" }} paths: - node_modules + - run: + name: Install projects dependencies + command: 'npm install' test: docker: - image: circleci/node steps: - - checkout +# - checkout - setup_remote_docker - run: name: Start kafka and schema-registry From a6b110c333f4c62d6f021b25661862bbb1a4cabc Mon Sep 17 00:00:00 2001 From: Piotr Leszczynski Date: Thu, 5 Sep 2019 13:56:51 +0200 Subject: [PATCH 17/62] configuration(Setting up tests) --- circle.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/circle.yml b/circle.yml index e4a1833..efb209d 100644 --- a/circle.yml +++ b/circle.yml @@ -20,6 +20,9 @@ jobs: key: dependency-cache-{{ checksum "package.json" }} paths: - node_modules + - run: + name: Install libreries requierd to build node-librdkafka + command: 'sudo apt-get update && sudo apt-get -y install libsasl2-dev libssl-dev' - run: name: Install projects dependencies command: 'npm install' From af33bd5f350e87f95d85d25db924e3fadeab51af Mon Sep 17 00:00:00 2001 From: Piotr Leszczynski Date: Thu, 5 Sep 2019 14:01:37 +0200 Subject: [PATCH 18/62] configuration(downgrading npm version used by circleCi) --- circle.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/circle.yml b/circle.yml index efb209d..df18be0 100644 --- a/circle.yml +++ b/circle.yml @@ -13,7 +13,7 @@ jobs: sudo mv ~/docker-compose /usr/local/bin/docker-compose - run: name: Update npm - command: 'sudo npm install -g npm@latest' + command: 'sudo npm install -g npm@6.1.0' - restore_cache: key: dependency-cache-{{ checksum "package.json" }} - save_cache: From e578795a5933029ef933ad6d47cba86ab9889627 Mon Sep 17 00:00:00 2001 From: Piotr Leszczynski Date: Thu, 5 Sep 2019 14:07:32 +0200 Subject: [PATCH 19/62] configuration(downgrading npm and node versions used by circleCi) --- circle.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/circle.yml b/circle.yml index df18be0..18ea3ac 100644 --- a/circle.yml +++ b/circle.yml @@ -2,7 +2,7 @@ version: 2 jobs: build: docker: - - image: circleci/node + - image: circleci/node@8.16.1 steps: - checkout - run: @@ -13,7 +13,7 @@ jobs: sudo mv ~/docker-compose /usr/local/bin/docker-compose - run: name: Update npm - command: 'sudo npm install -g npm@6.1.0' + command: 'sudo npm install -g npm@6.10' - restore_cache: key: dependency-cache-{{ checksum "package.json" }} - save_cache: From 25a197bccf52dc03888db22f29c3c57191fc791e Mon Sep 17 00:00:00 2001 From: Piotr Leszczynski Date: Thu, 5 Sep 2019 14:10:14 +0200 Subject: [PATCH 20/62] configuration(downgrading npm and node versions used by circleCi) --- circle.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/circle.yml b/circle.yml index 18ea3ac..69a23ca 100644 --- a/circle.yml +++ b/circle.yml @@ -2,7 +2,7 @@ version: 2 jobs: build: docker: - - image: circleci/node@8.16.1 + - image: circleci/node@8.16 steps: - checkout - run: From 51930e5c89bf19002158ccc77c1e4c0bada0fc30 Mon Sep 17 00:00:00 2001 From: Piotr Leszczynski Date: Thu, 5 Sep 2019 14:11:01 +0200 Subject: [PATCH 21/62] configuration(downgrading npm and node versions used by circleCi) --- circle.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/circle.yml b/circle.yml index 69a23ca..d7f7d1e 100644 --- a/circle.yml +++ b/circle.yml @@ -2,7 +2,7 @@ version: 2 jobs: build: docker: - - image: circleci/node@8.16 + - image: circleci/node@8 steps: - checkout - run: From 26680e0255fa12f8d5cd9e8a2747ddeee1b22483 Mon Sep 17 00:00:00 2001 From: Piotr Leszczynski Date: Thu, 5 Sep 2019 14:12:12 +0200 Subject: [PATCH 22/62] configuration(downgrading npm and node versions used by circleCi) --- circle.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/circle.yml b/circle.yml index d7f7d1e..21541ad 100644 --- a/circle.yml +++ b/circle.yml @@ -2,7 +2,7 @@ version: 2 jobs: build: docker: - - image: circleci/node@8 + - image: circleci/node:8.16 steps: - checkout - run: From 3b414665c03113dcfed12107d6169a92b8aa7ec3 Mon Sep 17 00:00:00 2001 From: Piotr Leszczynski Date: Thu, 5 Sep 2019 14:14:05 +0200 Subject: [PATCH 23/62] configuration(Setting up tests) --- circle.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/circle.yml b/circle.yml index 21541ad..cb50f32 100644 --- a/circle.yml +++ b/circle.yml @@ -30,7 +30,7 @@ jobs: docker: - image: circleci/node steps: -# - checkout + - checkout - setup_remote_docker - run: name: Start kafka and schema-registry From aa86743ca9d3004362e54afd96ecca12e64968ec Mon Sep 17 00:00:00 2001 From: Piotr Leszczynski Date: Thu, 5 Sep 2019 14:16:42 +0200 Subject: [PATCH 24/62] configuration(Setting up tests) --- circle.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/circle.yml b/circle.yml index cb50f32..35c7bc0 100644 --- a/circle.yml +++ b/circle.yml @@ -47,7 +47,7 @@ jobs: - store_artifacts: path: coverage prefix: coverage - - run: npm test + - run: npm install && npm test workflows: version: 2 build_and_test: From ab2c06de96dc0ec8d7dec942ce4b2872987d8f79 Mon Sep 17 00:00:00 2001 From: Piotr Leszczynski Date: Thu, 5 Sep 2019 14:20:24 +0200 Subject: [PATCH 25/62] configuration(Setting up tests) --- circle.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/circle.yml b/circle.yml index 35c7bc0..9d40ece 100644 --- a/circle.yml +++ b/circle.yml @@ -23,9 +23,9 @@ jobs: - run: name: Install libreries requierd to build node-librdkafka command: 'sudo apt-get update && sudo apt-get -y install libsasl2-dev libssl-dev' - - run: - name: Install projects dependencies - command: 'npm install' +# - run: +# name: Install projects dependencies +# command: 'npm install' test: docker: - image: circleci/node @@ -37,7 +37,7 @@ jobs: command: docker-compose up -d - run: name: Test - command: npm test + command: npm install && npm test - run: name: Generate code coverage command: './node_modules/.bin/nyc report --reporter=text-lcov' @@ -47,7 +47,6 @@ jobs: - store_artifacts: path: coverage prefix: coverage - - run: npm install && npm test workflows: version: 2 build_and_test: From 4ac4bc5398ca3c689c71cd2ad23209ff68d8f5cb Mon Sep 17 00:00:00 2001 From: Piotr Leszczynski Date: Thu, 5 Sep 2019 14:26:04 +0200 Subject: [PATCH 26/62] configuration(merging build and test workflows into one in cricleCi) --- circle.yml | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/circle.yml b/circle.yml index 9d40ece..2924eff 100644 --- a/circle.yml +++ b/circle.yml @@ -1,6 +1,7 @@ version: 2 jobs: build: + working_directory: ~/kafka-avro docker: - image: circleci/node:8.16 steps: @@ -23,21 +24,16 @@ jobs: - run: name: Install libreries requierd to build node-librdkafka command: 'sudo apt-get update && sudo apt-get -y install libsasl2-dev libssl-dev' -# - run: -# name: Install projects dependencies -# command: 'npm install' - test: - docker: - - image: circleci/node - steps: - - checkout + - run: + name: Install projects dependencies + command: 'npm install' - setup_remote_docker - run: name: Start kafka and schema-registry command: docker-compose up -d - run: name: Test - command: npm install && npm test + command: npm test - run: name: Generate code coverage command: './node_modules/.bin/nyc report --reporter=text-lcov' @@ -47,11 +43,3 @@ jobs: - store_artifacts: path: coverage prefix: coverage -workflows: - version: 2 - build_and_test: - jobs: - - build - - test: - requires: - - build From 22a8f5f2386a64640a035d5fadddd7c1c46a9f4d Mon Sep 17 00:00:00 2001 From: Piotr Leszczynski Date: Thu, 5 Sep 2019 14:36:50 +0200 Subject: [PATCH 27/62] configuration(setting up kafka hostnames in cricleCi) --- circle.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/circle.yml b/circle.yml index 2924eff..1af572f 100644 --- a/circle.yml +++ b/circle.yml @@ -1,5 +1,7 @@ version: 2 jobs: + hosts: + kafka: 127.0.0.1 build: working_directory: ~/kafka-avro docker: @@ -31,6 +33,9 @@ jobs: - run: name: Start kafka and schema-registry command: docker-compose up -d + - run: + name: Add hostnames for kafka + command: echo 127.0.0.1 kafka | sudo tee -a /etc/hosts && echo 127.0.0.1 schema-registry | sudo tee -a /etc/hosts && echo 127.0.0.1 zookeeper | sudo tee -a /etc/hosts - run: name: Test command: npm test From b75c9e0ba0afb18d3fd6926c9c74f83b67aaeaf9 Mon Sep 17 00:00:00 2001 From: Piotr Leszczynski Date: Thu, 5 Sep 2019 14:38:18 +0200 Subject: [PATCH 28/62] configuration(setting up kafka hostnames in cricleCi) --- circle.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/circle.yml b/circle.yml index 1af572f..fecb76e 100644 --- a/circle.yml +++ b/circle.yml @@ -1,7 +1,5 @@ version: 2 jobs: - hosts: - kafka: 127.0.0.1 build: working_directory: ~/kafka-avro docker: From 2fd080157760aee01c75e22f281a71af1a255e8d Mon Sep 17 00:00:00 2001 From: Piotr Leszczynski Date: Thu, 5 Sep 2019 14:50:56 +0200 Subject: [PATCH 29/62] configuration(cricleCi) --- test/lib/test.lib.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/lib/test.lib.js b/test/lib/test.lib.js index 7c6c9c8..68aec14 100644 --- a/test/lib/test.lib.js +++ b/test/lib/test.lib.js @@ -29,7 +29,7 @@ testLib.log = bunyan.createLogger({ }), }); -testLib.KAFKA_SCHEMA_REGISTRY_URL = 'http://localhost:8081'; +testLib.KAFKA_SCHEMA_REGISTRY_URL = 'http://schema-registry:8081'; testLib.KAFKA_BROKER_URL = 'kafka:9092'; testLib.topic = schemaFix.name; From d72e243f82d31e4401324a8a190f2dbcc609be62 Mon Sep 17 00:00:00 2001 From: Piotr Leszczynski Date: Thu, 5 Sep 2019 14:56:58 +0200 Subject: [PATCH 30/62] configuration(troubleshooting circleCi network problems when connecting to kafka and schema-registry) --- circle.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/circle.yml b/circle.yml index fecb76e..94b7500 100644 --- a/circle.yml +++ b/circle.yml @@ -12,6 +12,9 @@ jobs: curl -L https://github.com/docker/compose/releases/download/1.19.0/docker-compose-`uname -s`-`uname -m` > ~/docker-compose chmod +x ~/docker-compose sudo mv ~/docker-compose /usr/local/bin/docker-compose + - run: + name: Start kafka and schema-registry + command: docker-compose up -d - run: name: Update npm command: 'sudo npm install -g npm@6.10' @@ -28,12 +31,12 @@ jobs: name: Install projects dependencies command: 'npm install' - setup_remote_docker - - run: - name: Start kafka and schema-registry - command: docker-compose up -d - run: name: Add hostnames for kafka command: echo 127.0.0.1 kafka | sudo tee -a /etc/hosts && echo 127.0.0.1 schema-registry | sudo tee -a /etc/hosts && echo 127.0.0.1 zookeeper | sudo tee -a /etc/hosts + run: + name: Giving kafka and schema-registry addtional 30 seconds to start + command: sleep 30 - run: name: Test command: npm test From 8a7bb147af6e8ecdf7d3d0f55caa28ee23b770cf Mon Sep 17 00:00:00 2001 From: Piotr Leszczynski Date: Thu, 5 Sep 2019 14:59:59 +0200 Subject: [PATCH 31/62] configuration(troubleshooting circleCi network problems when connecting to kafka and schema-registry) --- circle.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/circle.yml b/circle.yml index 94b7500..9ed6cca 100644 --- a/circle.yml +++ b/circle.yml @@ -34,6 +34,9 @@ jobs: - run: name: Add hostnames for kafka command: echo 127.0.0.1 kafka | sudo tee -a /etc/hosts && echo 127.0.0.1 schema-registry | sudo tee -a /etc/hosts && echo 127.0.0.1 zookeeper | sudo tee -a /etc/hosts + run: + name: print /etc/hosts content + command: cat /etc/hosts run: name: Giving kafka and schema-registry addtional 30 seconds to start command: sleep 30 From 36fc50ad5c4be20599832c7694ad58c35a6ff1a6 Mon Sep 17 00:00:00 2001 From: Piotr Leszczynski Date: Thu, 5 Sep 2019 15:01:14 +0200 Subject: [PATCH 32/62] configuration(troubleshooting circleCi network problems when connecting to kafka and schema-registry) --- circle.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/circle.yml b/circle.yml index 9ed6cca..1715c79 100644 --- a/circle.yml +++ b/circle.yml @@ -34,12 +34,12 @@ jobs: - run: name: Add hostnames for kafka command: echo 127.0.0.1 kafka | sudo tee -a /etc/hosts && echo 127.0.0.1 schema-registry | sudo tee -a /etc/hosts && echo 127.0.0.1 zookeeper | sudo tee -a /etc/hosts - run: - name: print /etc/hosts content - command: cat /etc/hosts - run: - name: Giving kafka and schema-registry addtional 30 seconds to start - command: sleep 30 + - run: + name: print /etc/hosts content + command: cat /etc/hosts + - run: + name: Giving kafka and schema-registry addtional 30 seconds to start + command: sleep 30 - run: name: Test command: npm test From d98ef6c9b5fa0956fddb352901ed862909046028 Mon Sep 17 00:00:00 2001 From: Piotr Leszczynski Date: Thu, 5 Sep 2019 15:15:13 +0200 Subject: [PATCH 33/62] configuration(troubleshooting circleCi network problems when connecting to kafka and schema-registry) --- test/lib/test.lib.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/lib/test.lib.js b/test/lib/test.lib.js index 68aec14..62b24c8 100644 --- a/test/lib/test.lib.js +++ b/test/lib/test.lib.js @@ -121,6 +121,11 @@ testLib.registerSchema = Promise.method(function (topic, schema, type, retries) .catch(function (err) { testLib.log.error({err}, 'Axios SR creation failed after noOfRetries', {noOfRetries: retries}); retries++; + + if (retries > 30) { + process.exit(); + } + return new Promise(function (resolve) { setTimeout(function () { testLib.registerSchema(topic, schema, type, retries) From 25f7a6cc191c1b207551fcda5448edfa80547040 Mon Sep 17 00:00:00 2001 From: Piotr Leszczynski Date: Thu, 5 Sep 2019 15:16:15 +0200 Subject: [PATCH 34/62] configuration(troubleshooting circleCi network problems when connecting to kafka and schema-registry) --- circle.yml | 2 +- test/lib/test.lib.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/circle.yml b/circle.yml index 1715c79..2ccbc12 100644 --- a/circle.yml +++ b/circle.yml @@ -12,6 +12,7 @@ jobs: curl -L https://github.com/docker/compose/releases/download/1.19.0/docker-compose-`uname -s`-`uname -m` > ~/docker-compose chmod +x ~/docker-compose sudo mv ~/docker-compose /usr/local/bin/docker-compose + - setup_remote_docker - run: name: Start kafka and schema-registry command: docker-compose up -d @@ -30,7 +31,6 @@ jobs: - run: name: Install projects dependencies command: 'npm install' - - setup_remote_docker - run: name: Add hostnames for kafka command: echo 127.0.0.1 kafka | sudo tee -a /etc/hosts && echo 127.0.0.1 schema-registry | sudo tee -a /etc/hosts && echo 127.0.0.1 zookeeper | sudo tee -a /etc/hosts diff --git a/test/lib/test.lib.js b/test/lib/test.lib.js index 62b24c8..d199e87 100644 --- a/test/lib/test.lib.js +++ b/test/lib/test.lib.js @@ -122,7 +122,7 @@ testLib.registerSchema = Promise.method(function (topic, schema, type, retries) testLib.log.error({err}, 'Axios SR creation failed after noOfRetries', {noOfRetries: retries}); retries++; - if (retries > 30) { + if (retries > 20) { process.exit(); } From bc506568d9de5e42f1569ba7e61d7a98e6f3d6dc Mon Sep 17 00:00:00 2001 From: Piotr Leszczynski Date: Thu, 5 Sep 2019 15:35:41 +0200 Subject: [PATCH 35/62] configuration(troubleshooting circleCi network problems when connecting to kafka and schema-registry) --- circle.yml | 4 ++-- test/lib/test.lib.js | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/circle.yml b/circle.yml index 2ccbc12..f4060dd 100644 --- a/circle.yml +++ b/circle.yml @@ -15,7 +15,7 @@ jobs: - setup_remote_docker - run: name: Start kafka and schema-registry - command: docker-compose up -d + command: docker-compose up -d --project-name kafka-avro - run: name: Update npm command: 'sudo npm install -g npm@6.10' @@ -42,7 +42,7 @@ jobs: command: sleep 30 - run: name: Test - command: npm test + command: npm run-script coverage - run: name: Generate code coverage command: './node_modules/.bin/nyc report --reporter=text-lcov' diff --git a/test/lib/test.lib.js b/test/lib/test.lib.js index d199e87..bb26240 100644 --- a/test/lib/test.lib.js +++ b/test/lib/test.lib.js @@ -123,7 +123,8 @@ testLib.registerSchema = Promise.method(function (topic, schema, type, retries) retries++; if (retries > 20) { - process.exit(); + // process.exit(); + throw new Error('Environment has not started correctly'); } return new Promise(function (resolve) { From 781276555cd7121e49489db0a858c92d3f811a3d Mon Sep 17 00:00:00 2001 From: Piotr Leszczynski Date: Thu, 5 Sep 2019 15:48:02 +0200 Subject: [PATCH 36/62] configuration(troubleshooting circleCi network problems when connecting to kafka and schema-registry) --- circle.yml | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/circle.yml b/circle.yml index f4060dd..d7ba3ca 100644 --- a/circle.yml +++ b/circle.yml @@ -2,8 +2,9 @@ version: 2 jobs: build: working_directory: ~/kafka-avro - docker: - - image: circleci/node:8.16 + machine: true +# docker: +# - image: circleci/node:8.16 steps: - checkout - run: @@ -15,7 +16,19 @@ jobs: - setup_remote_docker - run: name: Start kafka and schema-registry - command: docker-compose up -d --project-name kafka-avro + command: docker-compose up -d + - run: + name: Install node@8.16 + command: | + set +e + curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.5/install.sh | bash + export NVM_DIR="/opt/circleci/.nvm" + [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" + nvm install v8.16 + nvm alias default v8.16 + # Each step uses the same `$BASH_ENV`, so need to modify it + echo 'export NVM_DIR="/opt/circleci/.nvm"' >> $BASH_ENV + echo "[ -s \"$NVM_DIR/nvm.sh\" ] && . \"$NVM_DIR/nvm.sh\"" >> $BASH_ENV - run: name: Update npm command: 'sudo npm install -g npm@6.10' From 8080b19e6704b230936d7cb1ee38e9b1d726ccf7 Mon Sep 17 00:00:00 2001 From: Piotr Leszczynski Date: Thu, 5 Sep 2019 15:48:50 +0200 Subject: [PATCH 37/62] configuration(troubleshooting circleCi network problems) --- circle.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/circle.yml b/circle.yml index d7ba3ca..23027ff 100644 --- a/circle.yml +++ b/circle.yml @@ -13,7 +13,7 @@ jobs: curl -L https://github.com/docker/compose/releases/download/1.19.0/docker-compose-`uname -s`-`uname -m` > ~/docker-compose chmod +x ~/docker-compose sudo mv ~/docker-compose /usr/local/bin/docker-compose - - setup_remote_docker +# - setup_remote_docker - run: name: Start kafka and schema-registry command: docker-compose up -d From 818ba5380509f83579750df4eafb18ad25d351d1 Mon Sep 17 00:00:00 2001 From: Piotr Leszczynski Date: Thu, 5 Sep 2019 15:52:49 +0200 Subject: [PATCH 38/62] configuration(troubleshooting circleCi network problems) --- circle.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/circle.yml b/circle.yml index 23027ff..164f7bb 100644 --- a/circle.yml +++ b/circle.yml @@ -29,6 +29,11 @@ jobs: # Each step uses the same `$BASH_ENV`, so need to modify it echo 'export NVM_DIR="/opt/circleci/.nvm"' >> $BASH_ENV echo "[ -s \"$NVM_DIR/nvm.sh\" ] && . \"$NVM_DIR/nvm.sh\"" >> $BASH_ENV + run: + name: Install node@8.16 + command: | + node -v + npm -v - run: name: Update npm command: 'sudo npm install -g npm@6.10' From b59f587e7feeac9adbe62cfede304f54e36620ab Mon Sep 17 00:00:00 2001 From: Piotr Leszczynski Date: Thu, 5 Sep 2019 15:54:55 +0200 Subject: [PATCH 39/62] configuration(troubleshooting circleCi network problems) --- lib/schema-registry.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/schema-registry.js b/lib/schema-registry.js index 07ead86..22595a9 100644 --- a/lib/schema-registry.js +++ b/lib/schema-registry.js @@ -26,6 +26,8 @@ const SchemaRegistry = module.exports = cip.extend(function (opts) { instance = axios.create({httpsAgent: opts.httpsAgent}); } + console.log('changing code so I can rerun circleCi build'); + /** @type {Object} The SR URL object */ this.schemaRegistryUrl = new URL(opts.schemaRegistryUrl + (opts.schemaRegistryUrl.endsWith('/') ? '' : '/')); From a7bfd609faa0ad48271b8a46ae0b67f1f0776f73 Mon Sep 17 00:00:00 2001 From: Piotr Leszczynski Date: Thu, 5 Sep 2019 16:04:50 +0200 Subject: [PATCH 40/62] configuration(troubleshooting) --- circle.yml | 7 ++++++- lib/kafka-avro.js | 3 --- lib/log.lib.js | 3 +-- lib/magic-byte.js | 2 +- lib/schema-registry.js | 2 -- 5 files changed, 8 insertions(+), 9 deletions(-) diff --git a/circle.yml b/circle.yml index 164f7bb..06f8ada 100644 --- a/circle.yml +++ b/circle.yml @@ -17,6 +17,11 @@ jobs: - run: name: Start kafka and schema-registry command: docker-compose up -d + - run: + name: verify if we can install nodejs using apt-get + command: | + sudo apt-get update + apt list -a nodejs - run: name: Install node@8.16 command: | @@ -30,7 +35,7 @@ jobs: echo 'export NVM_DIR="/opt/circleci/.nvm"' >> $BASH_ENV echo "[ -s \"$NVM_DIR/nvm.sh\" ] && . \"$NVM_DIR/nvm.sh\"" >> $BASH_ENV run: - name: Install node@8.16 + name: verify if nodejs was installed command: | node -v npm -v diff --git a/lib/kafka-avro.js b/lib/kafka-avro.js index ea6cb92..53ae166 100644 --- a/lib/kafka-avro.js +++ b/lib/kafka-avro.js @@ -66,9 +66,6 @@ const KafkaAvro = module.exports = CeventEmitter.extend(function (opts) { */ KafkaAvro.CODES = Kafka.CODES; -// -// Add Mixins -// KafkaAvro.mixin(Producer); KafkaAvro.mixin(Consumer); diff --git a/lib/log.lib.js b/lib/log.lib.js index 7f82473..21feb93 100644 --- a/lib/log.lib.js +++ b/lib/log.lib.js @@ -8,8 +8,7 @@ const shouldLog = !!process.env.KAFKA_AVRO_LOG_LEVEL; const logLevel = process.env.KAFKA_AVRO_LOG_LEVEL || 'info'; const noColors = !!process.env.KAFKA_AVRO_LOG_NO_COLORS; - -// FIXME: The users of this lib should be bale to inject their own loggers +// FIXME: The users of this lib should be allowed to inject their own loggers // default outstream mutes let outStream = { diff --git a/lib/magic-byte.js b/lib/magic-byte.js index a7f368b..a24b10c 100644 --- a/lib/magic-byte.js +++ b/lib/magic-byte.js @@ -22,7 +22,7 @@ magicByte.toMessageBuffer = function (val, type, schemaId, optLength) { const length = optLength || 1024; const buf = new Buffer(length); - buf[0] = MAGIC_BYTE; // Magic byte. + buf[0] = MAGIC_BYTE; buf.writeInt32BE(schemaId, 1); const pos = type.encode(val, buf, 5); diff --git a/lib/schema-registry.js b/lib/schema-registry.js index 22595a9..07ead86 100644 --- a/lib/schema-registry.js +++ b/lib/schema-registry.js @@ -26,8 +26,6 @@ const SchemaRegistry = module.exports = cip.extend(function (opts) { instance = axios.create({httpsAgent: opts.httpsAgent}); } - console.log('changing code so I can rerun circleCi build'); - /** @type {Object} The SR URL object */ this.schemaRegistryUrl = new URL(opts.schemaRegistryUrl + (opts.schemaRegistryUrl.endsWith('/') ? '' : '/')); From c44fa92ebf175b2171a97a983d226e6081036251 Mon Sep 17 00:00:00 2001 From: Piotr Leszczynski Date: Thu, 5 Sep 2019 16:06:40 +0200 Subject: [PATCH 41/62] configuration(troubleshooting) --- circle.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/circle.yml b/circle.yml index 06f8ada..64a6ed7 100644 --- a/circle.yml +++ b/circle.yml @@ -34,7 +34,7 @@ jobs: # Each step uses the same `$BASH_ENV`, so need to modify it echo 'export NVM_DIR="/opt/circleci/.nvm"' >> $BASH_ENV echo "[ -s \"$NVM_DIR/nvm.sh\" ] && . \"$NVM_DIR/nvm.sh\"" >> $BASH_ENV - run: + - run: name: verify if nodejs was installed command: | node -v From 6c88861952b84f0fb91704d8187ef0c37076d8fa Mon Sep 17 00:00:00 2001 From: Piotr Leszczynski Date: Thu, 5 Sep 2019 16:07:22 +0200 Subject: [PATCH 42/62] configuration(troubleshooting) --- circle.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/circle.yml b/circle.yml index 64a6ed7..9ce9f22 100644 --- a/circle.yml +++ b/circle.yml @@ -35,10 +35,10 @@ jobs: echo 'export NVM_DIR="/opt/circleci/.nvm"' >> $BASH_ENV echo "[ -s \"$NVM_DIR/nvm.sh\" ] && . \"$NVM_DIR/nvm.sh\"" >> $BASH_ENV - run: - name: verify if nodejs was installed - command: | - node -v - npm -v + name: verify if nodejs was installed + command: | + node -v + npm -v - run: name: Update npm command: 'sudo npm install -g npm@6.10' From 040200cb481c9e26c44062e7852c0bafca04581f Mon Sep 17 00:00:00 2001 From: Piotr Leszczynski Date: Thu, 5 Sep 2019 16:08:07 +0200 Subject: [PATCH 43/62] configuration(troubleshooting) --- circle.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/circle.yml b/circle.yml index 9ce9f22..da60cea 100644 --- a/circle.yml +++ b/circle.yml @@ -18,10 +18,10 @@ jobs: name: Start kafka and schema-registry command: docker-compose up -d - run: - name: verify if we can install nodejs using apt-get - command: | - sudo apt-get update - apt list -a nodejs + name: verify if we can install nodejs using apt-get + command: | + sudo apt-get update + apt list -a nodejs - run: name: Install node@8.16 command: | From d105e2ffccd140a31174210a78224daa4346833b Mon Sep 17 00:00:00 2001 From: Piotr Leszczynski Date: Fri, 6 Sep 2019 09:35:05 +0200 Subject: [PATCH 44/62] configuration(troubleshooting) --- circle.yml | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/circle.yml b/circle.yml index da60cea..ae7473e 100644 --- a/circle.yml +++ b/circle.yml @@ -17,11 +17,6 @@ jobs: - run: name: Start kafka and schema-registry command: docker-compose up -d - - run: - name: verify if we can install nodejs using apt-get - command: | - sudo apt-get update - apt list -a nodejs - run: name: Install node@8.16 command: | @@ -39,9 +34,9 @@ jobs: command: | node -v npm -v - - run: - name: Update npm - command: 'sudo npm install -g npm@6.10' +# - run: +# name: Update npm +# command: 'sudo npm install -g npm@6.10' - restore_cache: key: dependency-cache-{{ checksum "package.json" }} - save_cache: From 7b6e8878f76fd1b95caba18eb130c0bef27c37be Mon Sep 17 00:00:00 2001 From: Piotr Leszczynski Date: Fri, 6 Sep 2019 09:56:31 +0200 Subject: [PATCH 45/62] configuration(tweaking circleCi build) --- .gitignore | 1 + circle.yml | 35 +++++++++++++++++++++-------------- package.json | 2 +- 3 files changed, 23 insertions(+), 15 deletions(-) diff --git a/.gitignore b/.gitignore index 1ff50fb..6629bc7 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ npm-debug.log dump.rdb wiki temp +report.html diff --git a/circle.yml b/circle.yml index ae7473e..96f1305 100644 --- a/circle.yml +++ b/circle.yml @@ -2,17 +2,19 @@ version: 2 jobs: build: working_directory: ~/kafka-avro - machine: true + machine: + image: ubuntu-1604:201903-01 + docker_layer_caching: true # docker: # - image: circleci/node:8.16 steps: - checkout - - run: - name: Install Docker Compose - command: | - curl -L https://github.com/docker/compose/releases/download/1.19.0/docker-compose-`uname -s`-`uname -m` > ~/docker-compose - chmod +x ~/docker-compose - sudo mv ~/docker-compose /usr/local/bin/docker-compose +# - run: +# name: Install Docker Compose +# command: | +# curl -L https://github.com/docker/compose/releases/download/1.19.0/docker-compose-`uname -s`-`uname -m` > ~/docker-compose +# chmod +x ~/docker-compose +# sudo mv ~/docker-compose /usr/local/bin/docker-compose # - setup_remote_docker - run: name: Start kafka and schema-registry @@ -34,6 +36,11 @@ jobs: command: | node -v npm -v +# - run: +# name: verify if nodejs was installed +# command: | +# node -v +# npm -v # - run: # name: Update npm # command: 'sudo npm install -g npm@6.10' @@ -43,9 +50,9 @@ jobs: key: dependency-cache-{{ checksum "package.json" }} paths: - node_modules - - run: - name: Install libreries requierd to build node-librdkafka - command: 'sudo apt-get update && sudo apt-get -y install libsasl2-dev libssl-dev' +# - run: +# name: Install libreries requierd to build node-librdkafka +# command: 'sudo apt-get update && sudo apt-get -y install libsasl2-dev libssl-dev' - run: name: Install projects dependencies command: 'npm install' @@ -55,9 +62,9 @@ jobs: - run: name: print /etc/hosts content command: cat /etc/hosts - - run: - name: Giving kafka and schema-registry addtional 30 seconds to start - command: sleep 30 +# - run: +# name: Giving kafka and schema-registry addtional 30 seconds to start +# command: sleep 30 - run: name: Test command: npm run-script coverage @@ -65,7 +72,7 @@ jobs: name: Generate code coverage command: './node_modules/.bin/nyc report --reporter=text-lcov' - store_artifacts: - path: test-results.xml + path: report.html prefix: tests - store_artifacts: path: coverage diff --git a/package.json b/package.json index 089df35..dc46bc9 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "node": ">=0.12" }, "scripts": { - "test": "eslint lib && mocha -b test/spec && eslint lib/**", + "test": "eslint lib && mocha -b test/spec --reporter doc > report.html && eslint lib/**", "coverage": "nyc npm run test" }, "dependencies": { From 788137a8e031d21e8f120f90747a0124d25d2302 Mon Sep 17 00:00:00 2001 From: Piotr Leszczynski Date: Fri, 6 Sep 2019 10:13:04 +0200 Subject: [PATCH 46/62] configuration(simplifying circleCi build) --- .gitignore | 2 ++ circle.yml | 37 ------------------------------------- package.json | 2 +- 3 files changed, 3 insertions(+), 38 deletions(-) diff --git a/.gitignore b/.gitignore index 6629bc7..d5e039f 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ dump.rdb wiki temp report.html +coverage +.nyc_output diff --git a/circle.yml b/circle.yml index 96f1305..f6afbd0 100644 --- a/circle.yml +++ b/circle.yml @@ -5,17 +5,8 @@ jobs: machine: image: ubuntu-1604:201903-01 docker_layer_caching: true -# docker: -# - image: circleci/node:8.16 steps: - checkout -# - run: -# name: Install Docker Compose -# command: | -# curl -L https://github.com/docker/compose/releases/download/1.19.0/docker-compose-`uname -s`-`uname -m` > ~/docker-compose -# chmod +x ~/docker-compose -# sudo mv ~/docker-compose /usr/local/bin/docker-compose -# - setup_remote_docker - run: name: Start kafka and schema-registry command: docker-compose up -d @@ -31,28 +22,12 @@ jobs: # Each step uses the same `$BASH_ENV`, so need to modify it echo 'export NVM_DIR="/opt/circleci/.nvm"' >> $BASH_ENV echo "[ -s \"$NVM_DIR/nvm.sh\" ] && . \"$NVM_DIR/nvm.sh\"" >> $BASH_ENV - - run: - name: verify if nodejs was installed - command: | - node -v - npm -v -# - run: -# name: verify if nodejs was installed -# command: | -# node -v -# npm -v -# - run: -# name: Update npm -# command: 'sudo npm install -g npm@6.10' - restore_cache: key: dependency-cache-{{ checksum "package.json" }} - save_cache: key: dependency-cache-{{ checksum "package.json" }} paths: - node_modules -# - run: -# name: Install libreries requierd to build node-librdkafka -# command: 'sudo apt-get update && sudo apt-get -y install libsasl2-dev libssl-dev' - run: name: Install projects dependencies command: 'npm install' @@ -62,18 +37,6 @@ jobs: - run: name: print /etc/hosts content command: cat /etc/hosts -# - run: -# name: Giving kafka and schema-registry addtional 30 seconds to start -# command: sleep 30 - run: name: Test command: npm run-script coverage - - run: - name: Generate code coverage - command: './node_modules/.bin/nyc report --reporter=text-lcov' - - store_artifacts: - path: report.html - prefix: tests - - store_artifacts: - path: coverage - prefix: coverage diff --git a/package.json b/package.json index dc46bc9..089df35 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "node": ">=0.12" }, "scripts": { - "test": "eslint lib && mocha -b test/spec --reporter doc > report.html && eslint lib/**", + "test": "eslint lib && mocha -b test/spec && eslint lib/**", "coverage": "nyc npm run test" }, "dependencies": { From 05d714f5cc6865ad745bb15d6a8497701c05750c Mon Sep 17 00:00:00 2001 From: Piotr Leszczynski Date: Fri, 6 Sep 2019 10:25:52 +0200 Subject: [PATCH 47/62] fix(incorrect implementation of io.confluent.kafka.serializers.subject.TopicRecordNameStrategy) --- circle.yml | 2 +- lib/kafka-producer.js | 4 ++-- test/lib/test.lib.js | 12 ++++++------ 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/circle.yml b/circle.yml index f6afbd0..470d572 100644 --- a/circle.yml +++ b/circle.yml @@ -30,7 +30,7 @@ jobs: - node_modules - run: name: Install projects dependencies - command: 'npm install' + command: 'unset NVM_NODEJS_ORG_MIRROR NVM_IOJS_ORG_MIRROR && npm install' - run: name: Add hostnames for kafka command: echo 127.0.0.1 kafka | sudo tee -a /etc/hosts && echo 127.0.0.1 schema-registry | sudo tee -a /etc/hosts && echo 127.0.0.1 zookeeper | sudo tee -a /etc/hosts diff --git a/lib/kafka-producer.js b/lib/kafka-producer.js index 38392d7..2cc46dc 100644 --- a/lib/kafka-producer.js +++ b/lib/kafka-producer.js @@ -70,8 +70,8 @@ Producer.prototype.getProducer = Promise.method(function (opts, topicOptions) { */ Producer.prototype._serializeType = function (topicName, isKey, data) { // implementing io.confluent.kafka.serializers.subject.TopicRecordNameStrategy - // FIXME: This will break if this code gets minimized - const subject = `${data.constructor.name}-${topicName}`; + // FIXME: It will break if code gets minimized + const subject = `${topicName}-${data.constructor.name}`; const schema = isKey === false ? this.sr.valueSchemas[subject] diff --git a/test/lib/test.lib.js b/test/lib/test.lib.js index bb26240..a16053a 100644 --- a/test/lib/test.lib.js +++ b/test/lib/test.lib.js @@ -63,13 +63,13 @@ testLib.init = function () { this.timeout(180000); // wait up to 3' for the SR to come up return Promise.all([ - testLib.registerSchema(`Object-${testLib.topic}`, schemaFix, 'value'), - testLib.registerSchema(`Object-${testLib.topicTwo}`, schemaTwoFix, 'value'), - testLib.registerSchema(`Object-${testLib.topicThreeWithDuplicateSchema}`, schemaFix, 'value'), + testLib.registerSchema(`${testLib.topic}-Object`, schemaFix, 'value'), + testLib.registerSchema(`${testLib.topicTwo}-Object`, schemaTwoFix, 'value'), + testLib.registerSchema(`${testLib.topicThreeWithDuplicateSchema}-Object`, schemaFix, 'value'), - testLib.registerSchema(`String-${testLib.topic}`, keySchemaFix, 'key'), - testLib.registerSchema(`Object-${testLib.topicTwo}`, keySchemaFix, 'key'), - testLib.registerSchema(`Object-${testLib.topicThreeWithDuplicateSchema}`, keySchemaFix, 'key'), + testLib.registerSchema(`${testLib.topic}-String`, keySchemaFix, 'key'), + testLib.registerSchema(`${testLib.topicTwo}-Object`, keySchemaFix, 'key'), + testLib.registerSchema(`${testLib.topicThreeWithDuplicateSchema}-Object`, keySchemaFix, 'key'), ]); }); From a97eccf55b9d5d55e6c6f2d00d8857529effb951 Mon Sep 17 00:00:00 2001 From: Piotr Leszczynski Date: Fri, 6 Sep 2019 12:17:45 +0200 Subject: [PATCH 48/62] documentation(fixing icon to circleci test status) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a5fcf51..7fffe0b 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ > Node.js bindings for librdkafka with Avro schema serialization. -[![CircleCI](https://circleci.com/gh/pleszczy/workflows/kafka-avro/tree/master.svg?style=svg)](https://circleci.com/gh/pleszczy/workflows/kafka-avro/tree/master) +[![CircleCI](https://circleci.com/gh/pleszczy/kafka-avro/tree/master.svg?style=svg)](https://circleci.com/gh/pleszczy/kafka-avro/tree/master) The kafka-avro library is a wrapper that combines the [node-rdkafka][node-rdkafka] and [avsc][avsc] libraries to allow for Production and Consumption of messages on kafka validated and serialized by Avro. From b315e16ee205754d0e8dbe103e1b8f12785efc6c Mon Sep 17 00:00:00 2001 From: Piotr Leszczynski Date: Fri, 6 Sep 2019 13:16:46 +0200 Subject: [PATCH 49/62] tests(should produce and consume a multi type message using consume "on") --- test/fixtures/schema-student.fix.json | 24 +++++++++ test/fixtures/schema-teacher.fix.json | 20 +++++++ test/lib/test.lib.js | 9 ++++ test/spec/consumer.test.js | 77 ++++++++++++++++++++++++++- 4 files changed, 128 insertions(+), 2 deletions(-) create mode 100644 test/fixtures/schema-student.fix.json create mode 100644 test/fixtures/schema-teacher.fix.json diff --git a/test/fixtures/schema-student.fix.json b/test/fixtures/schema-student.fix.json new file mode 100644 index 0000000..7444b04 --- /dev/null +++ b/test/fixtures/schema-student.fix.json @@ -0,0 +1,24 @@ +{ + "type": "record", + "name": "Student", + "namespace": "org.test", + "doc": "Student schema for tests", + "fields": [ + { + "name": "firstName", + "type": "string" + }, + { + "name": "middleInitial", + "type": "string" + }, + { + "name": "lastName", + "type": "string" + }, + { + "name": "fullName", + "type": "string" + } + ] +} diff --git a/test/fixtures/schema-teacher.fix.json b/test/fixtures/schema-teacher.fix.json new file mode 100644 index 0000000..3bdee0f --- /dev/null +++ b/test/fixtures/schema-teacher.fix.json @@ -0,0 +1,20 @@ +{ + "type": "record", + "name": "Teacher", + "namespace": "org.test", + "doc": "Teacher schema for tests", + "fields": [ + { + "name": "firstName", + "type": "string" + }, + { + "name": "lastName", + "type": "string" + }, + { + "name": "profession", + "type": "string" + } + ] +} diff --git a/test/lib/test.lib.js b/test/lib/test.lib.js index a16053a..7d185eb 100644 --- a/test/lib/test.lib.js +++ b/test/lib/test.lib.js @@ -18,6 +18,9 @@ const schemaTwoFix = require('../fixtures/schema-two.fix'); const keySchemaFix = require('../fixtures/key-schema.fix'); +const schemaStudent = require('../fixtures/schema-student.fix'); +const schemaTeacher = require('../fixtures/schema-teacher.fix'); + const testLib = module.exports = {}; testLib.log = bunyan.createLogger({ @@ -34,6 +37,7 @@ testLib.KAFKA_BROKER_URL = 'kafka:9092'; testLib.topic = schemaFix.name; testLib.topicTwo = schemaTwoFix.name; +testLib.topicTree = `${schemaTeacher.name}-${schemaStudent.name}`; testLib.topicThreeWithDuplicateSchema = schemaFix.name + '-duplicateSchema'; let testBoot = false; @@ -67,6 +71,11 @@ testLib.init = function () { testLib.registerSchema(`${testLib.topicTwo}-Object`, schemaTwoFix, 'value'), testLib.registerSchema(`${testLib.topicThreeWithDuplicateSchema}-Object`, schemaFix, 'value'), + testLib.registerSchema(`${testLib.topicTree}-Teacher`, schemaTeacher, 'key'), + testLib.registerSchema(`${testLib.topicTree}-Teacher`, schemaTeacher, 'value'), + testLib.registerSchema(`${testLib.topicTree}-Student`, schemaStudent, 'key'), + testLib.registerSchema(`${testLib.topicTree}-Student`, schemaStudent, 'value'), + testLib.registerSchema(`${testLib.topic}-String`, keySchemaFix, 'key'), testLib.registerSchema(`${testLib.topicTwo}-Object`, keySchemaFix, 'key'), testLib.registerSchema(`${testLib.topicThreeWithDuplicateSchema}-Object`, keySchemaFix, 'key'), diff --git a/test/spec/consumer.test.js b/test/spec/consumer.test.js index 2b4ab7a..a8b36e1 100644 --- a/test/spec/consumer.test.js +++ b/test/spec/consumer.test.js @@ -12,6 +12,45 @@ const testLib = require('../lib/test.lib'); function noop() { } +const Student = /** @class */ (function () { + function Student(firstName, middleInitial, lastName) { + this.firstName = firstName; + this.middleInitial = middleInitial; + this.lastName = lastName; + this.fullName = firstName + ' ' + middleInitial + ' ' + lastName; + } + + return Student; +}()); + +const Teacher = /** @class */ (function () { + function Teacher(firstName, lastName, profession) { + this.firstName = firstName; + this.lastName = lastName; + this.profession = profession; + } + + return Teacher; +}()); + +function getRandomInt(max) { + return Math.floor(Math.random() * Math.floor(max)); +} + +function studentEquals(dataValue, student) { + expect(dataValue.constructor.name).to.equal(student.constructor.name); + expect(dataValue.firstName).to.equal(student.firstName); + expect(dataValue.lastName).to.equal(student.lastName); + expect(dataValue.middleInitial).to.equal(student.middleInitial); + expect(dataValue.fullName).to.equal(student.fullName); +} + +function teacherEquals(dataValue, teacher) { + expect(dataValue.constructor.name).to.equal(teacher.constructor.name); + expect(dataValue.firstName).to.equal(teacher.firstName); + expect(dataValue.profession).to.equal(teacher.profession); +} + describe('Consume', function () { testLib.init(); @@ -125,6 +164,38 @@ describe('Consume', function () { }, 10000); }); + it('should produce and consume a multi type message using consume "on"', function (done) { + const teacher = new Teacher('TeacherValue', `${getRandomInt(1000)}`, `${getRandomInt(1000)}`); + const teacherKey = new Teacher('TeacherKey', `${getRandomInt(1000)}`, `${getRandomInt(1000)}`); + const student = new Student('StudentValue', `${getRandomInt(1000)}`, '' + `${getRandomInt(1000)}`); + const studentKey = new Student('StudentKey', `${getRandomInt(1000)}`, '' + `${getRandomInt(1000)}`); + + this.consumer.subscribe([testLib.topicTree]); + this.consumer.consume(); + let receivedMessages = 0; + this.consumer.on('data', function (rawData) { + receivedMessages++; + const k = rawData.parsedKey; + const v = rawData.parsed; + + if (v.constructor.name === 'Teacher') { + teacherEquals(k, teacherKey); + teacherEquals(v, teacher); + } else { + studentEquals(k, studentKey); + studentEquals(v, student); + } + if (receivedMessages === 2) { + done(); + } + }.bind(this)); + + setTimeout(() => { + this.producer.produce(testLib.topicTree, -1, teacher, teacherKey); + this.producer.produce(testLib.topicTree, -1, student, studentKey); + }, 10000); + }); + it('should produce and consume a message using consume "on" with timestamp when provided', function (done) { let produceTime = Date.parse('04 Dec 2015 00:12:00 GMT'); //use date in the past to guarantee we don't get Date.now() @@ -238,7 +309,8 @@ describe('Consume', function () { this.producer.produce(testLib.topic, -1, message, key); }, 10000); }); - }); + }) + ; describe('Consume using Streams', function () { it('should produce and consume a message using streams on two topics', function (done) { @@ -328,4 +400,5 @@ describe('Consume', function () { }, 10000); }); }); -}); +}) +; From a1d6066a70840471152dff0a829e6566369ec2bc Mon Sep 17 00:00:00 2001 From: Piotr Leszczynski Date: Fri, 6 Sep 2019 14:00:18 +0200 Subject: [PATCH 50/62] documentation() --- README.md | 6 ++++++ lib/log.lib.js | 2 -- test/spec/producer.test.js | 1 + 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7fffe0b..2844726 100644 --- a/README.md +++ b/README.md @@ -200,6 +200,12 @@ kafka-avro intercepts all incoming messages and augments the object with two mor The KafkaAvro instance also provides the following methods: +### Support for several event types in the same topic +The Kafka Avro supports several events types in the same topic. The strategy chosen to implement this is [TopicRecordNameStrategy](https://github.com/confluentinc/schema-registry/blob/master/avro-serializer/src/main/java/io/confluent/kafka/serializers/subject/TopicRecordNameStrategy.java) +which is default strategy on the confluent platform. + +You can read more about this here : https://www.confluent.io/blog/put-several-event-types-kafka-topic/ + ### Logging The Kafka Avro library logs messages using the [Bunyan logger](https://github.com/trentm/node-bunyan/). To enable logging you will have to define at least one of the needed ENV variables: diff --git a/lib/log.lib.js b/lib/log.lib.js index 21feb93..e5f9aee 100644 --- a/lib/log.lib.js +++ b/lib/log.lib.js @@ -8,8 +8,6 @@ const shouldLog = !!process.env.KAFKA_AVRO_LOG_LEVEL; const logLevel = process.env.KAFKA_AVRO_LOG_LEVEL || 'info'; const noColors = !!process.env.KAFKA_AVRO_LOG_NO_COLORS; -// FIXME: The users of this lib should be allowed to inject their own loggers - // default outstream mutes let outStream = { write: function () { diff --git a/test/spec/producer.test.js b/test/spec/producer.test.js index a418f7b..d1ed021 100644 --- a/test/spec/producer.test.js +++ b/test/spec/producer.test.js @@ -67,6 +67,7 @@ describe('Produce', function () { } }, 1000); }); + it('should produce a message with an opaque value in delivery report', function (done) { const message = { name: 'Thanasis', From adb1b0c6c0a92ecfe7b54bd407c430db6082b183 Mon Sep 17 00:00:00 2001 From: Piotr Leszczynski Date: Fri, 6 Sep 2019 15:53:07 +0200 Subject: [PATCH 51/62] pr(fixing a typo) --- test/spec/producer.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/spec/producer.test.js b/test/spec/producer.test.js index d1ed021..99af0f3 100644 --- a/test/spec/producer.test.js +++ b/test/spec/producer.test.js @@ -111,9 +111,9 @@ describe('Produce', function () { name: 'Thanasis', }; - const banded = this.producer.produce.bind(this.producer, this.topicName, -1, message, 'key'); + const binded = this.producer.produce.bind(this.producer, this.topicName, -1, message, 'key'); - expect(banded).to.throw(Error); + expect(binded).to.throw(Error); }); }); From e2612d22e378760258c251413965c3b92f4362f1 Mon Sep 17 00:00:00 2001 From: Piotr Leszczynski Date: Mon, 9 Sep 2019 09:26:47 +0200 Subject: [PATCH 52/62] pr(implementing comments) --- README.md | 2 +- lib/kafka-consumer.js | 2 +- test/spec/magic-byte.test.js | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 2844726..282b1ff 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ > Node.js bindings for librdkafka with Avro schema serialization. -[![CircleCI](https://circleci.com/gh/pleszczy/kafka-avro/tree/master.svg?style=svg)](https://circleci.com/gh/pleszczy/kafka-avro/tree/master) +[![CircleCI](https://circleci.com/gh/waldophotos/kafka-avro/tree/master.svg?style=svg)](https://circleci.com/gh/waldophotos/kafka-avro/tree/master) The kafka-avro library is a wrapper that combines the [node-rdkafka][node-rdkafka] and [avsc][avsc] libraries to allow for Production and Consumption of messages on kafka validated and serialized by Avro. diff --git a/lib/kafka-consumer.js b/lib/kafka-consumer.js index ba2a3f4..5b19c06 100644 --- a/lib/kafka-consumer.js +++ b/lib/kafka-consumer.js @@ -108,7 +108,7 @@ Consumer.prototype._onWrapper = function (consumerInstance, eventName, cb) { {topic: message.topic}); message.parsedKey = null; - const key = message.key; + const {key} = message; if (key) { try { diff --git a/test/spec/magic-byte.test.js b/test/spec/magic-byte.test.js index c0d22e6..17528cd 100644 --- a/test/spec/magic-byte.test.js +++ b/test/spec/magic-byte.test.js @@ -5,7 +5,6 @@ const chai = require('chai'); const expect = chai.expect; const avro = require('avsc'); -// const testLib = require('../lib/test.lib'); const magicByte = require('../../lib/magic-byte'); const schemaFix = require('../fixtures/schema.fix'); From 996327a7feca55c0c9b32b9becd1bd8a40d4e420 Mon Sep 17 00:00:00 2001 From: Piotr Leszczynski Date: Mon, 9 Sep 2019 12:43:37 +0200 Subject: [PATCH 53/62] pr(implementing comments) --- lib/kafka-avro.js | 5 ++- lib/kafka-producer.js | 17 ++++++---- lib/schema-registry.js | 32 +++++++++++++++++++ lib/subject-strategy.js | 70 +++++++++++++++++++++++++++++++++++++++++ test/lib/test.lib.js | 2 ++ test/spec/base.test.js | 4 ++- 6 files changed, 122 insertions(+), 8 deletions(-) create mode 100644 lib/subject-strategy.js diff --git a/lib/kafka-avro.js b/lib/kafka-avro.js index 53ae166..482517e 100644 --- a/lib/kafka-avro.js +++ b/lib/kafka-avro.js @@ -13,6 +13,7 @@ const Kafka = require('node-rdkafka'); const rootLog = require('./log.lib'); const log = rootLog.getChild(__filename); const SchemaRegistry = require('./schema-registry'); +const { SubjectNameStrategy } = require('./subject-strategy'); // // Mixins @@ -45,7 +46,9 @@ const KafkaAvro = module.exports = CeventEmitter.extend(function (opts) { fetchAllVersions: opts.fetchAllVersions || false, fetchRefreshRate: opts.fetchRefreshRate || 0, parseOptions: opts.parseOptions, - httpsAgent: opts.httpsAgent + httpsAgent: opts.httpsAgent, + keySubjectStrategy: new SubjectNameStrategy(opts.keySubjectStrategy), + valueSubjectStrategy: new SubjectNameStrategy(opts.valueSubjectStrategy), }; /** @type {kafka-avro.SchemaRegistry} Instantiated SR. */ diff --git a/lib/kafka-producer.js b/lib/kafka-producer.js index 2cc46dc..ef6e84c 100644 --- a/lib/kafka-producer.js +++ b/lib/kafka-producer.js @@ -69,13 +69,18 @@ Producer.prototype.getProducer = Promise.method(function (opts, topicOptions) { * @private */ Producer.prototype._serializeType = function (topicName, isKey, data) { - // implementing io.confluent.kafka.serializers.subject.TopicRecordNameStrategy - // FIXME: It will break if code gets minimized - const subject = `${topicName}-${data.constructor.name}`; - const schema = isKey === false - ? this.sr.valueSchemas[subject] - : this.sr.keySchemas[subject]; + let schema = null; + let subject = null; + + if (isKey === false) { + subject = this.sr.keySubjectStrategy.prepareSubjectName(topicName, data); + schema = this.sr.valueSchemas[subject]; + } else { + subject = this.sr.valueSubjectStrategy.prepareSubjectName(topicName, data); + schema = this.sr.keySchemas[subject]; + + } const schemaType = isKey === false ? 'value' diff --git a/lib/schema-registry.js b/lib/schema-registry.js index 07ead86..a6f2841 100644 --- a/lib/schema-registry.js +++ b/lib/schema-registry.js @@ -44,6 +44,38 @@ const SchemaRegistry = module.exports = cip.extend(function (opts) { this.parseOptions.wrapUnions = true; } + /** + * *Key schema subject strategy* + * + * Avro serializer registers a schema in Schema Registry under a subject name, which essentially defines a namespace in the registry: + * Compatibility checks are per subject + * Versions are tied to subjects + * When schemas evolve, they are still associated to the same subject but get a new schema id and version + *Overview + *The subject name depends on the subject name strategy, which you can set to one of the following three values: + * + * *TopicNameStrategy* (io.confluent.kafka.serializers.subject.TopicNameStrategy) – this is the default + * *RecordNameStrategy* (io.confluent.kafka.serializers.subject.RecordNameStrategy) + * *TopicRecordNameStrategy* (io.confluent.kafka.serializers.subject.TopicRecordNameStrategy) + */ + this.keySubjectStrategy = opts.keySubjectStrategy; + + /** + * *Value schema subject strategy* + * + * Avro serializer registers a schema in Schema Registry under a subject name, which essentially defines a namespace in the registry: + * Compatibility checks are per subject + * Versions are tied to subjects + * When schemas evolve, they are still associated to the same subject but get a new schema id and version + *Overview + *The subject name depends on the subject name strategy, which you can set to one of the following three values: + * + * *TopicNameStrategy* (io.confluent.kafka.serializers.subject.TopicNameStrategy) – this is the default + * *RecordNameStrategy* (io.confluent.kafka.serializers.subject.RecordNameStrategy) + * *TopicRecordNameStrategy* (io.confluent.kafka.serializers.subject.TopicRecordNameStrategy) + */ + this.valueSubjectStrategy = opts.valueSubjectStrategy; + /** @type {?Number} The refresh time (in seconds) that the schema registry will be fetch */ this.fetchRefreshRate = opts.fetchRefreshRate; diff --git a/lib/subject-strategy.js b/lib/subject-strategy.js new file mode 100644 index 0000000..dcb8576 --- /dev/null +++ b/lib/subject-strategy.js @@ -0,0 +1,70 @@ +const SUBJECT_STRATEGIES = Object.freeze({ + + /** + * For any Avro record type that is published to Kafka topic <topic>, + * registers the schema in the registry under the subject name + * <topic>-<recordName>, where <recordName> is the + * fully-qualified Avro record name. This strategy allows a topic to contain + * a mixture of different record types, since no intra-topic compatibility + * checking is performed. Moreover, different topics may contain mutually + * incompatible versions of the same record name, since the compatibility + * check is scoped to a particular record name within a particular topic. + */ + TOPIC_RECORD_NAME_STRATEGY: 'TopicRecordNameStrategy', + + /** + * Default subject name strategy for any messages published to + * <topic>, the schema of the message key is registered under + * the subject name <topic>-key, and the message value is registered + * under the subject name <topic>-value. + */ + TOPIC_NAME_STRATEGY: 'TopicNameStrategy', + + /** + * For any Avro record type that is published to Kafka, registers the schema + * in the registry under the fully-qualified record name (regardless of the + * topic). This strategy allows a topic to contain a mixture of different + * record types, since no intra-topic compatibility checking is performed. + * Instead, checks compatibility of any occurrences of the same record name + * across all topics. + */ + RECORD_NAME_STRATEGY: 'RecordNameStrategy' + +}); +Object.freeze(SUBJECT_STRATEGIES); + +/** + * A SubjectNameStrategy is used by the Avro serializer to determine + * the subject name under which the event record schemas should be registered + * in the schema registry. The default is TopicNameStrategy. + */ +module.exports.SubjectNameStrategy = class SubjectNameStrategy { + + prepareSubjectName(topicName, data) { + switch (this.strategy) { + case SUBJECT_STRATEGIES.TOPIC_RECORD_NAME_STRATEGY: + return `${topicName}-${data.constructor.name}`; + case SUBJECT_STRATEGIES.TOPIC_NAME_STRATEGY: + return `${topicName}`; + case SUBJECT_STRATEGIES.RECORD_NAME_STRATEGY: + return `${data.constructor.name}`; + default: // If not configured use the default `TOPIC_NAME_STRATEGY` + return `${topicName}`; + } + } + + constructor(name) { + if (name) { + const normalizedName = name.toLowerCase(); + for (let [, value] of Object.entries(SUBJECT_STRATEGIES)) { + if (value.toLowerCase() == normalizedName) { + this.strategy = value; + return; + } + } + throw new Error(`Invalid subject name strategy ${name}. Allowed strategies are ${Object.values(SUBJECT_STRATEGIES)}`); + } else { + this.strategy = null; + } + } +}; diff --git a/test/lib/test.lib.js b/test/lib/test.lib.js index 7d185eb..5f2b52c 100644 --- a/test/lib/test.lib.js +++ b/test/lib/test.lib.js @@ -86,6 +86,8 @@ testLib.init = function () { let kafkaAvro = new KafkaAvro({ kafkaBroker: testLib.KAFKA_BROKER_URL, schemaRegistry: testLib.KAFKA_SCHEMA_REGISTRY_URL, + keySubjectStrategy: 'TopicRecordNameStrategy', + valueSubjectStrategy: 'TopicRecordNameStrategy', }); testLib.log.info('test.beforeEach 2: Invoking kafkaAvro.init()...'); diff --git a/test/spec/base.test.js b/test/spec/base.test.js index 5428712..396c587 100644 --- a/test/spec/base.test.js +++ b/test/spec/base.test.js @@ -27,13 +27,15 @@ describe('Base API Surface', function() { kafkaBroker: testLib.KAFKA_BROKER_URL, schemaRegistry: testLib.KAFKA_SCHEMA_REGISTRY_URL, }); - + // TODO: Test all strategies and different strategy combination for key/value return kafkaAvro.init() .bind(this) .then(function() { kafkaAvro = new KafkaAvro({ kafkaBroker: testLib.KAFKA_BROKER_URL, schemaRegistry: testLib.KAFKA_SCHEMA_REGISTRY_URL, + keySubjectStrategy: 'TopicRecordNameStrategy', + valueSubjectStrategy: 'TopicRecordNameStrategy', }); return kafkaAvro.init(); }) From b3a984c9756f0e778bd3842c5fab6ff2a80e205a Mon Sep 17 00:00:00 2001 From: Piotr Leszczynski Date: Mon, 9 Sep 2019 12:47:49 +0200 Subject: [PATCH 54/62] Changing how nodejs is being installed on circleCi. Switching from nvm to apt-get --- circle.yml | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/circle.yml b/circle.yml index 470d572..5fb30c0 100644 --- a/circle.yml +++ b/circle.yml @@ -13,15 +13,9 @@ jobs: - run: name: Install node@8.16 command: | - set +e - curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.5/install.sh | bash - export NVM_DIR="/opt/circleci/.nvm" - [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" - nvm install v8.16 - nvm alias default v8.16 - # Each step uses the same `$BASH_ENV`, so need to modify it - echo 'export NVM_DIR="/opt/circleci/.nvm"' >> $BASH_ENV - echo "[ -s \"$NVM_DIR/nvm.sh\" ] && . \"$NVM_DIR/nvm.sh\"" >> $BASH_ENV + curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash - + sudo apt install nodejs + node --version - restore_cache: key: dependency-cache-{{ checksum "package.json" }} - save_cache: From 1c0d969b474f7703dfe09110f966e2006cce6df2 Mon Sep 17 00:00:00 2001 From: Piotr Leszczynski Date: Mon, 9 Sep 2019 12:51:50 +0200 Subject: [PATCH 55/62] Changing how nodejs is being installed on circleCi. Switching from nvm to apt-get --- circle.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/circle.yml b/circle.yml index 5fb30c0..a06eb0c 100644 --- a/circle.yml +++ b/circle.yml @@ -13,9 +13,10 @@ jobs: - run: name: Install node@8.16 command: | - curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash - + curl -sL https://deb.nodesource.com/setup_10.x | sudo -E bash - sudo apt install nodejs node --version + npm --version - restore_cache: key: dependency-cache-{{ checksum "package.json" }} - save_cache: From eaa616f329935bed7d36e27c884fffbfdff6c912 Mon Sep 17 00:00:00 2001 From: Piotr Leszczynski Date: Mon, 9 Sep 2019 12:54:20 +0200 Subject: [PATCH 56/62] Changing how nodejs is being installed on circleCi. Switching from nvm to apt-get --- circle.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/circle.yml b/circle.yml index a06eb0c..6932f70 100644 --- a/circle.yml +++ b/circle.yml @@ -13,7 +13,7 @@ jobs: - run: name: Install node@8.16 command: | - curl -sL https://deb.nodesource.com/setup_10.x | sudo -E bash - + curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash - sudo apt install nodejs node --version npm --version From 75b8ca88a279129f734f1b7f7ffd037ad2656420 Mon Sep 17 00:00:00 2001 From: Piotr Leszczynski Date: Mon, 9 Sep 2019 12:55:58 +0200 Subject: [PATCH 57/62] Changing how nodejs is being installed on circleCi. Switching from nvm to apt-get --- circle.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/circle.yml b/circle.yml index 6932f70..8449b89 100644 --- a/circle.yml +++ b/circle.yml @@ -14,7 +14,7 @@ jobs: name: Install node@8.16 command: | curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash - - sudo apt install nodejs + sudo apt-get install -y nodejs node --version npm --version - restore_cache: From 7887800e80cf27a8411ac9f4edf908a24f08bc70 Mon Sep 17 00:00:00 2001 From: Piotr Leszczynski Date: Mon, 9 Sep 2019 13:00:16 +0200 Subject: [PATCH 58/62] Reverting : Changing how nodejs is being installed on circleCi. Going back to nvm. --- circle.yml | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/circle.yml b/circle.yml index 8449b89..470d572 100644 --- a/circle.yml +++ b/circle.yml @@ -13,10 +13,15 @@ jobs: - run: name: Install node@8.16 command: | - curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash - - sudo apt-get install -y nodejs - node --version - npm --version + set +e + curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.5/install.sh | bash + export NVM_DIR="/opt/circleci/.nvm" + [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" + nvm install v8.16 + nvm alias default v8.16 + # Each step uses the same `$BASH_ENV`, so need to modify it + echo 'export NVM_DIR="/opt/circleci/.nvm"' >> $BASH_ENV + echo "[ -s \"$NVM_DIR/nvm.sh\" ] && . \"$NVM_DIR/nvm.sh\"" >> $BASH_ENV - restore_cache: key: dependency-cache-{{ checksum "package.json" }} - save_cache: From 2788b5d3836cfb763b7a1528e96e5aef85394abf Mon Sep 17 00:00:00 2001 From: Piotr Leszczynski Date: Mon, 9 Sep 2019 13:15:30 +0200 Subject: [PATCH 59/62] Updating documentation. --- README.md | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 28457c8..29fc12d 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,8 @@ When instantiating kafka-avro you may pass the following options: * `parseOptions` **Object** Schema parse options to pass to `avro.parse()`. `parseOptions.wrapUnions` is set to `true` by default. * `httpsAgent` **Object** initialized [https Agent class](https://nodejs.org/api/https.html#https_class_https_agent) * `shouldFailWhenSchemaIsMissing` **Boolean** Set to true if producing a message for which no AVRO schema can be found should throw an error +* `keySubjectStrategy` **String** A SubjectNameStrategy for key. It is used by the Avro serializer to determine the subject name under which the event record schemas should be registered in the schema registry. The default is TopicNameStrategy. Allowed values are [TopicRecordNameStrategy, TopicNameStrategy, RecordNameStrategy] +* `valueSubjectStrategy` **String** **String** A SubjectNameStrategy for value. It is used by the Avro serializer to determine the subject name under which the event record schemas should be registered in the schema registry. The default is TopicNameStrategy. Allowed values are [TopicRecordNameStrategy, TopicNameStrategy, RecordNameStrategy] ### Producer @@ -202,8 +204,25 @@ kafka-avro intercepts all incoming messages and augments the object with two mor The KafkaAvro instance also provides the following methods: ### Support for several event types in the same topic -The Kafka Avro supports several events types in the same topic. The strategy chosen to implement this is [TopicRecordNameStrategy](https://github.com/confluentinc/schema-registry/blob/master/avro-serializer/src/main/java/io/confluent/kafka/serializers/subject/TopicRecordNameStrategy.java) -which is default strategy on the confluent platform. +Kafka Avro can support several events types in the same topic. This requires using TopicRecordNameStrategy strategy. + +```js +const KafkaAvro = require('kafka-avro'); + +const kafkaAvro = new KafkaAvro({ + kafkaBroker: 'localhost:9092', + schemaRegistry: 'http://localhost:8081', + keySubjectStrategy: "TopicRecordNameStrategy", + valueSubjectStrategy: "TopicRecordNameStrategy", +}); + +// Query the Schema Registry for all topic-schema's +// fetch them and evaluate them. +kafkaAvro.init() + .then(function() { + console.log('Ready to use'); + }); +``` You can read more about this here : https://www.confluent.io/blog/put-several-event-types-kafka-topic/ From 0c042e16aad297123a16cdd434066526963e8167 Mon Sep 17 00:00:00 2001 From: Piotr Leszczynski Date: Tue, 10 Sep 2019 09:54:29 +0200 Subject: [PATCH 60/62] Implementing pull request comments. --- lib/kafka-consumer.js | 32 +++++++++++++------------------- lib/magic-byte.js | 4 ++-- lib/subject-strategy.js | 1 - 3 files changed, 15 insertions(+), 22 deletions(-) diff --git a/lib/kafka-consumer.js b/lib/kafka-consumer.js index 5b19c06..c35cf4d 100644 --- a/lib/kafka-consumer.js +++ b/lib/kafka-consumer.js @@ -100,16 +100,15 @@ Consumer.prototype._onWrapper = function (consumerInstance, eventName, cb) { } return consumerInstance.__kafkaAvro_on('data', function (message) { - const keyDecoded = magicByte.fromMessageBuffer(message.key, this.sr); - const valueDecoded = magicByte.fromMessageBuffer(message.value, this.sr); - - if (!keyDecoded) { - log.warn('_onWrapper() :: Warning, consumer did not find topic on SR for key schema', + try { + const keyDecoded = magicByte.fromMessageBuffer(message.key, this.sr); + message.parsedKey = keyDecoded.value; + message.schemaIdKey = keyDecoded.schemaId; + } catch (err) { + log.warn({err}, '_onWrapper() :: Warning, consumer did not find topic on SR for key schema', {topic: message.topic}); - message.parsedKey = null; const {key} = message; - if (key) { try { message.parsedKey = Buffer.isBuffer(key) @@ -123,17 +122,17 @@ Consumer.prototype._onWrapper = function (consumerInstance, eventName, cb) { } } } - } else { - message.parsedKey = keyDecoded.value; - message.schemaIdKey = keyDecoded.schemaId; } - if (!valueDecoded) { - log.warn('_onWrapper() :: Warning, consumer did not find topic on SR for value schema', + try { + const valueDecoded = magicByte.fromMessageBuffer(message.value, this.sr); + message.parsed = valueDecoded.value; + message.schemaId = valueDecoded.schemaId; + cb(message); + } catch (err) { + log.warn({err}, '_onWrapper() :: Warning, consumer did not find topic on SR for value schema', {topic: message.topic}); - message.parsed = null; - if (message.value) { try { message.parsed = Buffer.isBuffer(message.value) @@ -148,13 +147,8 @@ Consumer.prototype._onWrapper = function (consumerInstance, eventName, cb) { } } cb(message); - return; } - message.parsed = valueDecoded.value; - message.schemaId = valueDecoded.schemaId; - - cb(message); }.bind(this)); }; diff --git a/lib/magic-byte.js b/lib/magic-byte.js index a24b10c..70c65b2 100644 --- a/lib/magic-byte.js +++ b/lib/magic-byte.js @@ -46,14 +46,14 @@ magicByte.toMessageBuffer = function (val, type, schemaId, optLength) { */ magicByte.fromMessageBuffer = function (encodedMessage, sr) { if (!encodedMessage || encodedMessage[0] !== MAGIC_BYTE) { - return null; + throw new TypeError('Message not serialized with magic byte'); } const schemaId = encodedMessage.readInt32BE(1); const schemaType = sr.schemaTypeById[('schema-' + schemaId)]; if (!schemaType) { - return null; + throw new TypeError(`No schema found for ${schemaId}`); } return { diff --git a/lib/subject-strategy.js b/lib/subject-strategy.js index dcb8576..424e917 100644 --- a/lib/subject-strategy.js +++ b/lib/subject-strategy.js @@ -31,7 +31,6 @@ const SUBJECT_STRATEGIES = Object.freeze({ RECORD_NAME_STRATEGY: 'RecordNameStrategy' }); -Object.freeze(SUBJECT_STRATEGIES); /** * A SubjectNameStrategy is used by the Avro serializer to determine From cff58a25e38da24e0a653e79323eda67dadac03a Mon Sep 17 00:00:00 2001 From: Piotr Leszczynski Date: Tue, 10 Sep 2019 17:09:01 +0200 Subject: [PATCH 61/62] Formatting requierments according to : [Node.js dependencies (e.g. require('util'))] [Third Party dependencies (e.g. require('axios'))] [Local dependencies (e.g. require('./local'))] --- lib/kafka-avro.js | 2 ++ lib/kafka-consumer.js | 1 + lib/kafka-producer.js | 1 + lib/schema-registry.js | 1 + 4 files changed, 5 insertions(+) diff --git a/lib/kafka-avro.js b/lib/kafka-avro.js index b1f9972..a048a3b 100644 --- a/lib/kafka-avro.js +++ b/lib/kafka-avro.js @@ -7,9 +7,11 @@ * Licensed under the MIT license. */ const EventEmitter = require('events').EventEmitter; + const Promise = require('bluebird'); const cip = require('cip'); const Kafka = require('node-rdkafka'); + const rootLog = require('./log.lib'); const log = rootLog.getChild(__filename); const SchemaRegistry = require('./schema-registry'); diff --git a/lib/kafka-consumer.js b/lib/kafka-consumer.js index c35cf4d..eec4e7e 100644 --- a/lib/kafka-consumer.js +++ b/lib/kafka-consumer.js @@ -4,6 +4,7 @@ const Promise = require('bluebird'); const cip = require('cip'); const kafka = require('node-rdkafka'); + const magicByte = require('./magic-byte'); const log = require('./log.lib').getChild(__filename); diff --git a/lib/kafka-producer.js b/lib/kafka-producer.js index a38934c..933c2ac 100644 --- a/lib/kafka-producer.js +++ b/lib/kafka-producer.js @@ -4,6 +4,7 @@ const Promise = require('bluebird'); const cip = require('cip'); const kafka = require('node-rdkafka'); + const magicByte = require('./magic-byte'); const log = require('./log.lib').getChild(__filename); diff --git a/lib/schema-registry.js b/lib/schema-registry.js index a6f2841..2428384 100644 --- a/lib/schema-registry.js +++ b/lib/schema-registry.js @@ -8,6 +8,7 @@ const cip = require('cip'); const axios = require('axios'); const avro = require('avsc'); let instance = axios; + const rootLog = require('./log.lib'); const log = rootLog.getChild(__filename); From d33c3529e9eb3df909f8c79e212a82470089f3a7 Mon Sep 17 00:00:00 2001 From: Piotr Leszczynski Date: Wed, 18 Sep 2019 09:41:39 +0200 Subject: [PATCH 62/62] Updating changelog. --- README.md | 5 +++++ package-lock.json | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 29fc12d..9dc23b8 100644 --- a/README.md +++ b/README.md @@ -300,6 +300,11 @@ You can use `docker-compose up` to up all the stack before you call your integra * `grunt release:major` for major number jump. ## Release History +- **3.0.0**, *19 Sep 2019* + - Adds support for `RecordNameStrategy`(io.confluent.kafka.serializers.subject.RecordNameStrategy) and `TopicRecordNameStrategy`(io.confluent.kafka.serializers.subject.TopicRecordNameStrategy) +schema subject name strategy. The purpose of the new strategies is to allow to put several event types in the same kafka topic (https://www.confluent.io/blog/put-several-event-types-kafka-topic) (by [pleszczy](github.com/pleszczy)) + - Adds new optional config params `keySubjectStrategy` and `valueSubjectStrategy` to configure schema subject name strategy for message key and value. Supported strategies are + `[TopicNameStrategy, TopicRecordNameStrategy, RecordNameStrategy]` (by [pleszczy](github.com/pleszczy)) - **1.2.1**, *06 Sep 2019* - Adds a new the optional config param `shouldFailWhenSchemaIsMissing` to let the producer fail when no schema could be found (instead of producing as JSON) (by [bfncs](https://github.com/bfncs)) - **1.2.0**, *03 March 2019* diff --git a/package-lock.json b/package-lock.json index ad5ece9..8e16836 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "kafka-avro", - "version": "1.2.0", + "version": "1.2.1", "lockfileVersion": 1, "requires": true, "dependencies": {