From e515658bd8260536cd29d098078345ab18b66bbe Mon Sep 17 00:00:00 2001 From: Lars Jacobsson Date: Sun, 11 Oct 2020 21:26:36 +0200 Subject: [PATCH] feat: evb extract-sam-event --- README.md | 4 ++ index.js | 26 ++++++++---- package.json | 2 +- src/template-parser.js | 89 +++++++++++++++++++++++++++++++++++++++--- 4 files changed, 106 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 54786cb..dfe2d81 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,10 @@ Options: This is an experimental feature. Grouping by tag is possible for the following target types: Lambda, StepFunctions, SNS, SQS, Kinesis. More will follow. +## Extract `AWS::Serverless::Function` Event to `AWS::Events::Rule` +Sometimes you start off with a simple [EventBridgeRule](https://github.com/aws/serverless-application-model/blob/master/versions/2016-10-31.md#eventbridgerule) transform on you `AWS::Serverless::Function` resource. Later on you might want to evolve it and start using an [InputTransformer](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-events-rule-inputtransformer.html) or retry/DLQ configurations which is only supported by `AWS::Events::Rule` + +Use `evb extract-sam-event` to unfold the SAM event to full CloudFormation syntax. ## Local debugging Local debugging makes use to API Gateway V2 websockets to forward actual events in the cloud to your developer machine. The requires a [Serverless Application Repository app](https://serverlessrepo.aws.amazon.com/applications/eu-west-1/751354400372/evb-local) to be installed in your account. Note that depending on your traffic, there will be some small effect on your billing in the form of Lambda invocations, API Gateway invocations, CloudWatch Logs and DynamoDB R/W. diff --git a/index.js b/index.js index 00f6d5e..b63e9d5 100755 --- a/index.js +++ b/index.js @@ -55,7 +55,7 @@ program await patternBuilder.browseEvents(cmd.format, schemaApi, evbApi); }); -program + program .command("diagram") .alias("d") .option( @@ -71,15 +71,25 @@ program await diagramBuilder.build(cmd.eventbus); }); + program + .command("extract-sam-event") + .alias("e") + .option("-t, --template [template]", "Path to template file", "template.yml") + .description("Exctracts an EventBusRule event from an AWS::Serverless::Function resource to an AWS::Events::Rule for more advanced use cases") + .action(async (cmd) => { + templateParser.load(cmd.template); + await templateParser.extractSamDefinition(); + }); + const ruleDefault = "choose from template"; program - .command("local") - .alias("l") - .option( - "-s, --stack-name [stackName]", - "Establishes local consumption of all rules in a stack" - ) - .option("--arn [arn]", "Establishes local consumption of a rule ARN") +.command("local") +.alias("l") +.option( + "-s, --stack-name [stackName]", + "Establishes local consumption of all rules in a stack" +) +.option("--arn [arn]", "Establishes local consumption of a rule ARN") .option( "-r, --rule [rule]", "Establishes local consumption of a rule in a local CloudFormation template", diff --git a/package.json b/package.json index 932ff7b..12fcf1a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@mhlabs/evb-cli", - "version": "1.1.13", + "version": "1.1.14", "description": "A package for building EventBridge/CloudWatch Events patterns", "main": "index.js", "scripts": { diff --git a/src/template-parser.js b/src/template-parser.js index 5c09a77..ec6ca30 100644 --- a/src/template-parser.js +++ b/src/template-parser.js @@ -94,13 +94,89 @@ async function injectPattern(pattern) { resource.value.Properties.EventPattern = pattern; resource.value.Properties.EventBusName = eventBus; template.Resources[resource.name] = resource.value; - fs.writeFileSync( - templatePath, - format === "json" - ? JSON.stringify(template, null, 2) - : YAML.stringify(template) - ); + saveTemplate(); + } +} + +function saveTemplate() { + fs.writeFileSync( + templatePath, + format === "json" + ? JSON.stringify(template, null, 2) + : YAML.stringify(template) + ); +} + +async function extractSamDefinition() { + const events = []; + Object.keys(template.Resources).filter( + (f) => + template.Resources[f].Type === "AWS::Serverless::Function" && + template.Resources[f].Properties.Events && + Object.keys(template.Resources[f].Properties.Events).forEach((e) => { + if ( + template.Resources[f].Properties.Events[e].Type === + "EventBridgeRule" || + template.Resources[f].Properties.Events[e].Type === "CloudWatchEvent" + ) { + events.push({ + name: `${e} -> ${f}`, + value: { + function: f, + event: e, + config: template.Resources[f].Properties.Events[e].Properties, + }, + }); + } + }) + ); + + const extractEvent = await inputUtil.selectFrom( + events, + "Select SAM event to extract" + ); + + delete template.Resources[extractEvent.function].Properties.Events[extractEvent.event]; + + + let suggestion = `${extractEvent.event}Rule`; + if (template.Resources[suggestion]) { + suggestion = `${extractEvent.event}To${extractEvent.function}Rule`; } + const resourceName = await inputUtil.text("Resource name", suggestion); + + template.Resources[resourceName] = { + Type: "AWS::Events::Rule", + Properties: { + EventBusName: extractEvent.config.EventBusName || "default", + EventPattern: extractEvent.config.Pattern, + State: "ENABLED", + Targets: [{ + Arn: { "Fn::GetAtt": [extractEvent.function, "Arn"] }, + Id: suggestion, + Input: extractEvent.config.Input, + InputPath: extractEvent.config.InputPath + }], + }, + }; + template.Resources[`${resourceName}Permission`] = { + Type: "AWS::Lambda::Permission", + Properties: { + FunctionName: { + Ref: extractEvent.function + }, + Action: "lambda:InvokeFunction", + Principal: "events.amazonaws.com", + SourceArn: { + "Fn::GetAtt": [ + suggestion, + "Arn" + ] + } + } + }; + + saveTemplate(); } module.exports = { @@ -108,4 +184,5 @@ module.exports = { getLambdaFunctions, load, injectPattern, + extractSamDefinition, };