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"