From 03fa0119800a4c364a6f92ad5aecbbc9c161be30 Mon Sep 17 00:00:00 2001 From: William Di Pasquale Date: Tue, 25 Aug 2020 13:13:36 +0100 Subject: [PATCH] fix: Integrated with @comicrelief/eslint-config (#83) In order to not introduce regression on highly complex functions, some of the rules have been disabled, with the aim of resolving disabled rules one by one in future releases. Added the `yarn lint` command. --- .eslintignore | 1 - .eslintrc | 18 +- package.json | 8 + .../DependencyInjection.class.js | 5 +- src/Model/CloudEvent.model.js | 2 +- src/Model/Model.model.js | 1 + src/Model/SQS/MarketingPreference.model.js | 57 ++++-- src/Model/Status.model.js | 2 +- src/Service/Logger.service.js | 52 +++--- src/Service/Request.service.js | 119 ++++++------ src/Service/SQS.service.js | 143 +++++++------- src/Wrapper/LambdaWrapper.js | 19 +- tests/.eslintrc | 3 + .../DependencyAware.class.js | 8 +- .../DependencyInjection.class.js | 16 +- tests/unit/Model/CloudEvent.model.js | 14 +- tests/unit/Model/Response.model.js | 14 +- .../Model/SQS/MarketingPreferences.model.js | 64 +++---- tests/unit/Model/SQS/Message.model.js | 8 +- tests/unit/Model/Status.model.js | 8 +- tests/unit/Service/Request.service.js | 108 +++++------ tests/unit/Wrapper/LambdaTermination.js | 16 +- tests/unit/Wrapper/LambdaWrapper.js | 43 ++--- yarn.lock | 176 ++++++++++++++---- 24 files changed, 498 insertions(+), 407 deletions(-) create mode 100644 tests/.eslintrc diff --git a/.eslintignore b/.eslintignore index 11f4aed2..0e75fe55 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,4 +1,3 @@ node_modules dist -tests coverage diff --git a/.eslintrc b/.eslintrc index a9ea0b31..51835a9c 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,20 +1,6 @@ { "extends": [ - "airbnb-base", - "plugin:flowtype/recommended" + "@comicrelief/eslint-config" ], - "parser": "babel-eslint", - "plugins": [ - "flowtype" - ], - "rules": { - "no-param-reassign": "off", - "react/destructuring-assignment": "off", - "flowtype/no-types-missing-file-annotation": "off", - "max-len": "off", - "import/no-cycle": "off", - "no-else-return": "off", - "class-methods-use-this": "off", - "consistent-return": "off" - } + "parser": "@babel/eslint-parser" } diff --git a/package.json b/package.json index 0aa521f6..63f98d51 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "description": "Lambda wrapper for all Comic Relief Serverless Projects", "main": "dist/index.js", "scripts": { + "lint": "yarn eslint .", "test": "nyc mocha \"tests/unit/**/*.js\"", "build": "babel src --out-dir dist --copy-files", "prepublish": "yarn snyk-protect; yarn build", @@ -14,18 +15,25 @@ "devDependencies": { "@babel/cli": "^7.0.0", "@babel/core": "^7.0.0", + "@babel/eslint-parser": "^7.11.4", "@babel/node": "^7.0.0", "@babel/plugin-syntax-flow": "^7.0.0", "@babel/plugin-transform-flow-strip-types": "^7.0.0", "@babel/preset-env": "^7.0.0", + "@comicrelief/eslint-config": "^1.0.6", "babel-eslint": "^10.1.0", "babel-plugin-istanbul": "^6.0.0", "eslint": "^7.5.0", "eslint-config-airbnb-base": "^14.2.0", + "eslint-config-prettier": "^6.11.0", "eslint-plugin-flowtype": "^5.2.0", "eslint-plugin-import": "^2.22.0", + "eslint-plugin-prettier": "^3.1.4", + "eslint-plugin-sonarjs": "^0.5.0", + "eslint-plugin-unicorn": "^21.0.0", "mocha": "^8.0.1", "nyc": "^15.1.0", + "prettier": "^2.1.0", "semantic-release": "^17.1.1", "serverless-mocha-plugin": "^1.12.0", "sinon": "^9.0.2", diff --git a/src/DependencyInjection/DependencyInjection.class.js b/src/DependencyInjection/DependencyInjection.class.js index 443a4a58..49222240 100644 --- a/src/DependencyInjection/DependencyInjection.class.js +++ b/src/DependencyInjection/DependencyInjection.class.js @@ -39,7 +39,7 @@ export default class DependencyInjection { */ get(definition) { if (typeof this.dependencies[definition] === 'undefined') { - throw Error(`${definition} does not exist in di container`); + throw new TypeError(`${definition} does not exist in di container`); } return this.dependencies[definition]; @@ -69,7 +69,8 @@ export default class DependencyInjection { getConfiguration(definition = null) { if (definition !== null && typeof this.configuration[definition] === 'undefined') { return null; - } else if (typeof this.configuration[definition] !== 'undefined') { + } + if (typeof this.configuration[definition] !== 'undefined') { return this.configuration[definition]; } diff --git a/src/Model/CloudEvent.model.js b/src/Model/CloudEvent.model.js index 5a43bfa7..90081ed4 100644 --- a/src/Model/CloudEvent.model.js +++ b/src/Model/CloudEvent.model.js @@ -16,7 +16,7 @@ export default class CloudEventModel extends Model { this.eventType = ''; this.source = ''; this.eventID = UUID(); - this.eventTime = (new Date()).toISOString(); + this.eventTime = new Date().toISOString(); this.extensions = {}; this.contentType = 'application/json'; this.data = {}; diff --git a/src/Model/Model.model.js b/src/Model/Model.model.js index 5c158d6f..14888bdf 100644 --- a/src/Model/Model.model.js +++ b/src/Model/Model.model.js @@ -1,3 +1,4 @@ +/* eslint-disable class-methods-use-this */ import validate from 'validate.js/validate'; export default class Model { diff --git a/src/Model/SQS/MarketingPreference.model.js b/src/Model/SQS/MarketingPreference.model.js index e98148ce..25be8e8d 100644 --- a/src/Model/SQS/MarketingPreference.model.js +++ b/src/Model/SQS/MarketingPreference.model.js @@ -1,3 +1,4 @@ +/* eslint-disable sonarjs/cognitive-complexity */ import validate from 'validate.js'; import Model from '../Model.model'; import requestConstraints from './MarketingPreference.constraints.json'; @@ -445,14 +446,12 @@ export default class MarketingPreference extends Model { * @returns {boolean} */ isPermissionSet() { - return (this.getPermissionEmail() !== null - && this.getPermissionEmail() !== '') - || (this.getPermissionPost() !== null - && this.getPermissionPost() !== '') - || (this.getPermissionPhone() !== null - && this.getPermissionPhone() !== '') - || (this.getPermissionSMS() !== null - && this.getPermissionSMS() !== ''); + return ( + (this.getPermissionEmail() !== null && this.getPermissionEmail() !== '') || + (this.getPermissionPost() !== null && this.getPermissionPost() !== '') || + (this.getPermissionPhone() !== null && this.getPermissionPhone() !== '') || + (this.getPermissionSMS() !== null && this.getPermissionSMS() !== '') + ); } /** @@ -462,11 +461,13 @@ export default class MarketingPreference extends Model { validate() { return new Promise((resolve, reject) => { const requestConstraintsClone = { ...requestConstraints }; - if ((this.getPermissionEmail() !== null - && this.getPermissionEmail() !== '' - && this.getPermissionEmail() !== '0' - && this.getPermissionEmail() !== 0) - || (this.getEmail())) { + if ( + (this.getPermissionEmail() !== null && + this.getPermissionEmail() !== '' && + this.getPermissionEmail() !== '0' && + this.getPermissionEmail() !== 0) || + this.getEmail() + ) { if (this.getEmail()) { requestConstraintsClone.email = { email: true }; } else { @@ -474,12 +475,30 @@ export default class MarketingPreference extends Model { } } // Update constraints if fields are not empty - requestConstraintsClone.firstname = this.getFirstName() !== null && this.getFirstName() !== '' ? { format: { pattern: "[a-zA-Z.'-_ ]+", flags: 'i', message: 'can only contain alphabetical characters' } } : ''; - requestConstraintsClone.lastname = this.getLastName() !== null && this.getLastName() !== '' ? { format: { pattern: "[a-zA-Z.'-_ ]+", flags: 'i', message: 'can only contain alphabetical characters' } } : ''; - requestConstraintsClone.phone = this.getPhone() !== null && this.getPhone() !== '' ? { format: { pattern: '[0-9 ]+', flags: 'i', message: 'can only contain numerical characters' } } : ''; - requestConstraintsClone.mobile = this.getMobile() !== null && this.getMobile() !== '' ? { format: { pattern: '[0-9 ]+', flags: 'i', message: 'can only contain numerical characters' } } : ''; - requestConstraintsClone.address1 = this.getAddress1() !== null && this.getAddress1() !== '' ? { format: { pattern: "[a-zA-Z.'-_& ]+", flags: 'i', message: 'can only contain alphanumeric characters and . \' - _ &' } } : ''; - requestConstraintsClone.country = this.getCountry() !== null && this.getCountry() !== '' ? { format: { pattern: "[a-zA-Z.'-_& ]+", flags: 'i', message: 'can only contain alphabetical characters and . \' - _ &' } } : ''; + requestConstraintsClone.firstname = + this.getFirstName() !== null && this.getFirstName() !== '' + ? { format: { pattern: "[a-zA-Z.'-_ ]+", flags: 'i', message: 'can only contain alphabetical characters' } } + : ''; + requestConstraintsClone.lastname = + this.getLastName() !== null && this.getLastName() !== '' + ? { format: { pattern: "[a-zA-Z.'-_ ]+", flags: 'i', message: 'can only contain alphabetical characters' } } + : ''; + requestConstraintsClone.phone = + this.getPhone() !== null && this.getPhone() !== '' + ? { format: { pattern: '[0-9 ]+', flags: 'i', message: 'can only contain numerical characters' } } + : ''; + requestConstraintsClone.mobile = + this.getMobile() !== null && this.getMobile() !== '' + ? { format: { pattern: '[0-9 ]+', flags: 'i', message: 'can only contain numerical characters' } } + : ''; + requestConstraintsClone.address1 = + this.getAddress1() !== null && this.getAddress1() !== '' + ? { format: { pattern: "[a-zA-Z.'-_& ]+", flags: 'i', message: "can only contain alphanumeric characters and . ' - _ &" } } + : ''; + requestConstraintsClone.country = + this.getCountry() !== null && this.getCountry() !== '' + ? { format: { pattern: "[a-zA-Z.'-_& ]+", flags: 'i', message: "can only contain alphabetical characters and . ' - _ &" } } + : ''; const validation = validate(this.getEntityMappings(), requestConstraintsClone); diff --git a/src/Model/Status.model.js b/src/Model/Status.model.js index 7529c729..7d8166e1 100644 --- a/src/Model/Status.model.js +++ b/src/Model/Status.model.js @@ -44,7 +44,7 @@ export default class StatusModel extends Model { */ setStatus(status: string) { if (typeof STATUS_TYPES[status] === 'undefined') { - throw new Error(`${StatusModel.name} - ${status} is not a valid status type`); + throw new TypeError(`${StatusModel.name} - ${status} is not a valid status type`); } this.status = status; diff --git a/src/Service/Logger.service.js b/src/Service/Logger.service.js index a78f6f35..661b9256 100644 --- a/src/Service/Logger.service.js +++ b/src/Service/Logger.service.js @@ -12,27 +12,27 @@ export const logger = Winston.createLogger({ replacer: (key, value) => { if (value instanceof Buffer) { return value.toString('base64'); - } else if (value instanceof Error) { + } + if (value instanceof Error) { const error = {}; - Object.getOwnPropertyNames(value).forEach(((objectKey) => { + Object.getOwnPropertyNames(value).forEach((objectKey) => { error[objectKey] = value[objectKey]; - })); + }); return error; } return value; }, - }), + }) ), - transports: [ - new Winston.transports.Console(), - ], + transports: [new Winston.transports.Console()], }); // Instantiate the sentry client -const sentryIsAvailable = typeof process.env.RAVEN_DSN !== 'undefined' && (typeof process.env.RAVEN_DSN === 'string' && process.env.RAVEN_DSN !== 'undefined'); +const sentryIsAvailable = + typeof process.env.RAVEN_DSN !== 'undefined' && typeof process.env.RAVEN_DSN === 'string' && process.env.RAVEN_DSN !== 'undefined'; if (sentryIsAvailable) { Sentry.init({ @@ -49,10 +49,12 @@ export default class LoggerService extends DependencyAwareClass { constructor(di: DependencyInjection) { super(di); this.sentry = null; + this.logger = logger; + const container = this.getContainer(); const event = container.getEvent(); const context = container.getContext(); - const isOffline = !Object.prototype.hasOwnProperty.call(context, 'invokedFunctionArn') || context.invokedFunctionArn.indexOf('offline') !== -1; + const isOffline = !Object.prototype.hasOwnProperty.call(context, 'invokedFunctionArn') || context.invokedFunctionArn.includes('offline'); // Set sentry client context if (sentryIsAvailable && isOffline === false) { @@ -87,11 +89,11 @@ export default class LoggerService extends DependencyAwareClass { } if ( - typeof process.env.EPSAGON_TOKEN === 'string' - && process.env.EPSAGON_TOKEN !== 'undefined' - && typeof process.env.EPSAGON_SERVICE_NAME === 'string' - && process.env.EPSAGON_SERVICE_NAME !== 'undefined' - && error instanceof Error + typeof process.env.EPSAGON_TOKEN === 'string' && + process.env.EPSAGON_TOKEN !== 'undefined' && + typeof process.env.EPSAGON_SERVICE_NAME === 'string' && + process.env.EPSAGON_SERVICE_NAME !== 'undefined' && + error instanceof Error ) { Epsagon.setError(error); } @@ -114,7 +116,7 @@ export default class LoggerService extends DependencyAwareClass { * @param message string */ info(message) { - logger.log('info', message); + this.logger.log('info', message); } /** @@ -124,16 +126,16 @@ export default class LoggerService extends DependencyAwareClass { */ label(descriptor, silent = false) { if ( - typeof process.env.EPSAGON_TOKEN === 'string' - && process.env.EPSAGON_TOKEN !== 'undefined' - && typeof process.env.EPSAGON_SERVICE_NAME === 'string' - && process.env.EPSAGON_SERVICE_NAME !== 'undefined' + typeof process.env.EPSAGON_TOKEN === 'string' && + process.env.EPSAGON_TOKEN !== 'undefined' && + typeof process.env.EPSAGON_SERVICE_NAME === 'string' && + process.env.EPSAGON_SERVICE_NAME !== 'undefined' ) { Epsagon.label(descriptor); } if (silent === false) { - logger.log('info', `label - ${descriptor}`); + this.logger.log('info', `label - ${descriptor}`); } } @@ -145,16 +147,16 @@ export default class LoggerService extends DependencyAwareClass { */ metric(descriptor, stat, silent = false) { if ( - typeof process.env.EPSAGON_TOKEN === 'string' - && process.env.EPSAGON_TOKEN !== 'undefined' - && typeof process.env.EPSAGON_SERVICE_NAME === 'string' - && process.env.EPSAGON_SERVICE_NAME !== 'undefined' + typeof process.env.EPSAGON_TOKEN === 'string' && + process.env.EPSAGON_TOKEN !== 'undefined' && + typeof process.env.EPSAGON_SERVICE_NAME === 'string' && + process.env.EPSAGON_SERVICE_NAME !== 'undefined' ) { Epsagon.label(descriptor, stat); } if (silent === false) { - logger.log('info', `metric - ${descriptor} - ${stat}`); + this.logger.log('info', `metric - ${descriptor} - ${stat}`); } } } diff --git a/src/Service/Request.service.js b/src/Service/Request.service.js index 3a76d091..47f6586c 100644 --- a/src/Service/Request.service.js +++ b/src/Service/Request.service.js @@ -1,3 +1,5 @@ +/* eslint-disable class-methods-use-this */ +/* eslint-disable sonarjs/no-duplicate-string */ /* @flow */ import QueryString from 'querystring'; import validate from 'validate.js/validate'; @@ -29,14 +31,14 @@ export default class RequestService extends DependencyAwareClass { * @param requestType * @return {*} */ - get(param: string, ifNull = null, requestType = null) { - const queryParams = this.getAll(requestType); + get(parameter: string, ifNull = null, requestType = null) { + const queryParameters = this.getAll(requestType); - if (queryParams === null) { + if (queryParameters === null) { return ifNull; } - return typeof queryParams[param] !== 'undefined' ? queryParams[param] : ifNull; + return typeof queryParameters[parameter] !== 'undefined' ? queryParameters[parameter] : ifNull; } /** @@ -66,17 +68,22 @@ export default class RequestService extends DependencyAwareClass { * @param ifNull mixed * @return {*} */ - getPathParameter(param: string = null, ifNull = {}) { + getPathParameter(parameter: string = null, ifNull = {}) { const event = this.getContainer().getEvent(); // If no parameter has been requested, return all path parameters - if (param === null && typeof event.pathParameters === 'object') { + if (parameter === null && typeof event.pathParameters === 'object') { return event.pathParameters; } // If a specifc parameter has been requested, return the parameter if it exists - if (typeof param === 'string' && typeof event.pathParameters === 'object' && event.pathParameters !== null && typeof event.pathParameters[param] !== 'undefined') { - return event.pathParameters[param]; + if ( + typeof parameter === 'string' && + typeof event.pathParameters === 'object' && + event.pathParameters !== null && + typeof event.pathParameters[parameter] !== 'undefined' + ) { + return event.pathParameters[parameter]; } return ifNull; @@ -87,6 +94,7 @@ export default class RequestService extends DependencyAwareClass { * @param requestType * @return {{}} */ + // eslint-disable-next-line sonarjs/cognitive-complexity getAll(requestType = null) { const event = this.getContainer().getEvent(); @@ -95,37 +103,45 @@ export default class RequestService extends DependencyAwareClass { } if (event.httpMethod === 'POST' || requestType === REQUEST_TYPES.POST) { - let queryParams = {}; + let queryParameters = {}; - if ((typeof event.headers['Content-Type'] !== 'undefined' && event.headers['Content-Type'].indexOf('application/x-www-form-urlencoded') !== -1) - || (typeof event.headers['content-type'] !== 'undefined' && event.headers['content-type'].indexOf('application/x-www-form-urlencoded') !== -1)) { - queryParams = QueryString.parse(event.body); + if ( + (typeof event.headers['Content-Type'] !== 'undefined' && event.headers['Content-Type'].includes('application/x-www-form-urlencoded')) || + (typeof event.headers['content-type'] !== 'undefined' && event.headers['content-type'].includes('application/x-www-form-urlencoded')) + ) { + queryParameters = QueryString.parse(event.body); } - if ((typeof event.headers['Content-Type'] !== 'undefined' && event.headers['Content-Type'].indexOf('application/json') !== -1) - || (typeof event.headers['content-type'] !== 'undefined' && event.headers['content-type'].indexOf('application/json') !== -1)) { + if ( + (typeof event.headers['Content-Type'] !== 'undefined' && event.headers['Content-Type'].includes('application/json')) || + (typeof event.headers['content-type'] !== 'undefined' && event.headers['content-type'].includes('application/json')) + ) { try { - queryParams = JSON.parse(event.body); - } catch (e) { - queryParams = {}; + queryParameters = JSON.parse(event.body); + } catch { + queryParameters = {}; } } - if ((typeof event.headers['Content-Type'] !== 'undefined' && event.headers['Content-Type'].indexOf('text/xml') !== -1) - || (typeof event.headers['content-type'] !== 'undefined' && event.headers['content-type'].indexOf('text/xml') !== -1)) { - XML2JS.parseString(event.body, ((err, result) => { - if (err) { - queryParams = {}; + if ( + (typeof event.headers['Content-Type'] !== 'undefined' && event.headers['Content-Type'].includes('text/xml')) || + (typeof event.headers['content-type'] !== 'undefined' && event.headers['content-type'].includes('text/xml')) + ) { + XML2JS.parseString(event.body, (error, result) => { + if (error) { + queryParameters = {}; } else { - queryParams = result; + queryParameters = result; } - })); + }); } - if ((typeof event.headers['Content-Type'] !== 'undefined' && event.headers['Content-Type'].indexOf('multipart/form-data') !== -1) - || (typeof event.headers['content-type'] !== 'undefined' && event.headers['content-type'].indexOf('multipart/form-data') !== -1)) { - queryParams = this.parseForm(true); + if ( + (typeof event.headers['Content-Type'] !== 'undefined' && event.headers['Content-Type'].includes('multipart/form-data')) || + (typeof event.headers['content-type'] !== 'undefined' && event.headers['content-type'].includes('multipart/form-data')) + ) { + queryParameters = this.parseForm(true); } - return typeof queryParams !== 'undefined' ? queryParams : {}; + return typeof queryParameters !== 'undefined' ? queryParameters : {}; } return null; @@ -138,9 +154,11 @@ export default class RequestService extends DependencyAwareClass { getIp() { const event = this.getContainer().getEvent(); - if (typeof event.requestContext !== 'undefined' - && typeof event.requestContext.identity !== 'undefined' - && typeof event.requestContext.identity.sourceIp !== 'undefined') { + if ( + typeof event.requestContext !== 'undefined' && + typeof event.requestContext.identity !== 'undefined' && + typeof event.requestContext.identity.sourceIp !== 'undefined' + ) { return event.requestContext.identity.sourceIp; } @@ -180,7 +198,7 @@ export default class RequestService extends DependencyAwareClass { 'operating-system': os.family, 'operating-system-version': agent.os.toVersion(), }; - } catch (error) { + } catch { this.getContainer().get(DEFINITIONS.LOGGER).label('user-agent-parsing-failed'); return null; @@ -222,21 +240,20 @@ export default class RequestService extends DependencyAwareClass { const body = event.isBase64Encoded ? Buffer.from(event.body, 'base64').toString('binary').trim() : event.body; const result = {}; - body - .split(boundary) - .forEach((item) => { - if (/filename=".+"/g.test(item)) { - result[item.match(/name=".+";/g)[0].slice(6, -2)] = { - type: 'file', - filename: item.match(/filename=".+"/g)[0].slice(10, -1), - contentType: item.match(/Content-Type:\s.+/g)[0].slice(14), - content: useBuffer ? Buffer.from(item.slice(item.search(/Content-Type:\s.+/g) + item.match(/Content-Type:\s.+/g)[0].length + 4, -4), 'binary') - : item.slice(item.search(/Content-Type:\s.+/g) + item.match(/Content-Type:\s.+/g)[0].length + 4, -4), - }; - } else if (/name=".+"/g.test(item)) { - result[item.match(/name=".+"/g)[0].slice(6, -1)] = item.slice(item.search(/name=".+"/g) + item.match(/name=".+"/g)[0].length + 4, -4); - } - }); + body.split(boundary).forEach((item) => { + if (/filename=".+"/g.test(item)) { + result[item.match(/name=".+";/g)[0].slice(6, -2)] = { + type: 'file', + filename: item.match(/filename=".+"/g)[0].slice(10, -1), + contentType: item.match(/Content-Type:\s.+/g)[0].slice(14), + content: useBuffer + ? Buffer.from(item.slice(item.search(/Content-Type:\s.+/g) + item.match(/Content-Type:\s.+/g)[0].length + 4, -4), 'binary') + : item.slice(item.search(/Content-Type:\s.+/g) + item.match(/Content-Type:\s.+/g)[0].length + 4, -4), + }; + } else if (/name=".+"/g.test(item)) { + result[item.match(/name=".+"/g)[0].slice(6, -1)] = item.slice(item.search(/name=".+"/g) + item.match(/name=".+"/g)[0].length + 4, -4); + } + }); return result; } @@ -248,18 +265,14 @@ export default class RequestService extends DependencyAwareClass { const event = this.getContainer().getEvent(); const eventRecord = event.Records && event.Records[0]; - if (typeof event.Records !== 'undefined' - && typeof event.Records[0] !== 'undefined' - && typeof eventRecord.eventSource !== 'undefined') { + if (typeof event.Records !== 'undefined' && typeof event.Records[0] !== 'undefined' && typeof eventRecord.eventSource !== 'undefined') { return eventRecord; } return null; } getValueIgnoringKeyCase(object, key) { - const foundKey = Object - .keys(object) - .find((currentKey) => currentKey.toLocaleLowerCase() === key.toLowerCase()); + const foundKey = Object.keys(object).find((currentKey) => currentKey.toLocaleLowerCase() === key.toLowerCase()); return object[foundKey]; } diff --git a/src/Service/SQS.service.js b/src/Service/SQS.service.js index 832d7e57..c879bc7f 100644 --- a/src/Service/SQS.service.js +++ b/src/Service/SQS.service.js @@ -33,7 +33,7 @@ export default class SQSService extends DependencyAwareClass { super(di); const context = this.getContainer().getContext(); const queues = this.getContainer().getConfiguration('QUEUES'); - const isOffline = !Object.prototype.hasOwnProperty.call(context, 'invokedFunctionArn') || context.invokedFunctionArn.indexOf('offline') !== -1; + const isOffline = !Object.prototype.hasOwnProperty.call(context, 'invokedFunctionArn') || context.invokedFunctionArn.includes('offline'); this.queues = {}; @@ -41,8 +41,8 @@ export default class SQSService extends DependencyAwareClass { if (queues !== null && Object.keys(queues).length >= 1) { Object.keys(queues).forEach((queueDefinition) => { if (isOffline === true) { - const offlineHost = typeof process.env.LAMBDA_WRAPPER_OFFLINE_SQS_HOST !== 'undefined' - ? process.env.LAMBDA_WRAPPER_OFFLINE_SQS_HOST : 'localhost'; + const offlineHost = + typeof process.env.LAMBDA_WRAPPER_OFFLINE_SQS_HOST !== 'undefined' ? process.env.LAMBDA_WRAPPER_OFFLINE_SQS_HOST : 'localhost'; this.queues[queueDefinition] = `http://${offlineHost}:4576/queue/${queues[queueDefinition]}`; } else { @@ -69,33 +69,40 @@ export default class SQSService extends DependencyAwareClass { Timer.start(timerId); // assuming openFiles is an array of file names - each(messageModels, ((messageModel, callback) => { - if (messageModel instanceof SQSMessageModel && messageModel.isForDeletion() === true) { - messagesForDeletion.push({ - Id: messageModel.getMessageId(), - ReceiptHandle: messageModel.getReceiptHandle(), - }); - } - callback(); - }), ((loopErr) => { - if (loopErr) { - Logger.error(loopErr); - resolve(); - } - - sqs.deleteMessageBatch({ - Entries: messagesForDeletion, - QueueUrl: queueUrl, - }, ((err) => { - Timer.stop(timerId); - - if (err) { - Logger.error(err); + each( + messageModels, + (messageModel, callback) => { + if (messageModel instanceof SQSMessageModel && messageModel.isForDeletion() === true) { + messagesForDeletion.push({ + Id: messageModel.getMessageId(), + ReceiptHandle: messageModel.getReceiptHandle(), + }); + } + callback(); + }, + (loopError) => { + if (loopError) { + Logger.error(loopError); + resolve(); } - resolve(); - })); - })); + sqs.deleteMessageBatch( + { + Entries: messagesForDeletion, + QueueUrl: queueUrl, + }, + (error) => { + Timer.stop(timerId); + + if (error) { + Logger.error(error); + } + + resolve(); + } + ); + } + ); }); } @@ -111,13 +118,13 @@ export default class SQSService extends DependencyAwareClass { return new Promise((resolve) => { Timer.start(timerId); - sqs.listQueues({}, ((err, data) => { + sqs.listQueues({}, (error, data) => { Timer.stop(timerId); const statusModel = new StatusModel('SQS', STATUS_TYPES.OK); - if (err) { - Logger.error(err); + if (error) { + Logger.error(error); statusModel.setStatus(STATUS_TYPES.APPLICATION_FAILURE); } @@ -126,7 +133,7 @@ export default class SQSService extends DependencyAwareClass { } resolve(statusModel); - })); + }); }); } @@ -144,19 +151,22 @@ export default class SQSService extends DependencyAwareClass { return new Promise((resolve) => { Timer.start(timerId); - sqs.getQueueAttributes({ - AttributeNames: ['ApproximateNumberOfMessages'], - QueueUrl: queueUrl, - }, ((err, data) => { - Timer.stop(timerId); + sqs.getQueueAttributes( + { + AttributeNames: ['ApproximateNumberOfMessages'], + QueueUrl: queueUrl, + }, + (error, data) => { + Timer.stop(timerId); - if (err) { - Logger.error(err); - resolve(0); - } + if (error) { + Logger.error(error); + resolve(0); + } - resolve(parseInt(data.Attributes.ApproximateNumberOfMessages, 10)); - })); + resolve(Number.parseInt(data.Attributes.ApproximateNumberOfMessages, 10)); + } + ); }); } @@ -176,27 +186,27 @@ export default class SQSService extends DependencyAwareClass { return new Promise((resolve) => { Timer.start(timerId); - const messageParams = { + const messageParameters = { MessageBody: JSON.stringify(messageObject), QueueUrl: queueUrl, }; if (queueUrl.includes('.fifo') === true) { - messageParams.MessageDeduplicationId = UUID(); - messageParams.MessageGroupId = messageGroupId !== null ? messageGroupId : UUID(); + messageParameters.MessageDeduplicationId = UUID(); + messageParameters.MessageGroupId = messageGroupId !== null ? messageGroupId : UUID(); } - sqs.sendMessage(messageParams, ((err) => { + sqs.sendMessage(messageParameters, (error) => { Timer.stop(timerId); - if (err) { - Logger.error(err); + if (error) { + Logger.error(error); } resolve({ queue, }); - })); + }); }); } @@ -215,24 +225,27 @@ export default class SQSService extends DependencyAwareClass { return new Promise((resolve, reject) => { Timer.start(timerId); - sqs.receiveMessage({ - QueueUrl: queueUrl, - VisibilityTimeout: timeout, - MaxNumberOfMessages: 10, - }, ((err, data) => { - Timer.stop(timerId); + sqs.receiveMessage( + { + QueueUrl: queueUrl, + VisibilityTimeout: timeout, + MaxNumberOfMessages: 10, + }, + (error, data) => { + Timer.stop(timerId); - if (err) { - Logger.error(err); - return reject(err); - } + if (error) { + Logger.error(error); + return reject(error); + } - if (typeof data.Messages === 'undefined') { - return resolve([]); - } + if (typeof data.Messages === 'undefined') { + return resolve([]); + } - return resolve(data.Messages.map((message) => new SQSMessageModel(message))); - })); + return resolve(data.Messages.map((message) => new SQSMessageModel(message))); + } + ); }); } } diff --git a/src/Wrapper/LambdaWrapper.js b/src/Wrapper/LambdaWrapper.js index 3d6dbe83..6c08c1c8 100644 --- a/src/Wrapper/LambdaWrapper.js +++ b/src/Wrapper/LambdaWrapper.js @@ -1,10 +1,11 @@ +/* eslint-disable sonarjs/cognitive-complexity */ import Epsagon from 'epsagon'; import DependencyInjection from '../DependencyInjection/DependencyInjection.class'; import { DEFINITIONS } from '../Config/Dependencies'; import ResponseModel from '../Model/Response.model'; -export default ((configuration, handler) => { +export default (configuration, handler) => { let instance = (event, context, callback) => { const di = new DependencyInjection(configuration, event, context); const request = di.get(DEFINITIONS.REQUEST); @@ -19,11 +20,7 @@ export default ((configuration, handler) => { details: error.details || 'unknown error', }; - const response = new ResponseModel( - responseDetails.body, - responseDetails.code, - responseDetails.details, - ); + const response = new ResponseModel(responseDetails.body, responseDetails.code, responseDetails.details); return response.generate(); }; @@ -63,10 +60,10 @@ export default ((configuration, handler) => { // If the Epsagon token is enabled, then wrap the instance in the Epsagon wrapper if ( - typeof process.env.EPSAGON_TOKEN === 'string' - && process.env.EPSAGON_TOKEN !== 'undefined' - && typeof process.env.EPSAGON_SERVICE_NAME === 'string' - && process.env.EPSAGON_SERVICE_NAME !== 'undefined' + typeof process.env.EPSAGON_TOKEN === 'string' && + process.env.EPSAGON_TOKEN !== 'undefined' && + typeof process.env.EPSAGON_SERVICE_NAME === 'string' && + process.env.EPSAGON_SERVICE_NAME !== 'undefined' ) { Epsagon.init({ token: process.env.EPSAGON_TOKEN, @@ -77,4 +74,4 @@ export default ((configuration, handler) => { } return instance; -}); +}; diff --git a/tests/.eslintrc b/tests/.eslintrc new file mode 100644 index 00000000..062b0808 --- /dev/null +++ b/tests/.eslintrc @@ -0,0 +1,3 @@ +{ + "extends": "@comicrelief/eslint-config/tests" +} diff --git a/tests/unit/DependencyInjection/DependencyAware.class.js b/tests/unit/DependencyInjection/DependencyAware.class.js index c10422ca..e257b4a7 100644 --- a/tests/unit/DependencyInjection/DependencyAware.class.js +++ b/tests/unit/DependencyInjection/DependencyAware.class.js @@ -2,18 +2,16 @@ import ServerlessMochaPlugin from 'serverless-mocha-plugin'; import DependencyInjection from '../../../src/DependencyInjection/DependencyInjection.class'; import DependencyAware from '../../../src/DependencyInjection/DependencyAware.class'; -let getEvent = require('../../mocks/aws/event.json'); -let getContext = require('../../mocks/aws/context.json'); +const getEvent = require('../../mocks/aws/event.json'); +const getContext = require('../../mocks/aws/context.json'); -const expect = ServerlessMochaPlugin.chai.expect; +const { expect } = ServerlessMochaPlugin.chai; describe('DependencyInjection/DependencyAwareClass', () => { - const dependencyInjectionClass = new DependencyInjection({}, getEvent, getContext); const dependencyAwareClass = new DependencyAware(dependencyInjectionClass); it('should instantiate and be able to get the dependency injection container', () => { expect(dependencyAwareClass.getContainer()).to.eql(dependencyInjectionClass); }); - }); diff --git a/tests/unit/DependencyInjection/DependencyInjection.class.js b/tests/unit/DependencyInjection/DependencyInjection.class.js index 04ff523a..a89ca66e 100644 --- a/tests/unit/DependencyInjection/DependencyInjection.class.js +++ b/tests/unit/DependencyInjection/DependencyInjection.class.js @@ -2,18 +2,16 @@ import ServerlessMochaPlugin from 'serverless-mocha-plugin'; import DependencyInjection from '../../../src/DependencyInjection/DependencyInjection.class'; import { DEFINITIONS } from '../../../src/Config/Dependencies'; -import RequestService from "../../../src/Service/Request.service"; -import LoggerService from "../../../src/Service/Logger.service"; +import RequestService from '../../../src/Service/Request.service'; +import LoggerService from '../../../src/Service/Logger.service'; -const expect = ServerlessMochaPlugin.chai.expect; +const { expect } = ServerlessMochaPlugin.chai; -let getEvent = require('../../mocks/aws/event.json'); -let getContext = require('../../mocks/aws/context.json'); +const getEvent = require('../../mocks/aws/event.json'); +const getContext = require('../../mocks/aws/context.json'); describe('DependencyInjection/DependencyInjectionClass', () => { - describe('should instantiate', () => { - const configuration = { test: 123, }; @@ -30,11 +28,9 @@ describe('DependencyInjection/DependencyInjectionClass', () => { it('should output the configuration that was provided to it', () => { expect(dependencyInjection.getConfiguration()).to.eql(configuration); }); - }); describe('should get dependencies', () => { - const dependencyInjection = new DependencyInjection({}, getEvent, getContext); it('Should throw validation errors when an non existent model is requested', () => { @@ -50,7 +46,5 @@ describe('DependencyInjection/DependencyInjectionClass', () => { expect(requestService instanceof RequestService).to.be.true; expect(requestService.di instanceof DependencyInjection).to.be.true; }); - }); - }); diff --git a/tests/unit/Model/CloudEvent.model.js b/tests/unit/Model/CloudEvent.model.js index 3838d15e..e4f9cefe 100644 --- a/tests/unit/Model/CloudEvent.model.js +++ b/tests/unit/Model/CloudEvent.model.js @@ -1,13 +1,11 @@ import ServerlessMochaPlugin from 'serverless-mocha-plugin'; -import CloudEventModel from "../../../src/Model/CloudEvent.model"; +import CloudEventModel from '../../../src/Model/CloudEvent.model'; -const expect = ServerlessMochaPlugin.chai.expect; +const { expect } = ServerlessMochaPlugin.chai; // Test definitions. describe('Model/CloudEventModel', () => { - describe('Ensure setting and getting of variables', () => { - const model = new CloudEventModel(); it('should get the cloud event version', () => { @@ -33,14 +31,14 @@ describe('Model/CloudEventModel', () => { }); it('should generate the current timestamp as the current time', () => { - expect(new CloudEventModel().getEventTime().replace(/:[^:]+$/, '')).to.eql((new Date()).toISOString().replace(/:[^:]+$/, '')); + expect(new CloudEventModel().getEventTime().replace(/:[^:]+$/, '')).to.eql(new Date().toISOString().replace(/:[^:]+$/, '')); }); it('should set and get the extensions', () => { expect(model.getExtensions()).to.eql({}); const extensions = { - test: "test", + test: 'test', }; model.setExtensions(extensions); expect(model.getExtensions()).to.eql(extensions); @@ -54,12 +52,10 @@ describe('Model/CloudEventModel', () => { expect(model.getData()).to.eql({}); const data = { - test: "test", + test: 'test', }; model.setData(data); expect(model.getData()).to.eql(data); }); - }); - }); diff --git a/tests/unit/Model/Response.model.js b/tests/unit/Model/Response.model.js index 62295c9f..56122d83 100644 --- a/tests/unit/Model/Response.model.js +++ b/tests/unit/Model/Response.model.js @@ -1,24 +1,22 @@ import ServerlessMochaPlugin from 'serverless-mocha-plugin'; import ResponseModel, { DEFAULT_MESSAGE, RESPONSE_HEADERS } from '../../../src/Model/Response.model'; -const expect = ServerlessMochaPlugin.chai.expect; +const { expect } = ServerlessMochaPlugin.chai; describe('Model/ResponseModel', () => { - it('should return the expected headers', () => { const response = new ResponseModel({}, 500); expect(response.generate().headers).to.eql(RESPONSE_HEADERS); }); describe('ensure body set correctly', () => { - it('should set the body data from the constructor', () => { const response = new ResponseModel({ test: 123 }, 500); const responseBody = response.generate(); expect(typeof responseBody.body).to.eql('string'); - expect(responseBody.body.indexOf("123")).to.not.eql(-1); + expect(responseBody.body.indexOf('123')).to.not.eql(-1); expect(JSON.parse(responseBody.body).data.test).to.eql(123); }); @@ -29,14 +27,12 @@ describe('Model/ResponseModel', () => { const responseBody = response.generate(); expect(typeof responseBody.body).to.eql('string'); - expect(responseBody.body.indexOf("234")).to.not.eql(-1); + expect(responseBody.body.indexOf('234')).to.not.eql(-1); expect(JSON.parse(responseBody.body).data.test).to.eql(234); }); - }); describe('ensure status codes are set correctly', () => { - it('should return the 200 status code that is supplied to it', () => { const response = new ResponseModel({}, 200); expect(response.generate().statusCode).to.eql(200); @@ -53,11 +49,9 @@ describe('Model/ResponseModel', () => { expect(response.generate().statusCode).to.eql(300); }); - }); describe('ensure messages are set correctly', () => { - it('should return a message field when a message is supplied to it', () => { const message = 'test 123'; const response = new ResponseModel({}, 500, message); @@ -81,7 +75,5 @@ describe('Model/ResponseModel', () => { expect(JSON.parse(response.generate().body).message).to.eql('test'); }); - }); - }); diff --git a/tests/unit/Model/SQS/MarketingPreferences.model.js b/tests/unit/Model/SQS/MarketingPreferences.model.js index 1d78a761..2ca0289f 100644 --- a/tests/unit/Model/SQS/MarketingPreferences.model.js +++ b/tests/unit/Model/SQS/MarketingPreferences.model.js @@ -1,12 +1,13 @@ +/* eslint-disable sonarjs/no-identical-functions */ +/* eslint-disable sonarjs/no-duplicate-string */ import ServerlessMochaPlugin from 'serverless-mocha-plugin'; import MarketingPreferencesModel from '../../../../src/Model/SQS/MarketingPreference.model'; import ResponseModel from '../../../../src/Model/Response.model'; -const expect = ServerlessMochaPlugin.chai.expect; +const { expect } = ServerlessMochaPlugin.chai; // Test definitions. describe('Model/MarketingPreferencesModel', () => { - describe('Ensure setting and getting of variables', () => { const mockedData = { firstname: 'Tim', @@ -14,7 +15,7 @@ describe('Model/MarketingPreferencesModel', () => { phone: '0208 254 3062', mobile: '07917 321 492', address1: '32-36', - address2: 'St. Smith\'s Avenue', + address2: "St. Smith's Avenue", address3: '', town: 'London', postcode: 'sw184bx', @@ -119,7 +120,8 @@ describe('Model/MarketingPreferencesModel', () => { }); it('should validate the model', (done) => { - model.validate() + model + .validate() .then(() => { expect(true).to.eql(true); done(); @@ -137,7 +139,8 @@ describe('Model/MarketingPreferencesModel', () => { const model = new MarketingPreferencesModel(mockedData); it('should validate the model and return an error response', (done) => { - model.validate() + model + .validate() .then(() => { expect(true).to.eql(false); done(); @@ -158,7 +161,7 @@ describe('Model/MarketingPreferencesModel', () => { lastname: 'Jones', mobile: '07917 321 492', address1: '32-36', - address2: 'St. Smith\'s Avenue', + address2: "St. Smith's Avenue", address3: '', town: 'London', postcode: 'sw184bx', @@ -178,17 +181,18 @@ describe('Model/MarketingPreferencesModel', () => { const model = new MarketingPreferencesModel(mockedData); it('should validate the model and return an error response', (done) => { - model.validate() + model + .validate() .then(() => { expect(true).to.eql(false); done(); }) .catch((error) => { - console.log('Error: ', error); + console.log('Error:', error); expect(error instanceof ResponseModel).to.eql(true); expect(error.getCode()).to.eql(400); - expect(error.body.validation_errors.email[0]).to.eql('Email can\'t be blank'); + expect(error.body.validation_errors.email[0]).to.eql("Email can't be blank"); expect(true).to.eql(true); done(); }); @@ -202,7 +206,7 @@ describe('Model/MarketingPreferencesModel', () => { phone: '0208 254 3062', mobile: '07917 321 492', address1: '32-36', - address2: 'St. Smith\'s Avenue', + address2: "St. Smith's Avenue", address3: '', town: 'London', postcode: 'sw184bx', @@ -222,7 +226,8 @@ describe('Model/MarketingPreferencesModel', () => { const model = new MarketingPreferencesModel(mockedData); it('should validate the model and return an error response', (done) => { - model.validate() + model + .validate() .then(() => { expect(true).to.eql(false); done(); @@ -265,20 +270,20 @@ describe('Model/MarketingPreferencesModel', () => { const model = new MarketingPreferencesModel(mockedData); it('should validate the model', (done) => { - model.validate() + model + .validate() .then(() => { expect(true).to.eql(true); done(); }) .catch((error) => { - console.log('Error: ', error); + console.log('Error:', error); expect(true).to.eql(false); done(); }); }); }); - describe('Ensure validation passes when email permission is NO', () => { const mockedData = { firstname: 'Kelvin', @@ -307,13 +312,14 @@ describe('Model/MarketingPreferencesModel', () => { const model = new MarketingPreferencesModel(mockedData); it('should validate the model', (done) => { - model.validate() + model + .validate() .then(() => { expect(true).to.eql(true); done(); }) .catch((error) => { - console.log('Error: ', error); + console.log('Error:', error); expect(true).to.eql(false); done(); }); @@ -321,14 +327,13 @@ describe('Model/MarketingPreferencesModel', () => { }); describe('Ensure generating of timestamp when not set', () => { - const mockedData = { firstname: 'Tim', lastname: 'Jones', phone: '0208 254 3062', mobile: '07917 321 492', address1: '32-36', - address2: 'St. Smith\'s Avenue', + address2: "St. Smith's Avenue", address3: '', town: 'London', postcode: 'sw184bx', @@ -352,7 +357,8 @@ describe('Model/MarketingPreferencesModel', () => { }); it('should validate the model', (done) => { - model.validate() + model + .validate() .then(() => { expect(true).to.eql(true); done(); @@ -364,14 +370,13 @@ describe('Model/MarketingPreferencesModel', () => { }); }); - describe('Ensure validation passes when nullable fields are not present', () => { const mockedData = { firstname: 'Tim', lastname: 'Jones', phone: '0208 254 3062', mobile: '07917 321 492', - address1: '32 Smith\'s Avenue', + address1: "32 Smith's Avenue", town: 'London', postcode: 'sw184bx', country: 'United Kindgom', @@ -385,27 +390,27 @@ describe('Model/MarketingPreferencesModel', () => { const model = new MarketingPreferencesModel(mockedData); it('should validate the model', (done) => { - model.validate() + model + .validate() .then(() => { expect(true).to.eql(true); done(); }) .catch((error) => { - console.log('Error: ', error); + console.log('Error:', error); expect(true).to.eql(false); done(); }); }); }); - describe('Ensure model permission evaluates to false when no permission is set', () => { const mockedData = { firstname: 'Tim', lastname: 'Jones', phone: '0208 254 3062', mobile: '07917 321 492', - address1: '32 Smith\'s Avenue', + address1: "32 Smith's Avenue", town: 'London', postcode: 'sw184bx', country: 'United Kindgom', @@ -423,20 +428,18 @@ describe('Model/MarketingPreferencesModel', () => { const model = new MarketingPreferencesModel(mockedData); it('should evaluate model permissions to false', (done) => { - expect(model.isPermissionSet()).to.eql(false); done(); }); }); - describe('Ensure model permission evaluates to true when at least one permission is set', () => { const mockedData = { firstname: 'Tim', lastname: 'Jones', phone: '0208 254 3062', mobile: '07917 321 492', - address1: '32 Smith\'s Avenue', + address1: "32 Smith's Avenue", town: 'London', postcode: 'sw184bx', country: 'United Kindgom', @@ -455,13 +458,8 @@ describe('Model/MarketingPreferencesModel', () => { const model = new MarketingPreferencesModel(mockedData); it('should evaluate model permissions to true', (done) => { - expect(model.isPermissionSet()).to.eql(true); done(); }); }); - - - - }); diff --git a/tests/unit/Model/SQS/Message.model.js b/tests/unit/Model/SQS/Message.model.js index c75dd996..60656812 100644 --- a/tests/unit/Model/SQS/Message.model.js +++ b/tests/unit/Model/SQS/Message.model.js @@ -1,13 +1,11 @@ import ServerlessMochaPlugin from 'serverless-mocha-plugin'; -import Message from "../../../../src/Model/SQS/Message.model"; +import Message from '../../../../src/Model/SQS/Message.model'; -const expect = ServerlessMochaPlugin.chai.expect; +const { expect } = ServerlessMochaPlugin.chai; // Test definitions. describe('Model/SQS/Message.model', () => { - describe('Ensure setting and getting of variables', () => { - const messageData = { test: 123, }; @@ -47,7 +45,5 @@ describe('Model/SQS/Message.model', () => { test: 123, }); }); - }); - }); diff --git a/tests/unit/Model/Status.model.js b/tests/unit/Model/Status.model.js index 42c52b08..ae739fd7 100644 --- a/tests/unit/Model/Status.model.js +++ b/tests/unit/Model/Status.model.js @@ -1,13 +1,11 @@ import ServerlessMochaPlugin from 'serverless-mocha-plugin'; -import StatusModel, { STATUS_TYPES } from "../../../src/Model/Status.model"; +import StatusModel, { STATUS_TYPES } from '../../../src/Model/Status.model'; -const expect = ServerlessMochaPlugin.chai.expect; +const { expect } = ServerlessMochaPlugin.chai; // Test definitions. describe('Model/StatusModel', () => { - describe('Ensure setting and getting of variables', () => { - const service = 'test'; const status = STATUS_TYPES.OK; const statusModel = new StatusModel(service, status); @@ -23,7 +21,5 @@ describe('Model/StatusModel', () => { it('should throw an error when trying to set an invalid status', () => { expect(() => statusModel.setStatus('invalid')).to.throw('StatusModel - invalid is not a valid status type'); }); - }); - }); diff --git a/tests/unit/Service/Request.service.js b/tests/unit/Service/Request.service.js index 336a869a..8c3ebce4 100644 --- a/tests/unit/Service/Request.service.js +++ b/tests/unit/Service/Request.service.js @@ -1,39 +1,38 @@ +/* eslint-disable sonarjs/no-duplicate-string */ import ServerlessMochaPlugin from 'serverless-mocha-plugin'; import QueryString from 'querystring'; import DependencyInjection from '../../../src/DependencyInjection/DependencyInjection.class'; import RequestService, { REQUEST_TYPES } from '../../../src/Service/Request.service'; -import CONFIGURATION from "../../../src/Config/Dependencies"; +import CONFIGURATION from '../../../src/Config/Dependencies'; -const expect = ServerlessMochaPlugin.chai.expect; +const { expect } = ServerlessMochaPlugin.chai; -let getEvent = require('../../mocks/aws/event.json'); -let getContext = require('../../mocks/aws/context.json'); +const getEvent = require('../../mocks/aws/event.json'); +const getContext = require('../../mocks/aws/context.json'); describe('Service/RequestService', () => { - describe('test GET request getter', () => { - - let testEvent = Object.assign({}, getEvent); + const testEvent = { ...getEvent }; testEvent.queryStringParameters.test = 123; it('should fetch a GET parameter from an AWS event', () => { - let request = new RequestService(new DependencyInjection(CONFIGURATION, testEvent, getContext)); + const request = new RequestService(new DependencyInjection(CONFIGURATION, testEvent, getContext)); expect(request.get('test')).to.eql(getEvent.queryStringParameters.test); }); it('should fetch a GET parameter from an AWS event when the request type is set', () => { - let request = new RequestService(new DependencyInjection(CONFIGURATION, testEvent, getContext)); + const request = new RequestService(new DependencyInjection(CONFIGURATION, testEvent, getContext)); expect(request.get('test'), null, REQUEST_TYPES.GET).to.eql(getEvent.queryStringParameters.test); }); it('should return null from a non existent GET parameter from an AWS event', () => { - let request = new RequestService(new DependencyInjection(CONFIGURATION, testEvent, getContext)); + const request = new RequestService(new DependencyInjection(CONFIGURATION, testEvent, getContext)); expect(request.get('fake')).to.eql(null); }); it('should return null from a non existent GET parameter from an AWS event when the request type is set', () => { - let request = new RequestService(new DependencyInjection(CONFIGURATION, testEvent, getContext)); + const request = new RequestService(new DependencyInjection(CONFIGURATION, testEvent, getContext)); expect(request.get('fake', null, REQUEST_TYPES.GET)).to.eql(null); }); @@ -43,7 +42,7 @@ describe('Service/RequestService', () => { ...testEvent, headers: undefined, }; - let request = new RequestService(new DependencyInjection(CONFIGURATION, event, getContext)); + const request = new RequestService(new DependencyInjection(CONFIGURATION, event, getContext)); expect(request.getUserBrowserAndDevice()).to.eql(null); }); @@ -53,101 +52,93 @@ describe('Service/RequestService', () => { ...testEvent, headers: null, }; - let request = new RequestService(new DependencyInjection(CONFIGURATION, event, getContext)); + const request = new RequestService(new DependencyInjection(CONFIGURATION, event, getContext)); expect(request.getUserBrowserAndDevice()).to.eql(null); }); it('should return a prettified user agent', () => { - let request = new RequestService(new DependencyInjection(CONFIGURATION, testEvent, getContext)); + const request = new RequestService(new DependencyInjection(CONFIGURATION, testEvent, getContext)); expect(request.getUserBrowserAndDevice()).to.eql({ 'browser-type': 'Safari', 'browser-version': '9.1.1', 'device-type': 'Other', 'operating-system': 'Mac OS X', - 'operating-system-version': '10.11.5' + 'operating-system-version': '10.11.5', }); }); - }) - + }); }); describe('test POST request getter', () => { - - let testEvent = Object.assign({}, getEvent); + const testEvent = { ...getEvent }; testEvent.httpMethod = 'POST'; testEvent.headers['Content-Type'] = 'application/x-www-form-urlencoded'; testEvent.body = 'grant_type=client_credentials&response_type=token&token_format=opaque'; - let queryParams = QueryString.parse(testEvent.body); + const queryParameters = QueryString.parse(testEvent.body); it('should fetch a POST parameter from an AWS event', () => { - let request = new RequestService(new DependencyInjection(CONFIGURATION, testEvent, getContext)); - expect(request.get('grant_type')).to.eql(queryParams['grant_type']); + const request = new RequestService(new DependencyInjection(CONFIGURATION, testEvent, getContext)); + expect(request.get('grant_type')).to.eql(queryParameters.grant_type); }); it('should fetch a POST parameter from an AWS event when the request type is set', () => { - let request = new RequestService(new DependencyInjection(CONFIGURATION, testEvent, getContext)); - expect(request.get('grant_type'), null, REQUEST_TYPES.POST).to.eql(queryParams['grant_type']); + const request = new RequestService(new DependencyInjection(CONFIGURATION, testEvent, getContext)); + expect(request.get('grant_type'), null, REQUEST_TYPES.POST).to.eql(queryParameters.grant_type); }); it('should return null from a non existent POST parameter from an AWS event', () => { - let request = new RequestService(new DependencyInjection(CONFIGURATION, testEvent, getContext)); + const request = new RequestService(new DependencyInjection(CONFIGURATION, testEvent, getContext)); expect(request.get('fake')).to.eql(null); }); it('should return null from a non existent POST parameter from an AWS event when the request type is set', () => { - let request = new RequestService(new DependencyInjection(CONFIGURATION, testEvent, getContext), getEvent); + const request = new RequestService(new DependencyInjection(CONFIGURATION, testEvent, getContext), getEvent); expect(request.get('fake', null, REQUEST_TYPES.POST)).to.eql(null); }); - }); describe('test GET request get all getter', () => { - - let testEvent = Object.assign({}, getEvent); + const testEvent = { ...getEvent }; testEvent.queryStringParameters.test = 123; testEvent.queryStringParameters.testTwo = 123; it('should return all get parameters as an array', () => { - let request = new RequestService(new DependencyInjection(CONFIGURATION, testEvent, getContext)); + const request = new RequestService(new DependencyInjection(CONFIGURATION, testEvent, getContext)); expect(request.getAll()).to.eql(testEvent.queryStringParameters); }); - }); describe('test POST request get all getter', () => { - - let testEvent = Object.assign({}, getEvent); + const testEvent = { ...getEvent }; testEvent.httpMethod = 'POST'; testEvent.headers['Content-Type'] = 'application/x-www-form-urlencoded'; testEvent.body = 'grant_type=client_credentials&response_type=token&token_format=opaque'; - let queryParams = QueryString.parse(testEvent.body); + const queryParameters = QueryString.parse(testEvent.body); it('should return all post parameters as an array', () => { - let request = new RequestService(new DependencyInjection(CONFIGURATION, testEvent, getContext)); - expect(request.getAll()).to.eql(queryParams); + const request = new RequestService(new DependencyInjection(CONFIGURATION, testEvent, getContext)); + expect(request.getAll()).to.eql(queryParameters); }); - }); describe('test request validation', () => { - - let testEvent = Object.assign({}, getEvent); - let constraints = { - "giftaid": { - "numericality": true, - } + const testEvent = { ...getEvent }; + const constraints = { + giftaid: { + numericality: true, + }, }; describe('test validation against GET request', () => { - it('should resolve if there are no validation errors', (done) => { testEvent.queryStringParameters.giftaid = 123; - let request = new RequestService(new DependencyInjection(CONFIGURATION, testEvent, getContext)); + const request = new RequestService(new DependencyInjection(CONFIGURATION, testEvent, getContext)); - request.validateAgainstConstraints(constraints) + request + .validateAgainstConstraints(constraints) .then(() => { expect(true).to.eql(true); done(); @@ -156,14 +147,14 @@ describe('Service/RequestService', () => { expect(true).to.eql(false); done(); }); - }); it('should return a response containing validation errors if the data provided is incorrect', (done) => { testEvent.queryStringParameters.giftaid = 'abc'; - let request = new RequestService(new DependencyInjection(CONFIGURATION, testEvent, getContext)); + const request = new RequestService(new DependencyInjection(CONFIGURATION, testEvent, getContext)); - request.validateAgainstConstraints(constraints) + request + .validateAgainstConstraints(constraints) .then(() => { expect(true).to.eql(false); done(); @@ -173,20 +164,18 @@ describe('Service/RequestService', () => { done(); }); }); - }); describe('test validation against POST request', () => { - testEvent.httpMethod = 'POST'; testEvent.headers['Content-Type'] = 'application/x-www-form-urlencoded'; it('should resolve if there are no validation errors', (done) => { - testEvent.body = 'giftaid=123'; - let request = new RequestService(new DependencyInjection(CONFIGURATION, testEvent, getContext)); + const request = new RequestService(new DependencyInjection(CONFIGURATION, testEvent, getContext)); - request.validateAgainstConstraints(constraints) + request + .validateAgainstConstraints(constraints) .then(() => { expect(true).to.eql(true); done(); @@ -195,15 +184,14 @@ describe('Service/RequestService', () => { expect(true).to.eql(false); done(); }); - }); it('should return a response containing validation errors if the data provided is incorrect', (done) => { - testEvent.body = 'giftaid=abc'; - let request = new RequestService(new DependencyInjection(CONFIGURATION, testEvent, getContext)); + const request = new RequestService(new DependencyInjection(CONFIGURATION, testEvent, getContext)); - request.validateAgainstConstraints(constraints) + request + .validateAgainstConstraints(constraints) .then(() => { expect(true).to.eql(false); done(); @@ -212,11 +200,7 @@ describe('Service/RequestService', () => { expect(true).to.eql(true); done(); }); - }); - }); - }); - }); diff --git a/tests/unit/Wrapper/LambdaTermination.js b/tests/unit/Wrapper/LambdaTermination.js index f50bab28..a7e272b2 100644 --- a/tests/unit/Wrapper/LambdaTermination.js +++ b/tests/unit/Wrapper/LambdaTermination.js @@ -2,25 +2,23 @@ import ServerlessMochaPlugin from 'serverless-mocha-plugin'; import LambdaTermination from '../../../src/Wrapper/LambdaTermination'; - -const expect = ServerlessMochaPlugin.chai.expect; +const { expect } = ServerlessMochaPlugin.chai; describe('Wrapper/LambdaTermination', () => { describe('Stores the custom fields', () => { - const props = { + const properties = { internal: 'INTERNAL', code: 401, body: 'BODY', }; - const lt = new LambdaTermination(props.internal, props.code, props.body); + const lt = new LambdaTermination(properties.internal, properties.code, properties.body); - Object.entries(props) - .forEach(([key, value]) => { - it(`Exposes '${key}'`, () => { - expect(lt[key]).to.be.equal(value); - }); + Object.entries(properties).forEach(([key, value]) => { + it(`Exposes '${key}'`, () => { + expect(lt[key]).to.be.equal(value); }); + }); }); it('Generates an error', () => { diff --git a/tests/unit/Wrapper/LambdaWrapper.js b/tests/unit/Wrapper/LambdaWrapper.js index 3f13047c..38366a22 100644 --- a/tests/unit/Wrapper/LambdaWrapper.js +++ b/tests/unit/Wrapper/LambdaWrapper.js @@ -2,29 +2,26 @@ import ServerlessMochaPlugin from 'serverless-mocha-plugin'; import sinon from 'sinon'; import { DEFINITIONS } from '../../../src/Config/Dependencies'; -import RequestService, {REQUEST_TYPES} from '../../../src/Service/Request.service'; -import DependencyInjection from "../../../src/DependencyInjection/DependencyInjection.class"; -import LambdaWrapper from "../../../src/Wrapper/LambdaWrapper"; +import RequestService, { REQUEST_TYPES } from '../../../src/Service/Request.service'; +import DependencyInjection from '../../../src/DependencyInjection/DependencyInjection.class'; +import LambdaWrapper from '../../../src/Wrapper/LambdaWrapper'; import LambdaTermination from '../../../src/Wrapper/LambdaTermination'; +const { expect } = ServerlessMochaPlugin.chai; -const expect = ServerlessMochaPlugin.chai.expect; - -let getEvent = require('../../mocks/aws/event.json'); -let getContext = require('../../mocks/aws/context.json'); +const getEvent = require('../../mocks/aws/event.json'); +const getContext = require('../../mocks/aws/context.json'); describe('Wrapper/LambdaWrapper', () => { - let dependencyInjection = {}; let requestService = {}; - let configuration = { + const configuration = { DEFINITIONS: {}, DEPENDENCIES: {}, }; describe('should inject dependency injection into the function', () => { - LambdaWrapper(configuration, (di, request) => { dependencyInjection = di; requestService = request; @@ -41,11 +38,9 @@ describe('Wrapper/LambdaWrapper', () => { it('dependency injection should output the event that was provided to it', () => { expect(dependencyInjection.getContext()).to.eql(getContext); }); - }); describe('should inject the request service into the function', () => { - LambdaWrapper(configuration, (di, request) => { dependencyInjection = di; requestService = request; @@ -58,14 +53,13 @@ describe('Wrapper/LambdaWrapper', () => { it('request service should contain variables that were sent to it via the event', () => { expect(requestService.get('test'), null, REQUEST_TYPES.GET).to.eql(getEvent.queryStringParameters.test); }); - }); describe('should catch exceptions and generate appropriate responses', () => { it('Logs the error', () => { let loggerStub; - const lambda = LambdaWrapper(configuration, di => { + const lambda = LambdaWrapper(configuration, (di) => { loggerStub = sinon.stub(di.dependencies[DEFINITIONS.LOGGER], 'error'); throw new Error('Some error'); }); @@ -76,7 +70,7 @@ describe('Wrapper/LambdaWrapper', () => { }); it('Returns 500 exception with a common error', () => { - const lambda = LambdaWrapper(configuration, di => { + const lambda = LambdaWrapper(configuration, (di) => { sinon.stub(di.dependencies[DEFINITIONS.LOGGER], 'error'); throw new Error('Some error'); }); @@ -89,7 +83,7 @@ describe('Wrapper/LambdaWrapper', () => { }); it('Returns a response generated by LambdaTermination', () => { - const lambda = LambdaWrapper(configuration, di => { + const lambda = LambdaWrapper(configuration, (di) => { sinon.stub(di.dependencies[DEFINITIONS.LOGGER], 'error'); throw new LambdaTermination('internal', 403, 'external', 'some message'); }); @@ -103,21 +97,20 @@ describe('Wrapper/LambdaWrapper', () => { }); it('Catches async errors', () => { - const lambda = LambdaWrapper(configuration, di => { - return new Promise((resolve) => { + const lambda = LambdaWrapper(configuration, (di) => { + return new Promise(() => { sinon.stub(di.dependencies[DEFINITIONS.LOGGER], 'error'); throw new LambdaTermination('internal', 403, 'external'); }); }); - return lambda(getEvent, getContext) - .then((response) => { - const body = JSON.parse(response.body); + return lambda(getEvent, getContext).then((response) => { + const body = JSON.parse(response.body); - expect(response.statusCode).to.be.equal(403); - expect(body.message).to.be.equal('unknown error'); - expect(body.data).to.be.equal('external'); - }); + expect(response.statusCode).to.be.equal(403); + expect(body.message).to.be.equal('unknown error'); + expect(body.data).to.be.equal('external'); + }); }); }); }); diff --git a/yarn.lock b/yarn.lock index 7b6c8ba0..0f336f20 100644 --- a/yarn.lock +++ b/yarn.lock @@ -63,6 +63,15 @@ semver "^5.4.1" source-map "^0.5.0" +"@babel/eslint-parser@^7.11.4": + version "7.11.4" + resolved "https://registry.yarnpkg.com/@babel/eslint-parser/-/eslint-parser-7.11.4.tgz#f79bac69088097a8418f5c67fc462c89a72c2f48" + integrity sha512-syIzsqEUvmc6WEYbLqrvBODCM1wMo3SQ4h4G9gtCcQctv1VUlA5davRAWHFm7ncQlxcJs4I7kaflsnAP8iA8Aw== + dependencies: + eslint-scope "5.1.0" + eslint-visitor-keys "^1.3.0" + semver "^6.3.0" + "@babel/generator@^7.11.0": version "7.11.0" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.11.0.tgz#4b90c78d8c12825024568cbe83ee6c9af193585c" @@ -893,6 +902,11 @@ lodash "^4.17.19" to-fast-properties "^2.0.0" +"@comicrelief/eslint-config@^1.0.6": + version "1.0.6" + resolved "https://registry.yarnpkg.com/@comicrelief/eslint-config/-/eslint-config-1.0.6.tgz#d2ad982d738a22a9efb9bf28e90afadfe60b4db7" + integrity sha512-2TIAQlVFQC6I5smTSe3aguL56CJX323fbB8mDqleSqNT12mSZw2vKqfZxKzYTxvxa0wkvFXjaynJ865LUgsNCA== + "@dabh/diagnostics@^2.0.2": version "2.0.2" resolved "https://registry.yarnpkg.com/@dabh/diagnostics/-/diagnostics-2.0.2.tgz#290d08f7b381b8f94607dc8f471a12c675f9db31" @@ -2595,6 +2609,13 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" +clean-regexp@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/clean-regexp/-/clean-regexp-1.0.0.tgz#8df7c7aae51fd36874e8f8d05b9180bc11a3fed7" + integrity sha1-jffHquUf02h06PjQW5GAvBGj/tc= + dependencies: + escape-string-regexp "^1.0.5" + clean-stack@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" @@ -3084,7 +3105,7 @@ debug@4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: dependencies: ms "^2.1.1" -debuglog@*, debuglog@^1.0.1: +debuglog@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" integrity sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI= @@ -3544,6 +3565,14 @@ escodegen@1.x.x: optionalDependencies: source-map "~0.6.1" +eslint-ast-utils@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/eslint-ast-utils/-/eslint-ast-utils-1.1.0.tgz#3d58ba557801cfb1c941d68131ee9f8c34bd1586" + integrity sha512-otzzTim2/1+lVrlH19EfQQJEhVJSu0zOb9ygb3iapN6UlyaDtyRq4b5U1FuW0v1lRa9Fp/GJyHkSwm6NqABgCA== + dependencies: + lodash.get "^4.4.2" + lodash.zip "^4.2.0" + eslint-config-airbnb-base@^14.2.0: version "14.2.0" resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.2.0.tgz#fe89c24b3f9dc8008c9c0d0d88c28f95ed65e9c4" @@ -3553,6 +3582,13 @@ eslint-config-airbnb-base@^14.2.0: object.assign "^4.1.0" object.entries "^1.1.2" +eslint-config-prettier@^6.11.0: + version "6.11.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.11.0.tgz#f6d2238c1290d01c859a8b5c1f7d352a0b0da8b1" + integrity sha512-oB8cpLWSAjOVFEJhhyMZh6NOEOtBVziaqdDQ86+qhDHFbZXoRTM7pNSvFRfW/W/L/LrQ38C99J5CGuRBBzBsdA== + dependencies: + get-stdin "^6.0.0" + eslint-import-resolver-node@^0.3.3: version "0.3.4" resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz#85ffa81942c25012d8231096ddf679c03042c717" @@ -3596,7 +3632,38 @@ eslint-plugin-import@^2.22.0: resolve "^1.17.0" tsconfig-paths "^3.9.0" -eslint-scope@^5.1.0: +eslint-plugin-prettier@^3.1.4: + version "3.1.4" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.4.tgz#168ab43154e2ea57db992a2cd097c828171f75c2" + integrity sha512-jZDa8z76klRqo+TdGDTFJSavwbnWK2ZpqGKNZ+VvweMW516pDUMmQ2koXvxEE4JhzNvTv+radye/bWGBmA6jmg== + dependencies: + prettier-linter-helpers "^1.0.0" + +eslint-plugin-sonarjs@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-sonarjs/-/eslint-plugin-sonarjs-0.5.0.tgz#ce17b2daba65a874c2862213a9e38e8986ad7d7d" + integrity sha512-XW5MnzlRjhXpIdbULC/qAdJYHWw3rRLws/DyawdlPU/IdVr9AmRK1r2LaCvabwKOAW2XYYSo3kDX58E4MrB7PQ== + +eslint-plugin-unicorn@^21.0.0: + version "21.0.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-unicorn/-/eslint-plugin-unicorn-21.0.0.tgz#7e3a8b0f725f003619e1f40d769939ecd8d708d0" + integrity sha512-S8v7+v4gZTQPj4pKKvexhgSUaLQSyItvxW2SVZDaX9Iu5IjlAmF2eni+L6w8a2aqshxgU8Lle4FIAVDtuejSKQ== + dependencies: + ci-info "^2.0.0" + clean-regexp "^1.0.0" + eslint-ast-utils "^1.1.0" + eslint-template-visitor "^2.0.0" + eslint-utils "^2.1.0" + import-modules "^2.0.0" + lodash "^4.17.15" + pluralize "^8.0.0" + read-pkg-up "^7.0.1" + regexp-tree "^0.1.21" + reserved-words "^0.1.2" + safe-regex "^2.1.1" + semver "^7.3.2" + +eslint-scope@5.1.0, eslint-scope@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.0.tgz#d0f971dfe59c69e0cada684b23d49dbf82600ce5" integrity sha512-iiGRvtxWqgtx5m8EyQUJihBloE4EnYeGE/bz1wSPwJE6tZuJUtHlhqDM4Xj2ukE8Dyy1+HCZ4hE0fzIVMzb58w== @@ -3604,6 +3671,16 @@ eslint-scope@^5.1.0: esrecurse "^4.1.0" estraverse "^4.1.1" +eslint-template-visitor@^2.0.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/eslint-template-visitor/-/eslint-template-visitor-2.2.1.tgz#2dccb1ab28fa7429e56ba6dd0144def2d89bc2d6" + integrity sha512-q3SxoBXz0XjPGkUpwGVAwIwIPIxzCAJX1uwfVc8tW3v7u/zS7WXNH3I2Mu2MDz2NgSITAyKLRaQFPHu/iyKxDQ== + dependencies: + babel-eslint "^10.1.0" + eslint-visitor-keys "^1.3.0" + esquery "^1.3.1" + multimap "^1.1.0" + eslint-utils@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" @@ -3677,7 +3754,7 @@ esprima@^4.0.0, esprima@^4.0.1, esprima@~4.0.0: resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== -esquery@^1.2.0: +esquery@^1.2.0, esquery@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.3.1.tgz#b78b5828aa8e214e29fb74c4d5b752e1c033da57" integrity sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ== @@ -3837,6 +3914,11 @@ fast-deep-equal@^3.1.1: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== +fast-diff@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" + integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== + fast-glob@^3.0.3, fast-glob@^3.1.1, fast-glob@^3.2.2: version "3.2.4" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.4.tgz#d20aefbf99579383e7f3cc66529158c9b98554d3" @@ -4224,6 +4306,11 @@ get-package-type@^0.1.0: resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== +get-stdin@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b" + integrity sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g== + get-stream@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" @@ -4750,7 +4837,12 @@ import-lazy@^2.1.0: resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43" integrity sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM= -imurmurhash@*, imurmurhash@^0.1.4: +import-modules@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/import-modules/-/import-modules-2.0.0.tgz#9c1e13b4e7a15682f70a6e3fa29534e4540cfc5d" + integrity sha512-iczM/v9drffdNnABOKwj0f9G3cFDon99VcG1mxeBsdqnbd+vnQ5c2uAiCHNQITqFTOPaEvwg3VjoWCur0uHLEw== + +imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= @@ -5764,11 +5856,6 @@ lockfile@^1.0.4: dependencies: signal-exit "^3.0.2" -lodash._baseindexof@*: - version "3.1.0" - resolved "https://registry.yarnpkg.com/lodash._baseindexof/-/lodash._baseindexof-3.1.0.tgz#fe52b53a1c6761e42618d654e4a25789ed61822c" - integrity sha1-/lK1OhxnYeQmGNZU5KJXie1hgiw= - lodash._baseuniq@~4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash._baseuniq/-/lodash._baseuniq-4.6.0.tgz#0ebb44e456814af7905c6212fa2c9b2d51b841e8" @@ -5777,33 +5864,11 @@ lodash._baseuniq@~4.6.0: lodash._createset "~4.0.0" lodash._root "~3.0.0" -lodash._bindcallback@*: - version "3.0.1" - resolved "https://registry.yarnpkg.com/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz#e531c27644cf8b57a99e17ed95b35c748789392e" - integrity sha1-5THCdkTPi1epnhftlbNcdIeJOS4= - -lodash._cacheindexof@*: - version "3.0.2" - resolved "https://registry.yarnpkg.com/lodash._cacheindexof/-/lodash._cacheindexof-3.0.2.tgz#3dc69ac82498d2ee5e3ce56091bafd2adc7bde92" - integrity sha1-PcaayCSY0u5ePOVgkbr9Ktx73pI= - -lodash._createcache@*: - version "3.1.2" - resolved "https://registry.yarnpkg.com/lodash._createcache/-/lodash._createcache-3.1.2.tgz#56d6a064017625e79ebca6b8018e17440bdcf093" - integrity sha1-VtagZAF2JeeevKa4AY4XRAvc8JM= - dependencies: - lodash._getnative "^3.0.0" - lodash._createset@~4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/lodash._createset/-/lodash._createset-4.0.3.tgz#0f4659fbb09d75194fa9e2b88a6644d363c9fe26" integrity sha1-D0ZZ+7CddRlPqeK4imZE02PJ/iY= -lodash._getnative@*, lodash._getnative@^3.0.0: - version "3.9.1" - resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5" - integrity sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U= - lodash._root@~3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/lodash._root/-/lodash._root-3.0.1.tgz#fba1c4524c19ee9a5f8136b4609f017cf4ded692" @@ -5939,11 +6004,6 @@ lodash.reduce@^4.6.0: resolved "https://registry.yarnpkg.com/lodash.reduce/-/lodash.reduce-4.6.0.tgz#f1ab6b839299ad48f784abbf476596f03b914d3b" integrity sha1-8atrg5KZrUj3hKu/R2WW8DuRTTs= -lodash.restparam@*: - version "3.6.1" - resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805" - integrity sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU= - lodash.set@^4.3.2: version "4.3.2" resolved "https://registry.yarnpkg.com/lodash.set/-/lodash.set-4.3.2.tgz#d8757b1da807dde24816b0d6a84bea1a76230b23" @@ -5994,6 +6054,11 @@ lodash.without@~4.4.0: resolved "https://registry.yarnpkg.com/lodash.without/-/lodash.without-4.4.0.tgz#3cd4574a00b67bae373a94b748772640507b7aac" integrity sha1-PNRXSgC2e643OpS3SHcmQFB7eqw= +lodash.zip@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.zip/-/lodash.zip-4.2.0.tgz#ec6662e4896408ed4ab6c542a3990b72cc080020" + integrity sha1-7GZi5IlkCO1KtsVCo5kLcswIACA= + lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.4: version "4.17.19" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b" @@ -6453,6 +6518,11 @@ ms@2.1.2, ms@^2.0.0, ms@^2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== +multimap@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/multimap/-/multimap-1.1.0.tgz#5263febc085a1791c33b59bb3afc6a76a2a10ca8" + integrity sha512-0ZIR9PasPxGXmRsEF8jsDzndzHDj7tIav+JUmvIFB/WHswliFnquxECT/De7GR4yg99ky/NlRKJT82G1y271bw== + mute-stream@0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" @@ -7547,6 +7617,11 @@ pluralize@^7.0.0: resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-7.0.0.tgz#298b89df8b93b0221dbf421ad2b1b1ea23fc6777" integrity sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow== +pluralize@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-8.0.0.tgz#1a6fa16a38d12a1901e0320fa017051c539ce3b1" + integrity sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA== + posix-character-classes@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" @@ -7572,6 +7647,18 @@ prepend-http@^2.0.0: resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= +prettier-linter-helpers@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" + integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== + dependencies: + fast-diff "^1.1.2" + +prettier@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.1.0.tgz#5a9789f767a243118c60f3e56d95cb6544914fbb" + integrity sha512-lz28cCbA1cDFHVuY8vvj6QuqOwIpyIfPUYkSl8AZ/vxH8qBXMMjE2knfLHCrZCmUsK/H1bg1P0tOo0dJkTJHvw== + pretty-bytes@^5.1.0: version "5.3.0" resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.3.0.tgz#f2849e27db79fb4d6cfe24764fc4134f165989f2" @@ -8003,6 +8090,11 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" +regexp-tree@^0.1.21, regexp-tree@~0.1.1: + version "0.1.21" + resolved "https://registry.yarnpkg.com/regexp-tree/-/regexp-tree-0.1.21.tgz#55e2246b7f7d36f1b461490942fa780299c400d7" + integrity sha512-kUUXjX4AnqnR8KRTCrayAo9PzYMRKmVoGgaz2tBuz0MF3g1ZbGebmtW0yFHfFK9CmBjQKeYIgoL22pFLBJY7sw== + regexpp@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2" @@ -8133,6 +8225,11 @@ require-main-filename@^2.0.0: resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== +reserved-words@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/reserved-words/-/reserved-words-0.1.2.tgz#00a0940f98cd501aeaaac316411d9adc52b31ab1" + integrity sha1-AKCUD5jNUBrqqsMWQR2a3FKzGrE= + resolve-alpn@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.0.0.tgz#745ad60b3d6aff4b4a48e01b8c0bdc70959e0e8c" @@ -8264,6 +8361,13 @@ safe-regex@^1.1.0: dependencies: ret "~0.1.10" +safe-regex@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-2.1.1.tgz#f7128f00d056e2fe5c11e81a1324dd974aadced2" + integrity sha512-rx+x8AMzKb5Q5lQ95Zoi6ZbJqwCLkqi3XuJXp5P3rT8OEc6sZCJG5AE5dU3lsgRr/F4Bs31jSlVN+j5KrsGu9A== + dependencies: + regexp-tree "~0.1.1" + "safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"