Skip to content

Commit

Permalink
feat(events-targets): LambdaFunction (aws#2350)
Browse files Browse the repository at this point in the history
The `LambdaFunction` class can be used to bind an AWS Lambda function as an event
rule target.

Related aws#1663 

BREAKING CHANGE: `lambda.Function` no longer implements `IEventRuleTarget`. Instead, use
`@aws-cdk/aws-events-targets.LambdaFunction`.
  • Loading branch information
Elad Ben-Israel authored and SanderKnape committed May 14, 2019
1 parent 33b67f2 commit 6850a0b
Show file tree
Hide file tree
Showing 9 changed files with 117 additions and 79 deletions.
3 changes: 2 additions & 1 deletion packages/@aws-cdk/aws-events-targets/lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './sns';
export * from './codebuild';
export * from './codebuild';
export * from './lambda';
36 changes: 36 additions & 0 deletions packages/@aws-cdk/aws-events-targets/lib/lambda.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import events = require('@aws-cdk/aws-events');
import iam = require('@aws-cdk/aws-iam');
import lambda = require('@aws-cdk/aws-lambda');

/**
* Use an AWS Lambda function as an event rule target.
*/
export class LambdaFunction implements events.IEventRuleTarget {

/**
* @param handler The lambda function
*/
constructor(private readonly handler: lambda.IFunction) {

}

/**
* Returns a RuleTarget that can be used to trigger this Lambda as a
* result from a CloudWatch event.
*/
public asEventRuleTarget(ruleArn: string, ruleId: string): events.EventRuleTargetProps {
const permissionId = `AllowEventRule${ruleId}`;
if (!this.handler.node.tryFindChild(permissionId)) {
this.handler.addPermission(permissionId, {
action: 'lambda:InvokeFunction',
principal: new iam.ServicePrincipal('events.amazonaws.com'),
sourceArn: ruleArn
});
}

return {
id: this.handler.node.id,
arn: this.handler.functionArn,
};
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import events = require('@aws-cdk/aws-events');
import lambda = require('@aws-cdk/aws-lambda');
import cdk = require('@aws-cdk/cdk');
import lambda = require('../lib');
import targets = require('../../lib');

const app = new cdk.App();

Expand All @@ -13,10 +14,10 @@ const fn = new lambda.Function(stack, 'MyFunc', {
});

const timer = new events.EventRule(stack, 'Timer', { scheduleExpression: 'rate(1 minute)' });
timer.addTarget(fn);
timer.addTarget(new targets.LambdaFunction(fn));

const timer2 = new events.EventRule(stack, 'Timer2', { scheduleExpression: 'rate(2 minutes)' });
timer2.addTarget(fn);
timer2.addTarget(new targets.LambdaFunction(fn));

app.run();

Expand Down
62 changes: 62 additions & 0 deletions packages/@aws-cdk/aws-events-targets/test/lambda/lambda.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { countResources, expect, haveResource } from '@aws-cdk/assert';
import events = require('@aws-cdk/aws-events');
import lambda = require('@aws-cdk/aws-lambda');
import cdk = require('@aws-cdk/cdk');
import targets = require('../../lib');

test('use lambda as an event rule target', () => {
// GIVEN
const stack = new cdk.Stack();
const fn = newTestLambda(stack);
const rule1 = new events.EventRule(stack, 'Rule', { scheduleExpression: 'rate(1 minute)' });
const rule2 = new events.EventRule(stack, 'Rule2', { scheduleExpression: 'rate(5 minutes)' });

// WHEN
rule1.addTarget(new targets.LambdaFunction(fn));
rule2.addTarget(new targets.LambdaFunction(fn));

// THEN
const lambdaId = "MyLambdaCCE802FB";

expect(stack).to(haveResource('AWS::Lambda::Permission', {
Action: "lambda:InvokeFunction",
FunctionName: {
"Fn::GetAtt": [
lambdaId,
"Arn"
]
},
Principal: "events.amazonaws.com",
SourceArn: { "Fn::GetAtt": ["Rule4C995B7F", "Arn"] }
}));

expect(stack).to(haveResource('AWS::Lambda::Permission', {
Action: "lambda:InvokeFunction",
FunctionName: {
"Fn::GetAtt": [
lambdaId,
"Arn"
]
},
Principal: "events.amazonaws.com",
SourceArn: { "Fn::GetAtt": ["Rule270732244", "Arn"] }
}));

expect(stack).to(countResources('AWS::Events::Rule', 2));
expect(stack).to(haveResource('AWS::Events::Rule', {
Targets: [
{
Arn: { "Fn::GetAtt": [lambdaId, "Arn"] },
Id: "MyLambda"
}
]
}));
});

function newTestLambda(scope: cdk.Construct) {
return new lambda.Function(scope, 'MyLambda', {
code: new lambda.InlineCode('foo'),
handler: 'bar',
runtime: lambda.Runtime.Python27
});
}
1 change: 1 addition & 0 deletions packages/@aws-cdk/aws-events/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,5 @@ The following targets are supported:

* `targets.SnsTopic`: publish into an SNS topic when an event rule is triggered.
* `targets.CodeBuildProject`: start a CodeBuild project when an event rule is triggered.
* `targets.LambdaFunction`: invoke an AWS Lambda function when an event rule is triggered.

10 changes: 10 additions & 0 deletions packages/@aws-cdk/aws-lambda/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,16 @@ granting permissions to other AWS accounts or organizations.

[Example of Lambda Layer usage](test/integ.layer-version.lit.ts)

## Event Rule Target

You can use an AWS Lambda function as a target for an Amazon CloudWatch event
rule:

```ts
import targets = require('@aws-cdk/aws-events-targets');
rule.addTarget(new targets.LambdaFunction(myFunction));
```

### Event Sources

AWS Lambda supports a [variety of event sources](https://docs.aws.amazon.com/lambda/latest/dg/invoking-lambda-function.html).
Expand Down
23 changes: 1 addition & 22 deletions packages/@aws-cdk/aws-lambda/lib/function-base.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import cloudwatch = require('@aws-cdk/aws-cloudwatch');
import ec2 = require('@aws-cdk/aws-ec2');
import events = require('@aws-cdk/aws-events');
import iam = require('@aws-cdk/aws-iam');
import logs = require('@aws-cdk/aws-logs');
import s3n = require('@aws-cdk/aws-s3-notifications');
Expand All @@ -11,7 +10,7 @@ import { IEventSource } from './event-source';
import { CfnPermission } from './lambda.generated';
import { Permission } from './permission';

export interface IFunction extends IResource, events.IEventRuleTarget, logs.ILogSubscriptionDestination,
export interface IFunction extends IResource, logs.ILogSubscriptionDestination,
s3n.IBucketNotificationDestination, ec2.IConnectable, stepfunctions.IStepFunctionsTaskResource, iam.IGrantable {

/**
Expand Down Expand Up @@ -215,26 +214,6 @@ export abstract class FunctionBase extends Resource implements IFunction {
return !!this._connections;
}

/**
* Returns a RuleTarget that can be used to trigger this Lambda as a
* result from a CloudWatch event.
*/
public asEventRuleTarget(ruleArn: string, ruleId: string): events.EventRuleTargetProps {
const permissionId = `AllowEventRule${ruleId}`;
if (!this.node.tryFindChild(permissionId)) {
this.addPermission(permissionId, {
action: 'lambda:InvokeFunction',
principal: new iam.ServicePrincipal('events.amazonaws.com'),
sourceArn: ruleArn
});
}

return {
id: this.node.id,
arn: this.functionArn,
};
}

/**
* Grant the given identity permissions to invoke this Lambda
*/
Expand Down
54 changes: 1 addition & 53 deletions packages/@aws-cdk/aws-lambda/test/test.lambda.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { countResources, expect, haveResource, MatchStyle, ResourcePart } from '@aws-cdk/assert';
import events = require('@aws-cdk/aws-events');
import { expect, haveResource, MatchStyle, ResourcePart } from '@aws-cdk/assert';
import iam = require('@aws-cdk/aws-iam');
import logs = require('@aws-cdk/aws-logs');
import sqs = require('@aws-cdk/aws-sqs');
Expand Down Expand Up @@ -257,57 +256,6 @@ export = {
},
},

'Lambda can serve as EventRule target, permission gets added'(test: Test) {
// GIVEN
const stack = new cdk.Stack();
const fn = newTestLambda(stack);
const rule1 = new events.EventRule(stack, 'Rule', { scheduleExpression: 'rate(1 minute)' });
const rule2 = new events.EventRule(stack, 'Rule2', { scheduleExpression: 'rate(5 minutes)' });

// WHEN
rule1.addTarget(fn);
rule2.addTarget(fn);

// THEN
const lambdaId = "MyLambdaCCE802FB";

expect(stack).to(haveResource('AWS::Lambda::Permission', {
"Action": "lambda:InvokeFunction",
"FunctionName": {
"Fn::GetAtt": [
lambdaId,
"Arn"
]
},
"Principal": "events.amazonaws.com",
"SourceArn": { "Fn::GetAtt": [ "Rule4C995B7F", "Arn" ] }
}));

expect(stack).to(haveResource('AWS::Lambda::Permission', {
"Action": "lambda:InvokeFunction",
"FunctionName": {
"Fn::GetAtt": [
lambdaId,
"Arn"
]
},
"Principal": "events.amazonaws.com",
"SourceArn": { "Fn::GetAtt": [ "Rule270732244", "Arn" ] }
}));

expect(stack).to(countResources('AWS::Events::Rule', 2));
expect(stack).to(haveResource('AWS::Events::Rule', {
"Targets": [
{
"Arn": { "Fn::GetAtt": [ lambdaId, "Arn" ] },
"Id": "MyLambda"
}
]
}));

test.done();
},

'Lambda code can be read from a local directory via an asset'(test: Test) {
// GIVEN
const stack = new cdk.Stack();
Expand Down

0 comments on commit 6850a0b

Please sign in to comment.