Skip to content

Commit

Permalink
feat(scheduler-targets): eventBridge putEvents target (#27629)
Browse files Browse the repository at this point in the history
An eventBridgePutEvents target was implemented similar to the already existing LambdaInvoke/StepFunctionStartExecution target.

I needed to change some properties and methods from Target.ts from private to protected so that the logic could be reused (hope that is ok).

Some design choices to outline (let me know if you disagree or have improvements I could take :) ):
1. PutEvents would accept multiple Entries (eg. an array), but I decided to support just one single event, because how Target is currently designed (to support only one target arn). It also aligns with the templated integration in the aws management console.
2. It throws an error in the constructor if the base prop `input` is used. All the props should be delivered by the new `EventBridgePutEventsEntry`. It felt not right for the developer experience to split the object (detail to `input` and `source`, `detailType` to `EventBridgePutEventsEntry` ).


Closes #27454.

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
WtfJoke committed Nov 29, 2023
1 parent 1a586f2 commit cd12ce4
Show file tree
Hide file tree
Showing 15 changed files with 36,058 additions and 3 deletions.
28 changes: 28 additions & 0 deletions packages/@aws-cdk/aws-scheduler-targets-alpha/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ The following targets are supported:
3. `targets.CodeBuildStartBuild`: [Start a CodeBuild job](#start-a-codebuild-job)
4. `targets.SqsSendMessage`: [Send a Message to an Amazon SQS Queue](#send-a-message-to-sqs-queue)
5. `targets.SnsPublish`: [Publish messages to an Amazon SNS topic](#publish-messages-to-an-amazon-sns-topic)
6. `targets.EventBridgePutEvents`: [Put Events on EventBridge](#send-events-to-an-eventbridge-event-bus)

## Invoke a Lambda function

Expand Down Expand Up @@ -178,3 +179,30 @@ new Schedule(this, 'Schedule', {
target,
});
```

## Send events to an EventBridge event bus

Use the `EventBridgePutEvents` target to send events to an EventBridge event bus.

The code snippet below creates an event rule with an EventBridge event bus as a target
called every hour by Event Bridge Scheduler with a custom event payload.

```ts
import * as events from 'aws-cdk-lib/aws-events';

const eventBus = new events.EventBus(this, 'EventBus', {
eventBusName: 'DomainEvents',
});

const eventEntry: targets.EventBridgePutEventsEntry = {
eventBus,
source: 'PetService',
detail: ScheduleTargetInput.fromObject({ Name: 'Fluffy' }),
detailType: '🐶',
};

new Schedule(this, 'Schedule', {
schedule: ScheduleExpression.rate(Duration.hours(1)),
target: new targets.EventBridgePutEvents(eventEntry, {}),
});
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { IScheduleTarget, ISchedule, ScheduleTargetInput, ScheduleTargetConfig } from '@aws-cdk/aws-scheduler-alpha';
import { Names } from 'aws-cdk-lib';
import * as events from 'aws-cdk-lib/aws-events';
import { IRole } from 'aws-cdk-lib/aws-iam';
import { ScheduleTargetBase, ScheduleTargetBaseProps } from './target';
import { sameEnvDimension } from './util';

/**
* An entry to be sent to EventBridge
*
* @see https://docs.aws.amazon.com/eventbridge/latest/APIReference/API_PutEventsRequestEntry.html
*/
export interface EventBridgePutEventsEntry {
/**
* The event body
*
* Can either be provided as an object or as a JSON-serialized string
* @example
*
* ScheduleTargetInput.fromText('{"instance-id": "i-1234567890abcdef0", "state": "terminated"}');
* ScheduleTargetInput.fromObject({ Message: 'Hello from a friendly event :)' });
*/
readonly detail: ScheduleTargetInput;

/**
* Used along with the source field to help identify the fields and values expected in the detail field
*
* For example, events by CloudTrail have detail type "AWS API Call via CloudTrail"
* @see https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-events.html
*/
readonly detailType: string;

/**
* The event bus the entry will be sent to.
*
*/
readonly eventBus: events.IEventBus;

/**
* The service or application that caused this event to be generated
*
* Example value: `com.example.service`
*
* @see https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-events.html
*/
readonly source: string;
}

/**
* Send an event to an AWS EventBridge by AWS EventBridge Scheduler.
*/
export class EventBridgePutEvents extends ScheduleTargetBase implements IScheduleTarget {
constructor(
private readonly entry: EventBridgePutEventsEntry,
private readonly props: ScheduleTargetBaseProps,
) {
super(props, entry.eventBus.eventBusArn);
if (this.props.input) {
throw new Error('ScheduleTargetBaseProps.input is not supported for EventBridgePutEvents. Please use entry.detail instead.');
}
}

protected addTargetActionToRole(schedule: ISchedule, role: IRole): void {
const eventBus = this.entry.eventBus;
const eventBusEnv = eventBus.env;
if (!sameEnvDimension(eventBusEnv.region, schedule.env.region)) {
throw new Error(`Cannot assign eventBus in region ${eventBusEnv.region} to the schedule ${Names.nodeUniqueId(schedule.node)} in region ${schedule.env.region}. Both the schedule and the eventBus must be in the same region.`);
}

if (!sameEnvDimension(eventBusEnv.account, schedule.env.account)) {
throw new Error(`Cannot assign eventBus in account ${eventBusEnv.account} to the schedule ${Names.nodeUniqueId(schedule.node)} in account ${schedule.env.region}. Both the schedule and the eventBus must be in the same account.`);
}

if (this.props.role && !sameEnvDimension(this.props.role.env.account, eventBusEnv.account)) {
throw new Error(`Cannot grant permission to execution role in account ${this.props.role.env.account} to invoke target ${Names.nodeUniqueId(eventBus.node)} in account ${eventBusEnv.account}. Both the target and the execution role must be in the same account.`);
}

eventBus.grantPutEventsTo(role);
}

protected bindBaseTargetConfig(_schedule: ISchedule): ScheduleTargetConfig {
return {
...super.bindBaseTargetConfig(_schedule),
input: this.entry.detail,
eventBridgeParameters: {
detailType: this.entry.detailType,
source: this.entry.source,
},
};
}
}
5 changes: 3 additions & 2 deletions packages/@aws-cdk/aws-scheduler-targets-alpha/lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export * from './codebuild-start-build';
export * from './event-bridge-put-events';
export * from './target';
export * from './lambda-invoke';
export * from './sns-publish';
export * from './sqs-send-message';
export * from './stepfunctions-start-execution';
export * from './target';
export * from './sqs-send-message';
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ export abstract class ScheduleTargetBase {
/**
* Create a return a Schedule Target Configuration for the given schedule
* @param schedule
* @returnn
* @returns a Schedule Target Configuration
*/
bind(schedule: ISchedule): ScheduleTargetConfig {
return this.bindBaseTargetConfig(schedule);
Expand Down
Loading

0 comments on commit cd12ce4

Please sign in to comment.