UNPKG

serverless

Version:

Serverless Framework - Build web, mobile and IoT applications with serverless architectures using AWS Lambda, Azure Functions, Google CloudFunctions & more

173 lines (157 loc) 6.58 kB
'use strict'; const _ = require('lodash'); class AwsCompileSQSEvents { constructor(serverless) { this.serverless = serverless; this.provider = this.serverless.getProvider('aws'); this.hooks = { 'package:compileEvents': this.compileSQSEvents.bind(this), }; } compileSQSEvents() { this.serverless.service.getAllFunctions().forEach((functionName) => { const functionObj = this.serverless.service.getFunction(functionName); if (functionObj.events) { const sqsStatement = { Effect: 'Allow', Action: [ 'sqs:ReceiveMessage', 'sqs:DeleteMessage', 'sqs:GetQueueAttributes', ], Resource: [], }; functionObj.events.forEach(event => { if (event.sqs) { let EventSourceArn; let BatchSize = 10; let Enabled = 'True'; // TODO validate arn syntax if (typeof event.sqs === 'object') { if (!event.sqs.arn) { const errorMessage = [ `Missing "arn" property for sqs event in function "${functionName}"`, ' The correct syntax is: sqs: <QueueArn>', ' OR an object with an "arn" property.', ' Please check the docs for more info.', ].join(''); throw new this.serverless.classes .Error(errorMessage); } if (typeof event.sqs.arn !== 'string') { // for dynamic arns (GetAtt/ImportValue) if (Object.keys(event.sqs.arn).length !== 1 || !(_.has(event.sqs.arn, 'Fn::ImportValue') || _.has(event.sqs.arn, 'Fn::GetAtt'))) { const errorMessage = [ `Bad dynamic ARN property on sqs event in function "${functionName}"`, ' If you use a dynamic "arn" (such as with Fn::GetAtt or Fn::ImportValue)', ' there must only be one key (either Fn::GetAtt or Fn::ImportValue) in the arn', ' object. Please check the docs for more info.', ].join(''); throw new this.serverless.classes .Error(errorMessage); } } EventSourceArn = event.sqs.arn; BatchSize = event.sqs.batchSize || BatchSize; if (typeof event.sqs.enabled !== 'undefined') { Enabled = event.sqs.enabled ? 'True' : 'False'; } } else if (typeof event.sqs === 'string') { EventSourceArn = event.sqs; } else { const errorMessage = [ `SQS event of function "${functionName}" is not an object nor a string`, ' The correct syntax is: sqs: <QueueArn>', ' OR an object with an "arn" property.', ' Please check the docs for more info.', ].join(''); throw new this.serverless.classes .Error(errorMessage); } const queueName = (function () { if (EventSourceArn['Fn::GetAtt']) { return EventSourceArn['Fn::GetAtt'][0]; } else if (EventSourceArn['Fn::ImportValue']) { return EventSourceArn['Fn::ImportValue']; } return EventSourceArn.split(':').pop(); }()); const lambdaLogicalId = this.provider.naming .getLambdaLogicalId(functionName); const queueLogicalId = this.provider.naming .getQueueLogicalId(functionName, queueName); const funcRole = functionObj.role || this.serverless.service.provider.role; let dependsOn = '"IamRoleLambdaExecution"'; if (funcRole) { if ( // check whether the custom role is an ARN typeof funcRole === 'string' && funcRole.indexOf(':') !== -1 ) { dependsOn = '[]'; } else if ( // otherwise, check if we have an in-service reference to a role ARN typeof funcRole === 'object' && 'Fn::GetAtt' in funcRole && Array.isArray(funcRole['Fn::GetAtt']) && funcRole['Fn::GetAtt'].length === 2 && typeof funcRole['Fn::GetAtt'][0] === 'string' && typeof funcRole['Fn::GetAtt'][1] === 'string' && funcRole['Fn::GetAtt'][1] === 'Arn' ) { dependsOn = `"${funcRole['Fn::GetAtt'][0]}"`; } else if ( // otherwise, check if we have an import typeof funcRole === 'object' && 'Fn::ImportValue' in funcRole ) { dependsOn = '[]'; } else if (typeof funcRole === 'string') { dependsOn = `"${funcRole}"`; } } const sqsTemplate = ` { "Type": "AWS::Lambda::EventSourceMapping", "DependsOn": ${dependsOn}, "Properties": { "BatchSize": ${BatchSize}, "EventSourceArn": ${JSON.stringify(EventSourceArn)}, "FunctionName": { "Fn::GetAtt": [ "${lambdaLogicalId}", "Arn" ] }, "Enabled": "${Enabled}" } } `; // add event source ARNs to PolicyDocument statements sqsStatement.Resource.push(EventSourceArn); const newSQSObject = { [queueLogicalId]: JSON.parse(sqsTemplate), }; _.merge(this.serverless.service.provider.compiledCloudFormationTemplate.Resources, newSQSObject); } }); // update the PolicyDocument statements (if default policy is used) if (this.serverless.service.provider.compiledCloudFormationTemplate .Resources.IamRoleLambdaExecution) { const statement = this.serverless.service.provider.compiledCloudFormationTemplate .Resources .IamRoleLambdaExecution .Properties .Policies[0] .PolicyDocument .Statement; if (sqsStatement.Resource.length) { statement.push(sqsStatement); } } } }); } } module.exports = AwsCompileSQSEvents;