diff --git a/lib/classes/CLI.js b/lib/classes/CLI.js index cf3ba1c9103..ba8032c9707 100644 --- a/lib/classes/CLI.js +++ b/lib/classes/CLI.js @@ -132,9 +132,11 @@ class CLI { this.consoleLog(`${indent}${chalk.yellow(command)} ${chalk.dim(dots)} ${usage}`); } - _.forEach(commandObject.commands, (subcommandObject, subcommand) => { - this.displayCommandUsage(subcommandObject, `${command} ${subcommand}`, indents); - }); + if (commandObject.commands) { + Object.entries(commandObject.commands).forEach(([subcommand, subcommandObject]) => { + this.displayCommandUsage(subcommandObject, `${command} ${subcommand}`, indents); + }); + } } displayCommandOptions(commandObject) { @@ -149,7 +151,7 @@ class CLI { }) : commandObject.options; - _.forEach(commandOptions, (optionsObject, option) => { + Object.entries(commandOptions).forEach(([option, optionsObject]) => { let optionsDots = '.'.repeat(Math.max(dotsLength - option.length, 0)); const optionsUsage = optionsObject.usage; @@ -231,7 +233,7 @@ class CLI { this.consoleLog(''); if (Object.keys(this.loadedCommands).length) { - _.forEach(this.loadedCommands, (details, command) => { + Object.entries(this.loadedCommands).forEach(([command, details]) => { this.displayCommandUsage(details, command); }); } else { @@ -291,25 +293,24 @@ functionalities related to given service or current environment.` // check for subcommands if ('commands' in cmd) { - _.forEach(cmd.commands, d => { + Object.values(cmd.commands).forEach(d => { addToPluginCommands(d); }); } }; // fill up pluginCommands with commands in loadedCommands - _.forEach(this.loadedCommands, details => { + Object.values(this.loadedCommands).forEach(details => { addToPluginCommands(details); }); // sort plugins alphabetically - pluginCommands = _(pluginCommands) - .toPairs() + pluginCommands = _(Object.entries(pluginCommands)) .sortBy(0) .fromPairs() .value(); - _.forEach(pluginCommands, (details, plugin) => { + Object.entries(pluginCommands).forEach(([plugin, details]) => { this.consoleLog(plugin); details.forEach(cmd => { // display command usage with single(1) indent diff --git a/lib/classes/PluginManager.js b/lib/classes/PluginManager.js index 8c3dc247f9e..b1f469e783d 100644 --- a/lib/classes/PluginManager.js +++ b/lib/classes/PluginManager.js @@ -261,44 +261,48 @@ class PluginManager { loadCommands(pluginInstance) { const pluginName = pluginInstance.constructor.name; - _.forEach(pluginInstance.commands, (details, key) => { - const command = this.loadCommand(pluginName, details, key); - // Grab and extract deprecated events - command.lifecycleEvents = (command.lifecycleEvents || []).map(event => { - if (event.startsWith('deprecated#')) { - // Extract event and optional redirect - const transformedEvent = /^deprecated#(.*?)(?:->(.*?))?$/.exec(event); - this.deprecatedEvents[`${command.key}:${transformedEvent[1]}`] = - transformedEvent[2] || null; - return transformedEvent[1]; - } - return event; + if (pluginInstance.commands) { + Object.entries(pluginInstance.commands).forEach(([key, details]) => { + const command = this.loadCommand(pluginName, details, key); + // Grab and extract deprecated events + command.lifecycleEvents = (command.lifecycleEvents || []).map(event => { + if (event.startsWith('deprecated#')) { + // Extract event and optional redirect + const transformedEvent = /^deprecated#(.*?)(?:->(.*?))?$/.exec(event); + this.deprecatedEvents[`${command.key}:${transformedEvent[1]}`] = + transformedEvent[2] || null; + return transformedEvent[1]; + } + return event; + }); + this.commands[key] = _.merge({}, this.commands[key], command); }); - this.commands[key] = _.merge({}, this.commands[key], command); - }); + } } loadHooks(pluginInstance) { const pluginName = pluginInstance.constructor.name; - _.forEach(pluginInstance.hooks, (hook, event) => { - let target = event; - const baseEvent = event.replace(/^(?:after:|before:)/, ''); - if (this.deprecatedEvents[baseEvent]) { - const redirectedEvent = this.deprecatedEvents[baseEvent]; - if (process.env.SLS_DEBUG) { - this.serverless.cli.log(`WARNING: Plugin ${pluginName} uses deprecated hook ${event}, + if (pluginInstance.hooks) { + Object.entries(pluginInstance.hooks).forEach(([event, hook]) => { + let target = event; + const baseEvent = event.replace(/^(?:after:|before:)/, ''); + if (this.deprecatedEvents[baseEvent]) { + const redirectedEvent = this.deprecatedEvents[baseEvent]; + if (process.env.SLS_DEBUG) { + this.serverless.cli.log(`WARNING: Plugin ${pluginName} uses deprecated hook ${event}, use ${redirectedEvent} hook instead`); + } + if (redirectedEvent) { + target = event.replace(baseEvent, redirectedEvent); + } } - if (redirectedEvent) { - target = event.replace(baseEvent, redirectedEvent); - } - } - this.hooks[target] = this.hooks[target] || []; - this.hooks[target].push({ - pluginName, - hook, + this.hooks[target] = this.hooks[target] || []; + this.hooks[target].push({ + pluginName, + hook, + }); }); - }); + } } loadVariableResolvers(pluginInstance) { @@ -560,31 +564,33 @@ class PluginManager { } validateOptions(command) { - _.forEach(command.options, (value, key) => { - if (value.required && (this.cliOptions[key] === true || !this.cliOptions[key])) { - let requiredThings = `the --${key} option`; + if (command.options) { + Object.entries(command.options).forEach(([key, value]) => { + if (value.required && (this.cliOptions[key] === true || !this.cliOptions[key])) { + let requiredThings = `the --${key} option`; - if (value.shortcut) { - requiredThings += ` / -${value.shortcut} shortcut`; - } - let errorMessage = `This command requires ${requiredThings}.`; + if (value.shortcut) { + requiredThings += ` / -${value.shortcut} shortcut`; + } + let errorMessage = `This command requires ${requiredThings}.`; - if (value.usage) { - errorMessage = `${errorMessage} Usage: ${value.usage}`; - } + if (value.usage) { + errorMessage = `${errorMessage} Usage: ${value.usage}`; + } - throw new this.serverless.classes.Error(errorMessage); - } + throw new this.serverless.classes.Error(errorMessage); + } - if ( - _.isPlainObject(value.customValidation) && - value.customValidation.regularExpression instanceof RegExp && - typeof value.customValidation.errorMessage === 'string' && - !value.customValidation.regularExpression.test(this.cliOptions[key]) - ) { - throw new this.serverless.classes.Error(value.customValidation.errorMessage); - } - }); + if ( + _.isPlainObject(value.customValidation) && + value.customValidation.regularExpression instanceof RegExp && + typeof value.customValidation.errorMessage === 'string' && + !value.customValidation.regularExpression.test(this.cliOptions[key]) + ) { + throw new this.serverless.classes.Error(value.customValidation.errorMessage); + } + }); + } } updateAutocompleteCacheFile() { @@ -594,7 +600,7 @@ class PluginManager { validationHash: '', }; - _.forEach(commands, (commandObj, commandName) => { + Object.entries(commands).forEach(([commandName, commandObj]) => { const command = commandObj; if (!command.options) { command.options = {}; @@ -618,23 +624,27 @@ class PluginManager { } convertShortcutsIntoOptions(command) { - _.forEach(command.options, (optionObject, optionKey) => { - if (optionObject.shortcut && Object.keys(this.cliOptions).includes(optionObject.shortcut)) { - Object.keys(this.cliOptions).forEach(option => { - if (option === optionObject.shortcut) { - this.cliOptions[optionKey] = this.cliOptions[option]; - } - }); - } - }); + if (command.options) { + Object.entries(command.options).forEach(([optionKey, optionObject]) => { + if (optionObject.shortcut && Object.keys(this.cliOptions).includes(optionObject.shortcut)) { + Object.keys(this.cliOptions).forEach(option => { + if (option === optionObject.shortcut) { + this.cliOptions[optionKey] = this.cliOptions[option]; + } + }); + } + }); + } } assignDefaultOptions(command) { - _.forEach(command.options, (value, key) => { - if (value.default != null && (!this.cliOptions[key] || this.cliOptions[key] === true)) { - this.cliOptions[key] = value.default; - } - }); + if (command.options) { + Object.entries(command.options).forEach(([key, value]) => { + if (value.default != null && (!this.cliOptions[key] || this.cliOptions[key] === true)) { + this.cliOptions[key] = value.default; + } + }); + } } asyncPluginInit() { diff --git a/lib/classes/Service.js b/lib/classes/Service.js index eb48cd964ca..1a74555308d 100644 --- a/lib/classes/Service.js +++ b/lib/classes/Service.js @@ -178,7 +178,7 @@ class Service { // setup function.name property const stageNameForFunction = options.stage || this.provider.stage; - _.forEach(that.functions, (functionObj, functionName) => { + Object.entries(that.functions).forEach(([functionName, functionObj]) => { if (!functionObj.events) { that.functions[functionName].events = []; } diff --git a/lib/plugins/aws/info/display.js b/lib/plugins/aws/info/display.js index 8cb75fd7161..eec85106739 100644 --- a/lib/plugins/aws/info/display.js +++ b/lib/plugins/aws/info/display.js @@ -1,7 +1,6 @@ 'use strict'; const chalk = require('chalk'); -const _ = require('lodash'); module.exports = { displayServiceInfo() { @@ -56,7 +55,7 @@ module.exports = { info.endpoints.forEach(endpoint => { // if the endpoint is of type http(s) if (endpoint.startsWith('https://')) { - _.forEach(this.serverless.service.functions, functionObject => { + Object.values(this.serverless.service.functions).forEach(functionObject => { functionObject.events.forEach(event => { if (event.http) { let method; diff --git a/lib/plugins/aws/lib/normalizeFiles.js b/lib/plugins/aws/lib/normalizeFiles.js index f160c14a21e..ebceb11577a 100644 --- a/lib/plugins/aws/lib/normalizeFiles.js +++ b/lib/plugins/aws/lib/normalizeFiles.js @@ -6,7 +6,7 @@ module.exports = { normalizeCloudFormationTemplate(template) { const normalizedTemplate = _.cloneDeep(template); - _.forEach(normalizedTemplate.Resources, (value, key) => { + Object.entries(normalizedTemplate.Resources).forEach(([key, value]) => { if (key.startsWith('ApiGatewayDeployment')) { delete Object.assign(normalizedTemplate.Resources, { ApiGatewayDeployment: normalizedTemplate.Resources[key], diff --git a/lib/plugins/aws/package/compile/events/alb/lib/validate.js b/lib/plugins/aws/package/compile/events/alb/lib/validate.js index 8b31c149f64..96d05cbc5c8 100644 --- a/lib/plugins/aws/package/compile/events/alb/lib/validate.js +++ b/lib/plugins/aws/package/compile/events/alb/lib/validate.js @@ -29,7 +29,7 @@ module.exports = { const events = []; - _.forEach(this.serverless.service.functions, (functionObject, functionName) => { + Object.entries(this.serverless.service.functions).forEach(([functionName, functionObject]) => { functionObject.events.forEach(event => { if (event.alb) { if (_.isObject(event.alb)) { diff --git a/lib/plugins/aws/package/compile/events/apiGateway/lib/cors.js b/lib/plugins/aws/package/compile/events/apiGateway/lib/cors.js index f07963f95ae..ebb939b117e 100644 --- a/lib/plugins/aws/package/compile/events/apiGateway/lib/cors.js +++ b/lib/plugins/aws/package/compile/events/apiGateway/lib/cors.js @@ -5,86 +5,91 @@ const BbPromise = require('bluebird'); module.exports = { compileCors() { - _.forEach(this.validated.corsPreflight, (config, path) => { - const resourceName = this.getResourceName(path); - const resourceRef = this.getResourceId(path); - const corsMethodLogicalId = this.provider.naming.getMethodLogicalId(resourceName, 'options'); - - let origin = config.origin; - let origins = config.origins && Array.isArray(config.origins) ? config.origins : undefined; - - if (origin && origin.indexOf(',') !== -1) { - origins = origin.split(',').map(a => a.trim()); - } - - if (Array.isArray(origins) && origins.length) { - origin = origins[0]; - } - - if (!origin) { - const errorMessage = 'must specify either origin or origins'; - throw new this.serverless.classes.Error(errorMessage); - } - - const preflightHeaders = { - 'Access-Control-Allow-Origin': `'${origin}'`, - 'Access-Control-Allow-Headers': `'${config.headers.join(',')}'`, - 'Access-Control-Allow-Methods': `'${config.methods.join(',')}'`, - }; - - // Only set Access-Control-Allow-Credentials when explicitly allowed (omit if false) - if (config.allowCredentials) { - preflightHeaders['Access-Control-Allow-Credentials'] = `'${config.allowCredentials}'`; - } - - // Enable CORS Max Age usage if set - if (config.maxAge) { - if (config.maxAge > 0) { - preflightHeaders['Access-Control-Max-Age'] = `'${config.maxAge}'`; - } else { - const errorMessage = 'maxAge should be an integer over 0'; + if (this.validated.corsPreflight) { + Object.entries(this.validated.corsPreflight).forEach(([path, config]) => { + const resourceName = this.getResourceName(path); + const resourceRef = this.getResourceId(path); + const corsMethodLogicalId = this.provider.naming.getMethodLogicalId( + resourceName, + 'options' + ); + + let origin = config.origin; + let origins = config.origins && Array.isArray(config.origins) ? config.origins : undefined; + + if (origin && origin.indexOf(',') !== -1) { + origins = origin.split(',').map(a => a.trim()); + } + + if (Array.isArray(origins) && origins.length) { + origin = origins[0]; + } + + if (!origin) { + const errorMessage = 'must specify either origin or origins'; throw new this.serverless.classes.Error(errorMessage); } - } - - // Allow Cache-Control header if set - if (config.cacheControl) { - preflightHeaders['Cache-Control'] = `'${config.cacheControl}'`; - } - - if (config.methods.includes('ANY')) { - preflightHeaders['Access-Control-Allow-Methods'] = preflightHeaders[ - 'Access-Control-Allow-Methods' - ].replace('ANY', 'DELETE,GET,HEAD,PATCH,POST,PUT'); - } - - this.apiGatewayMethodLogicalIds.push(corsMethodLogicalId); - - _.merge(this.serverless.service.provider.compiledCloudFormationTemplate.Resources, { - [corsMethodLogicalId]: { - Type: 'AWS::ApiGateway::Method', - Properties: { - AuthorizationType: 'NONE', - HttpMethod: 'OPTIONS', - MethodResponses: this.generateCorsMethodResponses(preflightHeaders), - RequestParameters: {}, - Integration: { - Type: 'MOCK', - RequestTemplates: { - 'application/json': '{statusCode:200}', + + const preflightHeaders = { + 'Access-Control-Allow-Origin': `'${origin}'`, + 'Access-Control-Allow-Headers': `'${config.headers.join(',')}'`, + 'Access-Control-Allow-Methods': `'${config.methods.join(',')}'`, + }; + + // Only set Access-Control-Allow-Credentials when explicitly allowed (omit if false) + if (config.allowCredentials) { + preflightHeaders['Access-Control-Allow-Credentials'] = `'${config.allowCredentials}'`; + } + + // Enable CORS Max Age usage if set + if (config.maxAge) { + if (config.maxAge > 0) { + preflightHeaders['Access-Control-Max-Age'] = `'${config.maxAge}'`; + } else { + const errorMessage = 'maxAge should be an integer over 0'; + throw new this.serverless.classes.Error(errorMessage); + } + } + + // Allow Cache-Control header if set + if (config.cacheControl) { + preflightHeaders['Cache-Control'] = `'${config.cacheControl}'`; + } + + if (config.methods.includes('ANY')) { + preflightHeaders['Access-Control-Allow-Methods'] = preflightHeaders[ + 'Access-Control-Allow-Methods' + ].replace('ANY', 'DELETE,GET,HEAD,PATCH,POST,PUT'); + } + + this.apiGatewayMethodLogicalIds.push(corsMethodLogicalId); + + _.merge(this.serverless.service.provider.compiledCloudFormationTemplate.Resources, { + [corsMethodLogicalId]: { + Type: 'AWS::ApiGateway::Method', + Properties: { + AuthorizationType: 'NONE', + HttpMethod: 'OPTIONS', + MethodResponses: this.generateCorsMethodResponses(preflightHeaders), + RequestParameters: {}, + Integration: { + Type: 'MOCK', + RequestTemplates: { + 'application/json': '{statusCode:200}', + }, + ContentHandling: 'CONVERT_TO_TEXT', + IntegrationResponses: this.generateCorsIntegrationResponses( + preflightHeaders, + origins + ), }, - ContentHandling: 'CONVERT_TO_TEXT', - IntegrationResponses: this.generateCorsIntegrationResponses( - preflightHeaders, - origins - ), + ResourceId: resourceRef, + RestApiId: this.provider.getApiGatewayRestApiId(), }, - ResourceId: resourceRef, - RestApiId: this.provider.getApiGatewayRestApiId(), }, - }, + }); }); - }); + } return BbPromise.resolve(); }, @@ -92,7 +97,7 @@ module.exports = { generateCorsMethodResponses(preflightHeaders) { const methodResponseHeaders = {}; - _.forEach(preflightHeaders, (value, header) => { + Object.keys(preflightHeaders).forEach(header => { methodResponseHeaders[`method.response.header.${header}`] = true; }); diff --git a/lib/plugins/aws/package/compile/events/apiGateway/lib/method/index.js b/lib/plugins/aws/package/compile/events/apiGateway/lib/method/index.js index 6ade873b7db..2ff56e0e340 100644 --- a/lib/plugins/aws/package/compile/events/apiGateway/lib/method/index.js +++ b/lib/plugins/aws/package/compile/events/apiGateway/lib/method/index.js @@ -13,7 +13,7 @@ module.exports = { const requestParameters = {}; if (event.http.request && event.http.request.parameters) { - _.forEach(event.http.request.parameters, (value, key) => { + Object.entries(event.http.request.parameters).forEach(([key, value]) => { requestParameters[key] = value.required === undefined ? value : value.required; }); } @@ -83,13 +83,15 @@ module.exports = { }); } const requestTemplates = template.Properties.Integration.RequestTemplates; - _.forEach(requestTemplates, (value, key) => { - let claimsString = ''; - if (extraCognitoPoolClaims && extraCognitoPoolClaims.length > 0) { - claimsString = extraCognitoPoolClaims.join(',').concat(','); - } - requestTemplates[key] = value.replace('extraCognitoPoolClaims', claimsString); - }); + if (requestTemplates) { + Object.entries(requestTemplates).forEach(([key, value]) => { + let claimsString = ''; + if (extraCognitoPoolClaims && extraCognitoPoolClaims.length > 0) { + claimsString = extraCognitoPoolClaims.join(',').concat(','); + } + requestTemplates[key] = value.replace('extraCognitoPoolClaims', claimsString); + }); + } this.apiGatewayMethodLogicalIds.push(methodLogicalId); diff --git a/lib/plugins/aws/package/compile/events/apiGateway/lib/method/integration.js b/lib/plugins/aws/package/compile/events/apiGateway/lib/method/integration.js index 060f205a9fb..add99dbd308 100644 --- a/lib/plugins/aws/package/compile/events/apiGateway/lib/method/integration.js +++ b/lib/plugins/aws/package/compile/events/apiGateway/lib/method/integration.js @@ -152,7 +152,7 @@ module.exports = { _.merge(integrationResponseHeaders, http.response.headers); } - _.forEach(http.response.statusCodes, (config, statusCode) => { + Object.entries(http.response.statusCodes).forEach(([statusCode, config]) => { const responseParameters = _.mapKeys( integrationResponseHeaders, (value, header) => `method.response.header.${header}` @@ -225,7 +225,7 @@ module.exports = { getIntegrationRequestParameters(http) { const parameters = {}; if (http.request && http.request.parameters) { - _.forEach(http.request.parameters, (value, key) => { + Object.entries(http.request.parameters).forEach(([key, value]) => { parameters[`integration.${key.substring('method.'.length)}`] = value.mappedValue || key; }); } diff --git a/lib/plugins/aws/package/compile/events/apiGateway/lib/method/responses.js b/lib/plugins/aws/package/compile/events/apiGateway/lib/method/responses.js index 2b1ddc86355..83e668ab1a1 100644 --- a/lib/plugins/aws/package/compile/events/apiGateway/lib/method/responses.js +++ b/lib/plugins/aws/package/compile/events/apiGateway/lib/method/responses.js @@ -32,7 +32,7 @@ module.exports = { _.merge(methodResponseHeaders, http.response.headers); } - _.forEach(http.response.statusCodes, (config, statusCode) => { + Object.entries(http.response.statusCodes).forEach(([statusCode, config]) => { const methodResponse = { ResponseParameters: {}, ResponseModels: {}, diff --git a/lib/plugins/aws/package/compile/events/apiGateway/lib/validate.js b/lib/plugins/aws/package/compile/events/apiGateway/lib/validate.js index 76af7c17043..93e84e7067c 100644 --- a/lib/plugins/aws/package/compile/events/apiGateway/lib/validate.js +++ b/lib/plugins/aws/package/compile/events/apiGateway/lib/validate.js @@ -40,7 +40,7 @@ module.exports = { const events = []; const corsPreflight = {}; - _.forEach(this.serverless.service.functions, (functionObject, functionName) => { + Object.entries(this.serverless.service.functions).forEach(([functionName, functionObject]) => { (functionObject.events || []).forEach(event => { if (event.http) { const http = this.getHttp(event, functionName); @@ -524,9 +524,12 @@ module.exports = { locations.forEach(location => { // strip the plural s const singular = location.substring(0, location.length - 1); - _.forEach(httpRequest.parameters[location], (value, key) => { - parameters[`method.request.${singular}.${key}`] = value; - }); + const parameter = httpRequest.parameters[location]; + if (parameter) { + Object.entries(parameter).forEach(([key, value]) => { + parameters[`method.request.${singular}.${key}`] = value; + }); + } }); return parameters; }, diff --git a/lib/plugins/aws/package/compile/events/cognitoUserPool/index.js b/lib/plugins/aws/package/compile/events/cognitoUserPool/index.js index 83784afc027..7a2ba6a99be 100644 --- a/lib/plugins/aws/package/compile/events/cognitoUserPool/index.js +++ b/lib/plugins/aws/package/compile/events/cognitoUserPool/index.js @@ -75,7 +75,7 @@ class AwsCompileCognitoUserPoolEvents { }); // Generate CloudFormation templates for IAM permissions to allow Cognito to trigger Lambda - _.forEach(cognitoUserPoolTriggerFunctions, cognitoUserPoolTriggerFunction => { + cognitoUserPoolTriggerFunctions.forEach(cognitoUserPoolTriggerFunction => { const userPoolLogicalId = this.provider.naming.getCognitoUserPoolLogicalId( cognitoUserPoolTriggerFunction.poolName ); diff --git a/lib/plugins/aws/package/compile/events/s3/index.js b/lib/plugins/aws/package/compile/events/s3/index.js index 19eb8f76c84..7292535a5c3 100644 --- a/lib/plugins/aws/package/compile/events/s3/index.js +++ b/lib/plugins/aws/package/compile/events/s3/index.js @@ -172,73 +172,75 @@ class AwsCompileS3Events { // iterate over all buckets to be created // and compile them to CF resources - _.forEach(bucketsLambdaConfigurations, (bucketLambdaConfiguration, bucketName) => { - let bucketConf = null; - if (bucketsMeta[bucketName]) { - const providerBucket = providerS3[bucketsMeta[bucketName].bucketRef]; - bucketConf = {}; - for (const [key, value] of Object.entries(providerBucket)) { - if (key !== 'name') { - if (!this.allowedBucketProperties.has(key)) { - const errorMessage = [ - `"${key}" is not a valid bucket property.`, - 'A bucket could only be configured with the following properties:\n', - ['name'].concat(Array.from(this.allowedBucketProperties)).join(', '), - ].join(''); - throw new this.serverless.classes.Error(errorMessage); + Object.entries(bucketsLambdaConfigurations).forEach( + ([bucketName, bucketLambdaConfiguration]) => { + let bucketConf = null; + if (bucketsMeta[bucketName]) { + const providerBucket = providerS3[bucketsMeta[bucketName].bucketRef]; + bucketConf = {}; + for (const [key, value] of Object.entries(providerBucket)) { + if (key !== 'name') { + if (!this.allowedBucketProperties.has(key)) { + const errorMessage = [ + `"${key}" is not a valid bucket property.`, + 'A bucket could only be configured with the following properties:\n', + ['name'].concat(Array.from(this.allowedBucketProperties)).join(', '), + ].join(''); + throw new this.serverless.classes.Error(errorMessage); + } + const property = _.upperFirst(key); + bucketConf[property] = value; } - const property = _.upperFirst(key); - bucketConf[property] = value; } } - } - let providedNotificationConfiguration = {}; - if (bucketConf && Object.keys(bucketConf).length > 0) { - providedNotificationConfiguration = bucketConf.NotificationConfiguration; - delete bucketConf.NotificationConfiguration; - } - - const bucketTemplate = { - Type: 'AWS::S3::Bucket', - Properties: Object.assign( - { - BucketName: bucketName, - NotificationConfiguration: Object.assign( - { - LambdaConfigurations: bucketLambdaConfiguration, - }, - providedNotificationConfiguration - ), - }, - bucketConf - ), - DependsOn: [], - }; + let providedNotificationConfiguration = {}; + if (bucketConf && Object.keys(bucketConf).length > 0) { + providedNotificationConfiguration = bucketConf.NotificationConfiguration; + delete bucketConf.NotificationConfiguration; + } - // create the DependsOn properties for the buckets permissions (which are created later on) - const dependsOnToCreate = s3EnabledFunctions.filter(func => func.bucketName === bucketName); + const bucketTemplate = { + Type: 'AWS::S3::Bucket', + Properties: Object.assign( + { + BucketName: bucketName, + NotificationConfiguration: Object.assign( + { + LambdaConfigurations: bucketLambdaConfiguration, + }, + providedNotificationConfiguration + ), + }, + bucketConf + ), + DependsOn: [], + }; + + // create the DependsOn properties for the buckets permissions (which are created later on) + const dependsOnToCreate = s3EnabledFunctions.filter(func => func.bucketName === bucketName); + + dependsOnToCreate.forEach(item => { + const lambdaPermissionLogicalId = this.provider.naming.getLambdaS3PermissionLogicalId( + item.functionName, + (bucketsMeta[item.bucketName] && bucketsMeta[item.bucketName].bucketRef) || + item.bucketName + ); + + bucketTemplate.DependsOn.push(lambdaPermissionLogicalId); + }); - dependsOnToCreate.forEach(item => { - const lambdaPermissionLogicalId = this.provider.naming.getLambdaS3PermissionLogicalId( - item.functionName, - (bucketsMeta[item.bucketName] && bucketsMeta[item.bucketName].bucketRef) || - item.bucketName + const bucketLogicalId = + (bucketsMeta[bucketName] && bucketsMeta[bucketName].logicalId) || + this.provider.naming.getBucketLogicalId(bucketName); + const bucketCFResource = { + [bucketLogicalId]: bucketTemplate, + }; + _.merge( + this.serverless.service.provider.compiledCloudFormationTemplate.Resources, + bucketCFResource ); - - bucketTemplate.DependsOn.push(lambdaPermissionLogicalId); - }); - - const bucketLogicalId = - (bucketsMeta[bucketName] && bucketsMeta[bucketName].logicalId) || - this.provider.naming.getBucketLogicalId(bucketName); - const bucketCFResource = { - [bucketLogicalId]: bucketTemplate, - }; - _.merge( - this.serverless.service.provider.compiledCloudFormationTemplate.Resources, - bucketCFResource - ); - }); + } + ); // iterate over all functions with S3 events // and give S3 permission to invoke them all diff --git a/lib/plugins/aws/package/compile/events/websockets/lib/validate.js b/lib/plugins/aws/package/compile/events/websockets/lib/validate.js index f25e92bb4a7..7913086aae9 100644 --- a/lib/plugins/aws/package/compile/events/websockets/lib/validate.js +++ b/lib/plugins/aws/package/compile/events/websockets/lib/validate.js @@ -11,7 +11,7 @@ module.exports = { return splitArn[splitArn.length - 1]; }; - _.forEach(this.serverless.service.functions, (functionObject, functionName) => { + Object.entries(this.serverless.service.functions).forEach(([functionName, functionObject]) => { functionObject.events.forEach(event => { if (event.websocket) { // dealing with the extended object definition diff --git a/lib/plugins/aws/package/compile/functions/index.js b/lib/plugins/aws/package/compile/functions/index.js index 57bdec54204..6e1968eb422 100644 --- a/lib/plugins/aws/package/compile/functions/index.js +++ b/lib/plugins/aws/package/compile/functions/index.js @@ -181,7 +181,7 @@ class AwsCompileFunctions { if (functionObject.tags || this.serverless.service.provider.tags) { const tags = Object.assign({}, this.serverless.service.provider.tags, functionObject.tags); functionResource.Properties.Tags = []; - _.forEach(tags, (Value, Key) => { + Object.entries(tags).forEach(([Key, Value]) => { functionResource.Properties.Tags.push({ Key, Value }); }); } diff --git a/lib/plugins/plugin/install/install.js b/lib/plugins/plugin/install/install.js index f655299fad1..6caf8298c04 100644 --- a/lib/plugins/plugin/install/install.js +++ b/lib/plugins/plugin/install/install.js @@ -161,7 +161,7 @@ class PluginInstall { return fse.readJson(pluginPackageJsonFilePath).then(pluginPackageJson => { if (pluginPackageJson.peerDependencies) { const pluginsArray = []; - _.forEach(pluginPackageJson.peerDependencies, (v, k) => { + Object.entries(pluginPackageJson.peerDependencies).forEach(([k, v]) => { pluginsArray.push(`${k}@"${v}"`); }); return BbPromise.map(pluginsArray, this.npmInstall); diff --git a/lib/plugins/plugin/uninstall/uninstall.js b/lib/plugins/plugin/uninstall/uninstall.js index 863ed78aced..66a534cf2a2 100644 --- a/lib/plugins/plugin/uninstall/uninstall.js +++ b/lib/plugins/plugin/uninstall/uninstall.js @@ -127,7 +127,7 @@ class PluginUninstall { .then(pluginPackageJson => { if (pluginPackageJson.peerDependencies) { const pluginsArray = []; - _.forEach(pluginPackageJson.peerDependencies, (v, k) => { + Object.keys(pluginPackageJson.peerDependencies).forEach(k => { pluginsArray.push(k); }); return BbPromise.map(pluginsArray, this.npmUninstall); diff --git a/lib/utils/getCommandSuggestion.js b/lib/utils/getCommandSuggestion.js index 9c6485d1d96..0299df02a64 100644 --- a/lib/utils/getCommandSuggestion.js +++ b/lib/utils/getCommandSuggestion.js @@ -6,7 +6,7 @@ const { distance: getDistance } = require('fastest-levenshtein'); const getCollectCommandWords = (commandObject, commandWordsArray) => { let wordsArray = Array.isArray(commandWordsArray) && commandWordsArray.length ? commandWordsArray : []; - _.forEach(commandObject, (commandChildObject, commandChildName) => { + Object.entries(commandObject).forEach(([commandChildName, commandChildObject]) => { wordsArray.push(commandChildName); if (commandChildObject.commands) { wordsArray = getCollectCommandWords(commandChildObject.commands, wordsArray);