diff --git a/libraries/logger/.eslintrc b/libraries/logger/.eslintrc new file mode 100644 index 0000000000..5ca14a0e9f --- /dev/null +++ b/libraries/logger/.eslintrc @@ -0,0 +1,75 @@ +{ + "extends": [ + "standard", + "standard-react", + "prettier", + "prettier/react", + "prettier/standard", + "plugin:jsx-a11y/recommended" + ], + "plugins": [ + "jsx-a11y", + "mocha", + "chai-expect", + "chai-friendly" + ], + "env": { + "mocha": true + }, + "globals": { + "expect": true, + "define": true, + "$": true, + "angular": true, + // Injected in layout.pug + "user_id": true, + "ace": true + }, + "settings": { + // Tell eslint-plugin-react to detect which version of React we are using + "react": { + "version": "detect" + } + }, + "rules": { + "max-len": ["error", { + "ignoreUrls": true, + // Ignore long describe/it test blocks, long import/require statements + "ignorePattern": "(^\\s*(it|describe)\\s*\\(['\"]|^import\\s*.*\\s*from\\s*['\"]|^.*\\s*=\\s*require\\(['\"])" + }], + + // Fix conflict between prettier & standard by overriding to prefer + // double quotes + "jsx-quotes": ["error", "prefer-double"], + + // Override weird behaviour of jsx-a11y label-has-for (says labels must be + // nested *and* have for/id attributes) + "jsx-a11y/label-has-for": [ + "error", + { + "required": { + "some": [ + "nesting", + "id" + ] + } + } + ], + + // Add some mocha specific rules + "mocha/handle-done-callback": "error", + "mocha/no-exclusive-tests": "error", + "mocha/no-global-tests": "error", + "mocha/no-identical-title": "error", + "mocha/no-nested-tests": "error", + "mocha/no-pending-tests": "error", + "mocha/no-skipped-tests": "error", + + // Add some chai specific rules + "chai-expect/missing-assertion": "error", + "chai-expect/terminating-properties": "error", + // Swap the no-unused-expressions rule with a more chai-friendly one + "no-unused-expressions": 0, + "chai-friendly/no-unused-expressions": "error" + } +} diff --git a/libraries/logger/.prettierrc b/libraries/logger/.prettierrc new file mode 100644 index 0000000000..b2095be81e --- /dev/null +++ b/libraries/logger/.prettierrc @@ -0,0 +1,4 @@ +{ + "semi": false, + "singleQuote": true +} diff --git a/libraries/logger/index.js b/libraries/logger/index.js index aeca420019..6cd6f9ef8b 100755 --- a/libraries/logger/index.js +++ b/libraries/logger/index.js @@ -1 +1 @@ -module.exports = require("./logging-manager.js"); +module.exports = require('./logging-manager.js') diff --git a/libraries/logger/logging-manager.js b/libraries/logger/logging-manager.js index d791844204..04e3bc18b2 100644 --- a/libraries/logger/logging-manager.js +++ b/libraries/logger/logging-manager.js @@ -1,16 +1,16 @@ -const bunyan = require("bunyan"); -const request = require("request"); +const bunyan = require('bunyan') +const request = require('request') Logger = module.exports = { initialize(name) { this.isProduction = - (process.env["NODE_ENV"] || "").toLowerCase() === "production"; + (process.env['NODE_ENV'] || '').toLowerCase() === 'production' this.defaultLevel = - process.env["LOG_LEVEL"] || (this.isProduction ? "warn" : "debug"); - this.loggerName = name; + process.env['LOG_LEVEL'] || (this.isProduction ? 'warn' : 'debug') + this.loggerName = name this.ringBuffer = new bunyan.RingBuffer({ - limit: process.env["LOG_RING_BUFFER_SIZE"] || 30 - }); + limit: process.env['LOG_RING_BUFFER_SIZE'] || 30 + }) this.logger = bunyan.createLogger({ name, serializers: bunyan.stdSerializers, @@ -20,88 +20,88 @@ Logger = module.exports = { stream: process.stdout }, { - level: "trace", - type: "raw", + level: 'trace', + type: 'raw', stream: this.ringBuffer } ] - }); + }) if (this.isProduction) { // clear interval if already set if (this.checkInterval) { - clearInterval(this.checkInterval); + clearInterval(this.checkInterval) } // check for log level override on startup - this.checkLogLevel(); + this.checkLogLevel() // re-check log level every minute - const checkLogLevel = () => this.checkLogLevel(); - this.checkInterval = setInterval(checkLogLevel, 1000 * 60); + const checkLogLevel = () => this.checkLogLevel() + this.checkInterval = setInterval(checkLogLevel, 1000 * 60) } - return this; + return this }, checkLogLevel() { const options = { headers: { - "Metadata-Flavor": "Google" + 'Metadata-Flavor': 'Google' }, uri: `http://metadata.google.internal/computeMetadata/v1/project/attributes/${ this.loggerName }-setLogLevelEndTime` - }; + } request(options, (err, response, body) => { if (parseInt(body) > Date.now()) { - this.logger.level("trace"); + this.logger.level('trace') } else { - this.logger.level(this.defaultLevel); + this.logger.level(this.defaultLevel) } - }); + }) }, initializeErrorReporting(sentry_dsn, options) { - const raven = require("raven"); - this.raven = new raven.Client(sentry_dsn, options); - this.lastErrorTimeStamp = 0; // for rate limiting on sentry reporting - this.lastErrorCount = 0; + const raven = require('raven') + this.raven = new raven.Client(sentry_dsn, options) + this.lastErrorTimeStamp = 0 // for rate limiting on sentry reporting + this.lastErrorCount = 0 }, captureException(attributes, message, level) { // handle case of logger.error "message" - let key, value; - if (typeof attributes === "string") { - attributes = { err: new Error(attributes) }; + let key, value + if (typeof attributes === 'string') { + attributes = { err: new Error(attributes) } } // extract any error object - let error = attributes.err || attributes.error; + let error = attributes.err || attributes.error // avoid reporting errors twice for (key in attributes) { - value = attributes[key]; + value = attributes[key] if (value instanceof Error && value.reportedToSentry) { - return; + return } } // include our log message in the error report if (error == null) { - if (typeof message === "string") { - error = { message }; + if (typeof message === 'string') { + error = { message } } } else if (message != null) { - attributes.description = message; + attributes.description = message } // report the error if (error != null) { // capture attributes and use *_id objects as tags - const tags = {}; - const extra = {}; + const tags = {} + const extra = {} for (key in attributes) { - value = attributes[key]; - if (key.match(/_id/) && typeof value === "string") { - tags[key] = value; + value = attributes[key] + if (key.match(/_id/) && typeof value === 'string') { + tags[key] = value } - extra[key] = value; + extra[key] = value } // capture req object if available - const { req } = attributes; + const { req } = attributes if (req != null) { extra.req = { method: req.method, @@ -109,112 +109,112 @@ Logger = module.exports = { query: req.query, headers: req.headers, ip: req.ip - }; + } } // recreate error objects that have been converted to a normal object - if (!(error instanceof Error) && typeof error === "object") { - const newError = new Error(error.message); + if (!(error instanceof Error) && typeof error === 'object') { + const newError = new Error(error.message) for (key of Object.keys(error || {})) { - value = error[key]; - newError[key] = value; + value = error[key] + newError[key] = value } - error = newError; + error = newError } // filter paths from the message to avoid duplicate errors in sentry // (e.g. errors from `fs` methods which have a path attribute) try { if (error.path) { - error.message = error.message.replace(` '${error.path}'`, ""); + error.message = error.message.replace(` '${error.path}'`, '') } } catch (error1) {} // send the error to sentry try { - this.raven.captureException(error, { tags, extra, level }); + this.raven.captureException(error, { tags, extra, level }) // put a flag on the errors to avoid reporting them multiple times return (() => { - const result = []; + const result = [] for (key in attributes) { - value = attributes[key]; + value = attributes[key] if (value instanceof Error) { - result.push((value.reportedToSentry = true)); + result.push((value.reportedToSentry = true)) } else { - result.push(undefined); + result.push(undefined) } } - return result; - })(); + return result + })() } catch (error2) { - return; + return } } }, debug() { - return this.logger.debug.apply(this.logger, arguments); + return this.logger.debug.apply(this.logger, arguments) }, info() { - return this.logger.info.apply(this.logger, arguments); + return this.logger.info.apply(this.logger, arguments) }, log() { - return this.logger.info.apply(this.logger, arguments); + return this.logger.info.apply(this.logger, arguments) }, error(attributes, message, ...args) { if (this.isProduction) { - attributes.logBuffer = this.ringBuffer.records; + attributes.logBuffer = this.ringBuffer.records } - this.logger.error(attributes, message, ...Array.from(args)); + this.logger.error(attributes, message, ...Array.from(args)) if (this.raven != null) { - const MAX_ERRORS = 5; // maximum number of errors in 1 minute - const now = new Date(); + const MAX_ERRORS = 5 // maximum number of errors in 1 minute + const now = new Date() // have we recently reported an error? - const recentSentryReport = now - this.lastErrorTimeStamp < 60 * 1000; + const recentSentryReport = now - this.lastErrorTimeStamp < 60 * 1000 // if so, increment the error count if (recentSentryReport) { - this.lastErrorCount++; + this.lastErrorCount++ } else { - this.lastErrorCount = 0; - this.lastErrorTimeStamp = now; + this.lastErrorCount = 0 + this.lastErrorTimeStamp = now } // only report 5 errors every minute to avoid overload if (this.lastErrorCount < MAX_ERRORS) { // add a note if the rate limit has been hit const note = - this.lastErrorCount + 1 === MAX_ERRORS ? "(rate limited)" : ""; + this.lastErrorCount + 1 === MAX_ERRORS ? '(rate limited)' : '' // report the exception - return this.captureException(attributes, message, `error${note}`); + return this.captureException(attributes, message, `error${note}`) } } }, err() { - return this.error.apply(this, arguments); + return this.error.apply(this, arguments) }, warn() { - return this.logger.warn.apply(this.logger, arguments); + return this.logger.warn.apply(this.logger, arguments) }, fatal(attributes, message, callback) { if (callback == null) { - callback = function() {}; + callback = function() {} } - this.logger.fatal(attributes, message); + this.logger.fatal(attributes, message) if (this.raven != null) { var cb = function(e) { // call the callback once after 'logged' or 'error' event - callback(); - return (cb = function() {}); - }; - this.captureException(attributes, message, "fatal"); - this.raven.once("logged", cb); - return this.raven.once("error", cb); + callback() + return (cb = function() {}) + } + this.captureException(attributes, message, 'fatal') + this.raven.once('logged', cb) + return this.raven.once('error', cb) } else { - return callback(); + return callback() } } -}; +} -Logger.initialize("default-sharelatex"); +Logger.initialize('default-sharelatex') diff --git a/libraries/logger/package-lock.json b/libraries/logger/package-lock.json deleted file mode 100644 index c76f298483..0000000000 --- a/libraries/logger/package-lock.json +++ /dev/null @@ -1,881 +0,0 @@ -{ - "name": "logger-sharelatex", - "version": "1.6.0", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@sinonjs/commons": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.4.0.tgz", - "integrity": "sha512-9jHK3YF/8HtJ9wCAbG+j8cD0i0+ATS9A7gXFqS36TblLPNy6rEEc+SB0imo91eCboGaBYGV/MT1/br/J+EE7Tw==", - "dev": true, - "requires": { - "type-detect": "4.0.8" - } - }, - "@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" - } - }, - "@sinonjs/samsam": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-3.2.0.tgz", - "integrity": "sha512-j5F1rScewLtx6pbTK0UAjA3jJj4RYiSKOix53YWv+Jzy/AZ69qHxUpU8fwVLjyKbEEud9QrLpv6Ggs7WqTimYw==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.0.2", - "array-from": "^2.1.1", - "lodash": "^4.17.11" - } - }, - "@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 - }, - "ajv": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz", - "integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==", - "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "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 - }, - "asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "requires": { - "safer-buffer": "~2.1.0" - } - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" - }, - "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-hook-jl": { - "version": "1.7.6", - "resolved": "https://registry.npmjs.org/async-hook-jl/-/async-hook-jl-1.7.6.tgz", - "integrity": "sha512-gFaHkFfSxTjvoxDMYqDuGHlcRyUuamF8s+ZTtJdDzqjws4mCt7v0vuV79/E2Wr2/riMQgtG4/yUtXWs1gZ7JMg==", - "requires": { - "stack-chain": "^1.3.7" - } - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" - }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" - }, - "aws4": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", - "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" - }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "requires": { - "tweetnacl": "^0.14.3" - } - }, - "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 - }, - "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" - } - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" - }, - "chai": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", - "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==", - "dev": true, - "requires": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.2", - "deep-eql": "^3.0.1", - "get-func-name": "^2.0.0", - "pathval": "^1.1.0", - "type-detect": "^4.0.5" - } - }, - "check-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", - "dev": true - }, - "cls-hooked": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/cls-hooked/-/cls-hooked-4.2.2.tgz", - "integrity": "sha512-J4Xj5f5wq/4jAvcdgoGsL3G103BtWpZrMo8NEinRltN+xpTZdI+M38pyQqhuFU/P792xkMFvnKSf+Lm81U1bxw==", - "requires": { - "async-hook-jl": "^1.7.6", - "emitter-listener": "^1.0.1", - "semver": "^5.4.1" - } - }, - "combined-stream": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", - "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==", - "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 - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "cookie": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", - "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" - }, - "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=" - }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "requires": { - "assert-plus": "^1.0.0" - } - }, - "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" - } - }, - "deep-eql": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", - "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", - "dev": true, - "requires": { - "type-detect": "^4.0.0" - } - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" - }, - "diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", - "dev": true - }, - "dtrace-provider": { - "version": "0.8.7", - "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.7.tgz", - "integrity": "sha1-3JObTT4GIM/gwc2APQ0tftBP/QQ=", - "optional": true, - "requires": { - "nan": "^2.10.0" - } - }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "emitter-listener": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/emitter-listener/-/emitter-listener-1.1.2.tgz", - "integrity": "sha512-Bt1sBAGFHY9DKY+4/2cV6izcKJUf5T7/gkdmkxzX/qv9CcGH8xSwVRW5mtX03SWJtRTWSOpzCuWN9rBFYZepZQ==", - "requires": { - "shimmer": "^1.2.0" - } - }, - "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 - }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" - }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" - }, - "fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" - }, - "fast-json-stable-stringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" - }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" - }, - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "get-func-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", - "dev": true - }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "requires": { - "assert-plus": "^1.0.0" - } - }, - "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" - } - }, - "growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "dev": true - }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" - }, - "har-validator": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", - "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", - "requires": { - "ajv": "^6.5.5", - "har-schema": "^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 - }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.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.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" - }, - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" - }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" - }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" - }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - } - }, - "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 - }, - "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", - "dev": true - }, - "lolex": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/lolex/-/lolex-3.1.0.tgz", - "integrity": "sha512-zFo5MgCJ0rZ7gQg69S4pqBsLURbFw11X68C18OcJjJQbqaXm2NoTrGl1IMM3TIz0/BnN1tIs2tzmmqvCsOMMjw==", - "dev": true - }, - "lsmod": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lsmod/-/lsmod-1.0.0.tgz", - "integrity": "sha1-mgD3bco26yP6BTUK/htYXUKZ5ks=" - }, - "mime-db": { - "version": "1.38.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.38.0.tgz", - "integrity": "sha512-bqVioMFFzc2awcdJZIzR3HjZFX20QhilVS7hytkKrv7xFAn8bM1gzc/FOX2awLISvWe0PV8ptFKcon+wZ5qYkg==" - }, - "mime-types": { - "version": "2.1.22", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.22.tgz", - "integrity": "sha512-aGl6TZGnhm/li6F7yx82bJiBZwgiEa4Hf6CNr8YO+r5UHr53tSTYZb102zyU50DOWWKeOv0uQLRL0/9EiKWCog==", - "requires": { - "mime-db": "~1.38.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": { - "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" - } - } - } - }, - "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=", - "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.12.1", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.12.1.tgz", - "integrity": "sha512-JY7V6lRkStKcKTvHO5NVSQRv+RV+FIL5pvDoLiAtSL9pKlC5x9PKQcZDsq7m4FO4d57mkhC6Z+QhAh3Jdk5JFw==", - "optional": true - }, - "ncp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", - "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=", - "optional": true - }, - "nise": { - "version": "1.4.10", - "resolved": "https://registry.npmjs.org/nise/-/nise-1.4.10.tgz", - "integrity": "sha512-sa0RRbj53dovjc7wombHmVli9ZihXbXCQ2uH3TNm03DyvOSIQbxg+pbqDKrk2oxMK1rtLGVlKxcB9rrc6X5YjA==", - "dev": true, - "requires": { - "@sinonjs/formatio": "^3.1.0", - "@sinonjs/text-encoding": "^0.7.1", - "just-extend": "^4.0.2", - "lolex": "^2.3.2", - "path-to-regexp": "^1.7.0" - }, - "dependencies": { - "lolex": { - "version": "2.7.5", - "resolved": "https://registry.npmjs.org/lolex/-/lolex-2.7.5.tgz", - "integrity": "sha512-l9x0+1offnKKIzYVjyXU2SiwhXDLekRzKyhnbyldPHvC7BvLPVpdNUNR2KeMAiCN2D/kLNttZgQD5WjSxuBx3Q==", - "dev": true - } - } - }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1" - } - }, - "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-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" - } - }, - "pathval": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", - "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", - "dev": true - }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" - }, - "psl": { - "version": "1.1.31", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz", - "integrity": "sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==" - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" - }, - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" - }, - "raven": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/raven/-/raven-1.1.3.tgz", - "integrity": "sha1-QnPBrm005CMPUbLAEEGjK5Iygio=", - "requires": { - "cookie": "0.3.1", - "json-stringify-safe": "5.0.1", - "lsmod": "1.0.0", - "stack-trace": "0.0.9", - "uuid": "3.0.0" - }, - "dependencies": { - "uuid": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.0.tgz", - "integrity": "sha1-Zyj8BFnEUNeWqZwxg3VpvfZy1yg=" - } - } - }, - "request": { - "version": "2.88.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", - "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.0", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.4.3", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "dependencies": { - "uuid": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" - } - } - }, - "require-like": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/require-like/-/require-like-0.1.2.tgz", - "integrity": "sha1-rW8wwTvs15cBDEaK+ndcDAprR/o=", - "dev": true - }, - "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" - } - }, - "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==" - }, - "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==" - }, - "sandboxed-module": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/sandboxed-module/-/sandboxed-module-2.0.3.tgz", - "integrity": "sha1-x+VFkzm7y6KMUwPusz9ug4e/upY=", - "dev": true, - "requires": { - "require-like": "0.1.2", - "stack-trace": "0.0.9" - } - }, - "semver": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", - "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==" - }, - "shimmer": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz", - "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==" - }, - "sinon": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-7.2.3.tgz", - "integrity": "sha512-i6j7sqcLEqTYqUcMV327waI745VASvYuSuQMCjbAwlpAeuCgKZ3LtrjDxAbu+GjNQR0FEDpywtwGCIh8GicNyg==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.3.0", - "@sinonjs/formatio": "^3.1.0", - "@sinonjs/samsam": "^3.0.2", - "diff": "^3.5.0", - "lolex": "^3.0.0", - "nise": "^1.4.8", - "supports-color": "^5.5.0" - }, - "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" - } - } - } - }, - "sinon-chai": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/sinon-chai/-/sinon-chai-3.3.0.tgz", - "integrity": "sha512-r2JhDY7gbbmh5z3Q62pNbrjxZdOAjpsqW/8yxAZRSqLZqowmfGZPGUZPFf3UX36NLis0cv8VEM5IJh9HgkSOAA==", - "dev": true - }, - "sshpk": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", - "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - } - }, - "stack-chain": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/stack-chain/-/stack-chain-1.3.7.tgz", - "integrity": "sha1-0ZLJ/06moiyUxN1FkXHj8AzqEoU=" - }, - "stack-trace": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.9.tgz", - "integrity": "sha1-qPbq7KkGdMMz58Q5U/J1tFFRBpU=" - }, - "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" - } - }, - "tough-cookie": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", - "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", - "requires": { - "psl": "^1.1.24", - "punycode": "^1.4.1" - }, - "dependencies": { - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" - } - } - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" - }, - "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 - }, - "uri-js": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", - "requires": { - "punycode": "^2.1.0" - } - }, - "uuid": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" - }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - } - } -} diff --git a/libraries/logger/package.json b/libraries/logger/package.json index 692ba2b815..92f5a4dd81 100644 --- a/libraries/logger/package.json +++ b/libraries/logger/package.json @@ -8,7 +8,9 @@ }, "version": "1.7.0", "scripts": { - "test": "mocha test/**/*.js" + "test": "mocha test/**/*.js", + "format": "prettier-eslint '**/*.js' --list-different", + "format:fix": "prettier-eslint '**/*.js' --write" }, "dependencies": { "bunyan": "1.8.12", diff --git a/libraries/logger/test/unit/loggingManagerTests.js b/libraries/logger/test/unit/loggingManagerTests.js index a1f505d258..7d1ec558a4 100644 --- a/libraries/logger/test/unit/loggingManagerTests.js +++ b/libraries/logger/test/unit/loggingManagerTests.js @@ -1,20 +1,20 @@ -const SandboxedModule = require("sandboxed-module"); -const bunyan = require("bunyan"); -const chai = require("chai"); -const path = require("path"); -const sinon = require("sinon"); -const sinonChai = require("sinon-chai"); +const SandboxedModule = require('sandboxed-module') +const bunyan = require('bunyan') +const chai = require('chai') +const path = require('path') +const sinon = require('sinon') +const sinonChai = require('sinon-chai') -chai.use(sinonChai); -chai.should(); +chai.use(sinonChai) +chai.should() -const modulePath = path.join(__dirname, "../../logging-manager.js"); +const modulePath = path.join(__dirname, '../../logging-manager.js') -describe("LoggingManager", function() { +describe('LoggingManager', function() { beforeEach(function() { - this.start = Date.now(); - this.clock = sinon.useFakeTimers(this.start); - this.captureException = sinon.stub(); + this.start = Date.now() + this.clock = sinon.useFakeTimers(this.start) + this.captureException = sinon.stub() this.mockBunyanLogger = { debug: sinon.stub(), error: sinon.stub(), @@ -22,11 +22,11 @@ describe("LoggingManager", function() { info: sinon.stub(), level: sinon.stub(), warn: sinon.stub() - }; + } this.mockRavenClient = { captureException: this.captureException, once: sinon.stub().yields() - }; + } this.LoggingManager = SandboxedModule.require(modulePath, { globals: { console }, requires: { @@ -39,335 +39,335 @@ describe("LoggingManager", function() { }), request: (this.Request = sinon.stub()) } - }); - this.loggerName = "test"; - this.logger = this.LoggingManager.initialize(this.loggerName); - this.logger.initializeErrorReporting("test_dsn"); - }); + }) + this.loggerName = 'test' + this.logger = this.LoggingManager.initialize(this.loggerName) + this.logger.initializeErrorReporting('test_dsn') + }) afterEach(function() { - this.clock.restore(); - }); + this.clock.restore() + }) - describe("initialize", function() { + describe('initialize', function() { beforeEach(function() { - this.checkLogLevelStub = sinon.stub(this.LoggingManager, "checkLogLevel"); - this.Bunyan.createLogger.reset(); - }); + this.checkLogLevelStub = sinon.stub(this.LoggingManager, 'checkLogLevel') + this.Bunyan.createLogger.reset() + }) afterEach(function() { - this.checkLogLevelStub.restore(); - }); + this.checkLogLevelStub.restore() + }) - describe("not in production", function() { + describe('not in production', function() { beforeEach(function() { - this.logger = this.LoggingManager.initialize(this.loggerName); - }); + this.logger = this.LoggingManager.initialize(this.loggerName) + }) - it("should default to log level debug", function() { + it('should default to log level debug', function() { this.Bunyan.createLogger.firstCall.args[0].streams[0].level.should.equal( - "debug" - ); - }); + 'debug' + ) + }) - it("should not run checkLogLevel", function() { - this.checkLogLevelStub.should.not.have.been.called; - }); - }); + it('should not run checkLogLevel', function() { + this.checkLogLevelStub.should.not.have.been.called + }) + }) - describe("in production", function() { + describe('in production', function() { beforeEach(function() { - process.env.NODE_ENV = "production"; - this.logger = this.LoggingManager.initialize(this.loggerName); - }); + process.env.NODE_ENV = 'production' + this.logger = this.LoggingManager.initialize(this.loggerName) + }) - afterEach(() => delete process.env.NODE_ENV); + afterEach(() => delete process.env.NODE_ENV) - it("should default to log level warn", function() { + it('should default to log level warn', function() { this.Bunyan.createLogger.firstCall.args[0].streams[0].level.should.equal( - "warn" - ); - }); + 'warn' + ) + }) - it("should run checkLogLevel", function() { - this.checkLogLevelStub.should.have.been.calledOnce; - }); + it('should run checkLogLevel', function() { + this.checkLogLevelStub.should.have.been.calledOnce + }) - describe("after 1 minute", () => - it("should run checkLogLevel again", function() { - this.clock.tick(61 * 1000); - this.checkLogLevelStub.should.have.been.calledTwice; - })); + describe('after 1 minute', () => + it('should run checkLogLevel again', function() { + this.clock.tick(61 * 1000) + this.checkLogLevelStub.should.have.been.calledTwice + })) - describe("after 2 minutes", () => - it("should run checkLogLevel again", function() { - this.clock.tick(121 * 1000); - this.checkLogLevelStub.should.have.been.calledThrice; - })); - }); + describe('after 2 minutes', () => + it('should run checkLogLevel again', function() { + this.clock.tick(121 * 1000) + this.checkLogLevelStub.should.have.been.calledThrice + })) + }) - describe("when LOG_LEVEL set in env", function() { + describe('when LOG_LEVEL set in env', function() { beforeEach(function() { - process.env.LOG_LEVEL = "trace"; - this.LoggingManager.initialize(); - }); + process.env.LOG_LEVEL = 'trace' + this.LoggingManager.initialize() + }) - afterEach(() => delete process.env.LOG_LEVEL); + afterEach(() => delete process.env.LOG_LEVEL) - it("should use custom log level", function() { + it('should use custom log level', function() { this.Bunyan.createLogger.firstCall.args[0].streams[0].level.should.equal( - "trace" - ); - }); - }); - }); + 'trace' + ) + }) + }) + }) - describe("bunyan logging", function() { + describe('bunyan logging', function() { beforeEach(function() { - this.logArgs = [{ foo: "bar" }, "foo", "bar"]; - }); + this.logArgs = [{ foo: 'bar' }, 'foo', 'bar'] + }) - it("should log debug", function() { - this.logger.debug(this.logArgs); - this.mockBunyanLogger.debug.should.have.been.calledWith(this.logArgs); - }); + it('should log debug', function() { + this.logger.debug(this.logArgs) + this.mockBunyanLogger.debug.should.have.been.calledWith(this.logArgs) + }) - it("should log error", function() { - this.logger.error(this.logArgs); - this.mockBunyanLogger.error.should.have.been.calledWith(this.logArgs); - }); + it('should log error', function() { + this.logger.error(this.logArgs) + this.mockBunyanLogger.error.should.have.been.calledWith(this.logArgs) + }) - it("should log fatal", function() { - this.logger.fatal(this.logArgs); - this.mockBunyanLogger.fatal.should.have.been.calledWith(this.logArgs); - }); + it('should log fatal', function() { + this.logger.fatal(this.logArgs) + this.mockBunyanLogger.fatal.should.have.been.calledWith(this.logArgs) + }) - it("should log info", function() { - this.logger.info(this.logArgs); - this.mockBunyanLogger.info.should.have.been.calledWith(this.logArgs); - }); + it('should log info', function() { + this.logger.info(this.logArgs) + this.mockBunyanLogger.info.should.have.been.calledWith(this.logArgs) + }) - it("should log warn", function() { - this.logger.warn(this.logArgs); - this.mockBunyanLogger.warn.should.have.been.calledWith(this.logArgs); - }); + it('should log warn', function() { + this.logger.warn(this.logArgs) + this.mockBunyanLogger.warn.should.have.been.calledWith(this.logArgs) + }) - it("should log err", function() { - this.logger.err(this.logArgs); - this.mockBunyanLogger.error.should.have.been.calledWith(this.logArgs); - }); + it('should log err', function() { + this.logger.err(this.logArgs) + this.mockBunyanLogger.error.should.have.been.calledWith(this.logArgs) + }) - it("should log log", function() { - this.logger.log(this.logArgs); - this.mockBunyanLogger.info.should.have.been.calledWith(this.logArgs); - }); - }); + it('should log log', function() { + this.logger.log(this.logArgs) + this.mockBunyanLogger.info.should.have.been.calledWith(this.logArgs) + }) + }) - describe("logger.error", function() { - it("should report a single error to sentry", function() { - this.logger.error({ foo: "bar" }, "message"); - this.captureException.called.should.equal(true); - }); + describe('logger.error', function() { + it('should report a single error to sentry', function() { + this.logger.error({ foo: 'bar' }, 'message') + this.captureException.called.should.equal(true) + }) - it("should report the same error to sentry only once", function() { - const error1 = new Error("this is the error"); - this.logger.error({ foo: error1 }, "first message"); - this.logger.error({ bar: error1 }, "second message"); - this.captureException.callCount.should.equal(1); - }); + it('should report the same error to sentry only once', function() { + const error1 = new Error('this is the error') + this.logger.error({ foo: error1 }, 'first message') + this.logger.error({ bar: error1 }, 'second message') + this.captureException.callCount.should.equal(1) + }) - it("should report two different errors to sentry individually", function() { - const error1 = new Error("this is the error"); - const error2 = new Error("this is the error"); - this.logger.error({ foo: error1 }, "first message"); - this.logger.error({ bar: error2 }, "second message"); - this.captureException.callCount.should.equal(2); - }); + it('should report two different errors to sentry individually', function() { + const error1 = new Error('this is the error') + const error2 = new Error('this is the error') + this.logger.error({ foo: error1 }, 'first message') + this.logger.error({ bar: error2 }, 'second message') + this.captureException.callCount.should.equal(2) + }) - it("should remove the path from fs errors", function() { + it('should remove the path from fs errors', function() { const fsError = new Error( "Error: ENOENT: no such file or directory, stat '/tmp/3279b8d0-da10-11e8-8255-efd98985942b'" - ); - fsError.path = "/tmp/3279b8d0-da10-11e8-8255-efd98985942b"; - this.logger.error({ err: fsError }, "message"); + ) + fsError.path = '/tmp/3279b8d0-da10-11e8-8255-efd98985942b' + this.logger.error({ err: fsError }, 'message') this.captureException .calledWith( sinon.match.has( - "message", - "Error: ENOENT: no such file or directory, stat" + 'message', + 'Error: ENOENT: no such file or directory, stat' ) ) - .should.equal(true); - }); + .should.equal(true) + }) - it("for multiple errors should only report a maximum of 5 errors to sentry", function() { - this.logger.error({ foo: "bar" }, "message"); - this.logger.error({ foo: "bar" }, "message"); - this.logger.error({ foo: "bar" }, "message"); - this.logger.error({ foo: "bar" }, "message"); - this.logger.error({ foo: "bar" }, "message"); - this.logger.error({ foo: "bar" }, "message"); - this.logger.error({ foo: "bar" }, "message"); - this.logger.error({ foo: "bar" }, "message"); - this.logger.error({ foo: "bar" }, "message"); - this.captureException.callCount.should.equal(5); - }); + it('for multiple errors should only report a maximum of 5 errors to sentry', function() { + this.logger.error({ foo: 'bar' }, 'message') + this.logger.error({ foo: 'bar' }, 'message') + this.logger.error({ foo: 'bar' }, 'message') + this.logger.error({ foo: 'bar' }, 'message') + this.logger.error({ foo: 'bar' }, 'message') + this.logger.error({ foo: 'bar' }, 'message') + this.logger.error({ foo: 'bar' }, 'message') + this.logger.error({ foo: 'bar' }, 'message') + this.logger.error({ foo: 'bar' }, 'message') + this.captureException.callCount.should.equal(5) + }) - it("for multiple errors with a minute delay should report 10 errors to sentry", function() { + it('for multiple errors with a minute delay should report 10 errors to sentry', function() { // the first five errors should be reported to sentry - this.logger.error({ foo: "bar" }, "message"); - this.logger.error({ foo: "bar" }, "message"); - this.logger.error({ foo: "bar" }, "message"); - this.logger.error({ foo: "bar" }, "message"); - this.logger.error({ foo: "bar" }, "message"); + this.logger.error({ foo: 'bar' }, 'message') + this.logger.error({ foo: 'bar' }, 'message') + this.logger.error({ foo: 'bar' }, 'message') + this.logger.error({ foo: 'bar' }, 'message') + this.logger.error({ foo: 'bar' }, 'message') // the following errors should not be reported - this.logger.error({ foo: "bar" }, "message"); - this.logger.error({ foo: "bar" }, "message"); - this.logger.error({ foo: "bar" }, "message"); - this.logger.error({ foo: "bar" }, "message"); + this.logger.error({ foo: 'bar' }, 'message') + this.logger.error({ foo: 'bar' }, 'message') + this.logger.error({ foo: 'bar' }, 'message') + this.logger.error({ foo: 'bar' }, 'message') // allow a minute to pass - this.clock.tick(this.start + 61 * 1000); + this.clock.tick(this.start + 61 * 1000) // after a minute the next five errors should be reported to sentry - this.logger.error({ foo: "bar" }, "message"); - this.logger.error({ foo: "bar" }, "message"); - this.logger.error({ foo: "bar" }, "message"); - this.logger.error({ foo: "bar" }, "message"); - this.logger.error({ foo: "bar" }, "message"); + this.logger.error({ foo: 'bar' }, 'message') + this.logger.error({ foo: 'bar' }, 'message') + this.logger.error({ foo: 'bar' }, 'message') + this.logger.error({ foo: 'bar' }, 'message') + this.logger.error({ foo: 'bar' }, 'message') // the following errors should not be reported to sentry - this.logger.error({ foo: "bar" }, "message"); - this.logger.error({ foo: "bar" }, "message"); - this.logger.error({ foo: "bar" }, "message"); - this.logger.error({ foo: "bar" }, "message"); - this.captureException.callCount.should.equal(10); - }); - }); + this.logger.error({ foo: 'bar' }, 'message') + this.logger.error({ foo: 'bar' }, 'message') + this.logger.error({ foo: 'bar' }, 'message') + this.logger.error({ foo: 'bar' }, 'message') + this.captureException.callCount.should.equal(10) + }) + }) - describe("checkLogLevel", function() { - it("should request log level override from google meta data service", function() { - this.logger.checkLogLevel(); + describe('checkLogLevel', function() { + it('should request log level override from google meta data service', function() { + this.logger.checkLogLevel() const options = { headers: { - "Metadata-Flavor": "Google" + 'Metadata-Flavor': 'Google' }, uri: `http://metadata.google.internal/computeMetadata/v1/project/attributes/${ this.loggerName }-setLogLevelEndTime` - }; - this.Request.should.have.been.calledWithMatch(options); - }); + } + this.Request.should.have.been.calledWithMatch(options) + }) - describe("when request has error", function() { + describe('when request has error', function() { beforeEach(function() { - this.Request.yields("error"); - this.logger.checkLogLevel(); - }); + this.Request.yields('error') + this.logger.checkLogLevel() + }) - it("should only set default level", function() { + it('should only set default level', function() { this.mockBunyanLogger.level.should.have.been.calledOnce.and.calledWith( - "debug" - ); - }); - }); + 'debug' + ) + }) + }) - describe("when statusCode is not 200", function() { + describe('when statusCode is not 200', function() { beforeEach(function() { - this.Request.yields(null, { statusCode: 404 }); - this.logger.checkLogLevel(); - }); + this.Request.yields(null, { statusCode: 404 }) + this.logger.checkLogLevel() + }) - it("should only set default level", function() { + it('should only set default level', function() { this.mockBunyanLogger.level.should.have.been.calledOnce.and.calledWith( - "debug" - ); - }); - }); + 'debug' + ) + }) + }) - describe("when time value returned that is less than current time", function() { + describe('when time value returned that is less than current time', function() { beforeEach(function() { - this.Request.yields(null, { statusCode: 200 }, "1"); - this.logger.checkLogLevel(); - }); + this.Request.yields(null, { statusCode: 200 }, '1') + this.logger.checkLogLevel() + }) - it("should only set default level", function() { + it('should only set default level', function() { this.mockBunyanLogger.level.should.have.been.calledOnce.and.calledWith( - "debug" - ); - }); - }); + 'debug' + ) + }) + }) - describe("when time value returned that is less than current time", function() { - describe("when level is already set", function() { + describe('when time value returned that is less than current time', function() { + describe('when level is already set', function() { beforeEach(function() { - this.mockBunyanLogger.level.returns(10); - this.Request.yields(null, { statusCode: 200 }, this.start + 1000); - this.logger.checkLogLevel(); - }); + this.mockBunyanLogger.level.returns(10) + this.Request.yields(null, { statusCode: 200 }, this.start + 1000) + this.logger.checkLogLevel() + }) - it("should set trace level", function() { + it('should set trace level', function() { this.mockBunyanLogger.level.should.have.been.calledOnce.and.calledWith( - "trace" - ); - }); - }); + 'trace' + ) + }) + }) - describe("when level is not already set", function() { + describe('when level is not already set', function() { beforeEach(function() { - this.mockBunyanLogger.level.returns(20); - this.Request.yields(null, { statusCode: 200 }, this.start + 1000); - this.logger.checkLogLevel(); - }); + this.mockBunyanLogger.level.returns(20) + this.Request.yields(null, { statusCode: 200 }, this.start + 1000) + this.logger.checkLogLevel() + }) - it("should set trace level", function() { + it('should set trace level', function() { this.mockBunyanLogger.level.should.have.been.calledOnce.and.calledWith( - "trace" - ); - }); - }); - }); - }); + 'trace' + ) + }) + }) + }) + }) - describe("ringbuffer", function() { + describe('ringbuffer', function() { beforeEach(function() { this.logBufferMock = [ { - msg: "log 1" + msg: 'log 1' }, { - msg: "log 2" + msg: 'log 2' } - ]; - }); + ] + }) - describe("in production", function() { + describe('in production', function() { beforeEach(function() { - process.env["NODE_ENV"] = "production"; - this.logger = this.LoggingManager.initialize(this.loggerName); - this.logger.ringBuffer.records = this.logBufferMock; - this.logger.error({}, "error"); - }); + process.env['NODE_ENV'] = 'production' + this.logger = this.LoggingManager.initialize(this.loggerName) + this.logger.ringBuffer.records = this.logBufferMock + this.logger.error({}, 'error') + }) afterEach(function() { - process.env["NODE_ENV"] = undefined; - }); + process.env['NODE_ENV'] = undefined + }) - it("should include buffered logs in error log", function() { + it('should include buffered logs in error log', function() { this.mockBunyanLogger.error.lastCall.args[0].logBuffer.should.equal( this.logBufferMock - ); - }); - }); + ) + }) + }) - describe("not in production", function() { + describe('not in production', function() { beforeEach(function() { - this.logger = this.LoggingManager.initialize(this.loggerName); - this.logger.ringBuffer.records = this.logBufferMock; - this.logger.error({}, "error"); - }); + this.logger = this.LoggingManager.initialize(this.loggerName) + this.logger.ringBuffer.records = this.logBufferMock + this.logger.error({}, 'error') + }) - it("should not include buffered logs in error log", function() { + it('should not include buffered logs in error log', function() { chai.expect(this.mockBunyanLogger.error.lastCall.args[0].logBuffer).be - .undefined; - }); - }); - }); -}); + .undefined + }) + }) + }) +})