From a114a2349c3d0a042a9bcea71911badd7c499373 Mon Sep 17 00:00:00 2001 From: DaWyz Date: Sun, 13 Dec 2020 20:32:38 -0800 Subject: [PATCH 1/5] feat(aws-events): Add ability to archive events --- packages/@aws-cdk/aws-events/lib/archive.ts | 71 ++++++++++ packages/@aws-cdk/aws-events/lib/event-bus.ts | 97 +++++++++++--- packages/@aws-cdk/aws-events/lib/index.ts | 1 + .../@aws-cdk/aws-events/test/test.archive.ts | 45 +++++++ .../aws-events/test/test.event-bus.ts | 121 ++++++++++++++++++ 5 files changed, 318 insertions(+), 17 deletions(-) create mode 100644 packages/@aws-cdk/aws-events/lib/archive.ts create mode 100644 packages/@aws-cdk/aws-events/test/test.archive.ts diff --git a/packages/@aws-cdk/aws-events/lib/archive.ts b/packages/@aws-cdk/aws-events/lib/archive.ts new file mode 100644 index 0000000000000..033424274db25 --- /dev/null +++ b/packages/@aws-cdk/aws-events/lib/archive.ts @@ -0,0 +1,71 @@ +import { Duration, Resource } from '@aws-cdk/core'; +import { Construct } from 'constructs'; +import { IEventBus } from './event-bus'; +import { EventPattern } from './event-pattern'; +import { CfnArchive } from './events.generated'; + +/** + * The event archive properties + */ +export interface ArchiveProps { + /** + * The name of the archive. + * + * @default - none + */ + readonly archiveName?: string; + /** + * A description for the archive. + * + * @default - none + */ + readonly description?: string; + /** + * An event pattern to use to filter events sent to the archive. + */ + readonly eventPattern: EventPattern; + /** + * The number of days to retain events for. Default value is 0. If set to 0, events are retained indefinitely. + * @default - Infinite + */ + readonly retention?: Duration; + /** + * The event source associated with the archive. + */ + readonly sourceEventBus: IEventBus; +} + +/** + * Define an EventBridge Archive + * + * @resource AWS::Events::Archive + */ +export class Archive extends Resource { + /** + * The archive name. + * @attribute + */ + public readonly archiveName: string; + + /** + * The ARN of the archive created. + * @attribute + */ + public readonly archiveArn: string; + + constructor(scope: Construct, id: string, props: ArchiveProps) { + super(scope, id, { physicalName: props.archiveName }); + + let archive = new CfnArchive(this, 'Archive', { + sourceArn: props.sourceEventBus.eventBusArn, + description: props.description, + eventPattern: props.eventPattern, + retentionDays: props.retention?.toDays() || 0, + }); + + archive.addPropertyOverride('archiveName', this.physicalName); + + this.archiveArn = archive.attrArn; + this.archiveName = archive.attrArchiveName; + } +} diff --git a/packages/@aws-cdk/aws-events/lib/event-bus.ts b/packages/@aws-cdk/aws-events/lib/event-bus.ts index 27c79c9c7fe3a..4b89bb109dbf0 100644 --- a/packages/@aws-cdk/aws-events/lib/event-bus.ts +++ b/packages/@aws-cdk/aws-events/lib/event-bus.ts @@ -1,6 +1,8 @@ import * as iam from '@aws-cdk/aws-iam'; -import { IResource, Lazy, Names, Resource, Stack, Token } from '@aws-cdk/core'; +import { Duration, IResource, Lazy, Names, Resource, Stack, Token } from '@aws-cdk/core'; import { Construct } from 'constructs'; +import { Archive } from './archive'; +import { EventPattern } from './event-pattern'; import { CfnEventBus } from './events.generated'; /** @@ -37,6 +39,23 @@ export interface IEventBus extends IResource { * @link https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-events-eventbus.html#cfn-events-eventbus-eventsourcename */ readonly eventSourceName?: string; + + /** + * Create an EventBridge archive to send events to. + * When you create an archive, incoming events might not immediately start being sent to the archive. + * Allow a short period of time for changes to take effect. + * + * @param eventPattern An event pattern to use to filter events sent to the archive. + * @param archiveName The name of the archive. + * @param description A description for the archive. + * @param retention The number of days to retain events for. Default value is 0. If set to 0, events are retained indefinitely. + */ + archive( + eventPattern: EventPattern, + archiveName?: string, + description?: string, + retention?: Duration, + ): Archive; } /** @@ -96,12 +115,50 @@ export interface EventBusAttributes { readonly eventSourceName?: string; } +abstract class EventBusBase extends Resource implements IEventBus { + /** + * The physical ID of this event bus resource + */ + public abstract readonly eventBusName: string; + + /** + * The ARN of the event bus, such as: + * arn:aws:events:us-east-2:123456789012:event-bus/aws.partner/PartnerName/acct1/repo1. + */ + public abstract readonly eventBusArn: string; + + /** + * The policy for the event bus in JSON form. + */ + public abstract readonly eventBusPolicy: string; + + /** + * The name of the partner event source + */ + public abstract readonly eventSourceName?: string; + + public archive( + eventPattern: EventPattern, + archiveName?: string, + description?: string, + retention?: Duration, + ): Archive { + return new Archive(this, 'Archive', { + sourceEventBus: this, + description: description || Lazy.string({ produce: () => `Event Archive for ${this.eventBusName} Event Bus` }), + eventPattern: eventPattern, + retention: retention, + archiveName: archiveName, + }); + } +} + /** * Define an EventBridge EventBus * * @resource AWS::Events::EventBus */ -export class EventBus extends Resource implements IEventBus { +export class EventBus extends EventBusBase { /** * Import an existing event bus resource @@ -112,13 +169,11 @@ export class EventBus extends Resource implements IEventBus { public static fromEventBusArn(scope: Construct, id: string, eventBusArn: string): IEventBus { const parts = Stack.of(scope).parseArn(eventBusArn); - class Import extends Resource implements IEventBus { - public readonly eventBusArn = eventBusArn; - public readonly eventBusName = parts.resourceName || ''; - public readonly eventBusPolicy = ''; - } - - return new Import(scope, id); + return new ImportedEventBus(scope, id, { + eventBusArn: eventBusArn, + eventBusName: parts.resourceName || '', + eventBusPolicy: '', + }); } /** @@ -128,14 +183,7 @@ export class EventBus extends Resource implements IEventBus { * @param attrs Imported event bus properties */ public static fromEventBusAttributes(scope: Construct, id: string, attrs: EventBusAttributes): IEventBus { - class Import extends Resource implements IEventBus { - public readonly eventBusArn = attrs.eventBusArn; - public readonly eventBusName = attrs.eventBusName; - public readonly eventBusPolicy = attrs.eventBusPolicy; - public readonly eventSourceName = attrs.eventSourceName; - } - - return new Import(scope, id); + return new ImportedEventBus(scope, id, attrs); } /** @@ -241,3 +289,18 @@ export class EventBus extends Resource implements IEventBus { this.eventSourceName = eventBus.eventSourceName; } } + +class ImportedEventBus extends EventBusBase { + public readonly eventBusArn: string; + public readonly eventBusName: string; + public readonly eventBusPolicy: string; + public readonly eventSourceName?: string; + constructor(scope: Construct, id: string, attrs: EventBusAttributes) { + super(scope, id); + + this.eventBusArn = attrs.eventBusArn; + this.eventBusName = attrs.eventBusName; + this.eventBusPolicy = attrs.eventBusPolicy; + this.eventSourceName = attrs.eventSourceName; + } +} diff --git a/packages/@aws-cdk/aws-events/lib/index.ts b/packages/@aws-cdk/aws-events/lib/index.ts index e0aa655afaf72..718b236bf6e91 100644 --- a/packages/@aws-cdk/aws-events/lib/index.ts +++ b/packages/@aws-cdk/aws-events/lib/index.ts @@ -6,6 +6,7 @@ export * from './event-bus'; export * from './event-pattern'; export * from './schedule'; export * from './on-event-options'; +export * from './archive'; // AWS::Events CloudFormation Resources: export * from './events.generated'; diff --git a/packages/@aws-cdk/aws-events/test/test.archive.ts b/packages/@aws-cdk/aws-events/test/test.archive.ts new file mode 100644 index 0000000000000..fc8f38a516f2b --- /dev/null +++ b/packages/@aws-cdk/aws-events/test/test.archive.ts @@ -0,0 +1,45 @@ +import { expect, haveResource } from '@aws-cdk/assert'; +import { Duration, Stack } from '@aws-cdk/core'; +import { Test } from 'nodeunit'; +import { EventBus } from '../lib'; +import { Archive } from '../lib/archive'; + +export = { + 'creates an archive for an EventBus'(test: Test) { + // GIVEN + const stack = new Stack(); + + // WHEN + let eventBus = new EventBus(stack, 'Bus'); + + new Archive(stack, 'Archive', { + sourceEventBus: eventBus, + eventPattern: { + account: [stack.account], + }, + retention: Duration.days(10), + }); + + // THEN + expect(stack).to(haveResource('AWS::Events::EventBus', { + Name: 'Bus', + })); + + expect(stack).to(haveResource('AWS::Events::Archive', { + EventPattern: { + account: [{ + Ref: 'AWS::AccountId', + }], + }, + RetentionDays: 10, + SourceArn: { + 'Fn::GetAtt': [ + 'BusEA82B648', + 'Arn', + ], + }, + })); + + test.done(); + }, +} diff --git a/packages/@aws-cdk/aws-events/test/test.event-bus.ts b/packages/@aws-cdk/aws-events/test/test.event-bus.ts index a6c32885ec4a9..40140cb4de1f7 100644 --- a/packages/@aws-cdk/aws-events/test/test.event-bus.ts +++ b/packages/@aws-cdk/aws-events/test/test.event-bus.ts @@ -245,6 +245,127 @@ export = { ], })); + test.done(); + }, + 'can archive events'(test: Test) { + // GIVEN + const stack = new Stack(); + + // WHEN + const event = new EventBus(stack, 'Bus'); + + event.archive({ + account: [stack.account], + }, 'MyArchive'); + + // THEN + expect(stack).to(haveResource('AWS::Events::EventBus', { + Name: 'Bus', + })); + + expect(stack).to(haveResource('AWS::Events::Archive', { + SourceArn: { + 'Fn::GetAtt': [ + 'BusEA82B648', + 'Arn', + ], + }, + Description: { + 'Fn::Join': [ + '', + [ + 'Event Archive for ', + { + Ref: 'BusEA82B648', + }, + ' Event Bus', + ], + ], + }, + EventPattern: { + account: [ + { + Ref: 'AWS::AccountId', + }, + ], + }, + RetentionDays: 0, + archiveName: 'MyArchive', + })); + + test.done(); + }, + 'can archive events from an imported EventBus'(test: Test) { + // GIVEN + const stack = new Stack(); + + // WHEN + const bus = new EventBus(stack, 'Bus'); + + const importedBus = EventBus.fromEventBusArn(stack, 'ImportedBus', bus.eventBusArn); + + importedBus.archive({ + account: [stack.account], + }, 'MyArchive'); + + // THEN + expect(stack).to(haveResource('AWS::Events::EventBus', { + Name: 'Bus', + })); + + expect(stack).to(haveResource('AWS::Events::Archive', { + SourceArn: { + 'Fn::GetAtt': [ + 'BusEA82B648', + 'Arn', + ], + }, + Description: { + 'Fn::Join': [ + '', + [ + 'Event Archive for ', + { + 'Fn::Select': [ + 1, + { + 'Fn::Split': [ + '/', + { + 'Fn::Select': [ + 5, + { + 'Fn::Split': [ + ':', + { + 'Fn::GetAtt': [ + 'BusEA82B648', + 'Arn', + ], + }, + ], + }, + ], + }, + ], + }, + ], + }, + ' Event Bus', + ], + ], + }, + EventPattern: { + account: [ + { + Ref: 'AWS::AccountId', + }, + ], + }, + RetentionDays: 0, + archiveName: 'MyArchive', + })); + test.done(); }, }; From f2df4449444d7e2e775e6e240d00472e685a7fee Mon Sep 17 00:00:00 2001 From: DaWyz Date: Mon, 14 Dec 2020 21:30:21 -0800 Subject: [PATCH 2/5] Updating README.md for aws-events --- packages/@aws-cdk/aws-events/README.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/packages/@aws-cdk/aws-events/README.md b/packages/@aws-cdk/aws-events/README.md index d19309455559d..f73a6536e1557 100644 --- a/packages/@aws-cdk/aws-events/README.md +++ b/packages/@aws-cdk/aws-events/README.md @@ -163,3 +163,26 @@ In this situation, the CDK will wire the 2 accounts together: For more information, see the [AWS documentation on cross-account events](https://docs.aws.amazon.com/eventbridge/latest/userguide/eventbridge-cross-account-event-delivery.html). + +## Archiving + +It is possible to archive all or some events sent to an event bus. It is then possible to [replay these events](https://aws.amazon.com/blogs/aws/new-archive-and-replay-events-with-amazon-eventbridge/). + +```ts +import * as cdk from '@aws-cdk/core'; + +const stack = new stack(); + +const bus = new EventBus(stack, 'bus', { + eventBusName: 'MyCustomEventBus' +}); + +bus.archive( + eventPattern: { + account: [stack.account], + }, + archiveName: 'MyCustomEventBusArchive', + description: 'MyCustomerEventBus Archive', + retention: cdk.Duration.days(365), +) +``` From 49c66d0d1bdd22aa3fe88ed370ac1b2f28bdacb0 Mon Sep 17 00:00:00 2001 From: DaWyz Date: Wed, 13 Jan 2021 21:53:53 -0800 Subject: [PATCH 3/5] Changed eventBus.archive() properties definition to a single object --- packages/@aws-cdk/aws-events/lib/archive.ts | 13 ++++++-- packages/@aws-cdk/aws-events/lib/event-bus.ts | 32 ++++++------------- .../aws-events/test/test.event-bus.ts | 14 +++++--- 3 files changed, 29 insertions(+), 30 deletions(-) diff --git a/packages/@aws-cdk/aws-events/lib/archive.ts b/packages/@aws-cdk/aws-events/lib/archive.ts index 033424274db25..16156578a3b91 100644 --- a/packages/@aws-cdk/aws-events/lib/archive.ts +++ b/packages/@aws-cdk/aws-events/lib/archive.ts @@ -5,13 +5,13 @@ import { EventPattern } from './event-pattern'; import { CfnArchive } from './events.generated'; /** - * The event archive properties + * The event archive base properties */ -export interface ArchiveProps { +export interface BaseArchiveProps { /** * The name of the archive. * - * @default - none + * @default - Automatically generated */ readonly archiveName?: string; /** @@ -29,6 +29,13 @@ export interface ArchiveProps { * @default - Infinite */ readonly retention?: Duration; +} + + +/** + * The event archive properties + */ +export interface ArchiveProps extends BaseArchiveProps { /** * The event source associated with the archive. */ diff --git a/packages/@aws-cdk/aws-events/lib/event-bus.ts b/packages/@aws-cdk/aws-events/lib/event-bus.ts index 4b89bb109dbf0..79969a663d539 100644 --- a/packages/@aws-cdk/aws-events/lib/event-bus.ts +++ b/packages/@aws-cdk/aws-events/lib/event-bus.ts @@ -1,8 +1,7 @@ import * as iam from '@aws-cdk/aws-iam'; -import { Duration, IResource, Lazy, Names, Resource, Stack, Token } from '@aws-cdk/core'; +import { IResource, Lazy, Names, Resource, Stack, Token } from '@aws-cdk/core'; import { Construct } from 'constructs'; -import { Archive } from './archive'; -import { EventPattern } from './event-pattern'; +import { Archive, BaseArchiveProps } from './archive'; import { CfnEventBus } from './events.generated'; /** @@ -45,17 +44,9 @@ export interface IEventBus extends IResource { * When you create an archive, incoming events might not immediately start being sent to the archive. * Allow a short period of time for changes to take effect. * - * @param eventPattern An event pattern to use to filter events sent to the archive. - * @param archiveName The name of the archive. - * @param description A description for the archive. - * @param retention The number of days to retain events for. Default value is 0. If set to 0, events are retained indefinitely. + * @param props Properties of the archive */ - archive( - eventPattern: EventPattern, - archiveName?: string, - description?: string, - retention?: Duration, - ): Archive; + archive(props: BaseArchiveProps): Archive; } /** @@ -137,18 +128,13 @@ abstract class EventBusBase extends Resource implements IEventBus { */ public abstract readonly eventSourceName?: string; - public archive( - eventPattern: EventPattern, - archiveName?: string, - description?: string, - retention?: Duration, - ): Archive { + public archive(props: BaseArchiveProps): Archive { return new Archive(this, 'Archive', { sourceEventBus: this, - description: description || Lazy.string({ produce: () => `Event Archive for ${this.eventBusName} Event Bus` }), - eventPattern: eventPattern, - retention: retention, - archiveName: archiveName, + description: props.description || Lazy.string({ produce: () => `Event Archive for ${this.eventBusName} Event Bus` }), + eventPattern: props.eventPattern, + retention: props.retention, + archiveName: props.archiveName, }); } } diff --git a/packages/@aws-cdk/aws-events/test/test.event-bus.ts b/packages/@aws-cdk/aws-events/test/test.event-bus.ts index 40140cb4de1f7..615b69d44ffed 100644 --- a/packages/@aws-cdk/aws-events/test/test.event-bus.ts +++ b/packages/@aws-cdk/aws-events/test/test.event-bus.ts @@ -255,8 +255,11 @@ export = { const event = new EventBus(stack, 'Bus'); event.archive({ - account: [stack.account], - }, 'MyArchive'); + eventPattern: { + account: [stack.account], + }, + archiveName: 'MyArchive', + }); // THEN expect(stack).to(haveResource('AWS::Events::EventBus', { @@ -305,8 +308,11 @@ export = { const importedBus = EventBus.fromEventBusArn(stack, 'ImportedBus', bus.eventBusArn); importedBus.archive({ - account: [stack.account], - }, 'MyArchive'); + eventPattern: { + account: [stack.account], + }, + archiveName: 'MyArchive', + }); // THEN expect(stack).to(haveResource('AWS::Events::EventBus', { From 2321766e05ce1b41e9ed40f60a548282a296f87c Mon Sep 17 00:00:00 2001 From: DaWyz Date: Wed, 3 Feb 2021 20:06:35 -0800 Subject: [PATCH 4/5] Fixing issue with ArchiveName + removing escape hatch since it's supported by cloudformation latest version --- packages/@aws-cdk/aws-events/lib/archive.ts | 3 +-- packages/@aws-cdk/aws-events/test/test.event-bus.ts | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/@aws-cdk/aws-events/lib/archive.ts b/packages/@aws-cdk/aws-events/lib/archive.ts index 16156578a3b91..dae8ebea82216 100644 --- a/packages/@aws-cdk/aws-events/lib/archive.ts +++ b/packages/@aws-cdk/aws-events/lib/archive.ts @@ -68,10 +68,9 @@ export class Archive extends Resource { description: props.description, eventPattern: props.eventPattern, retentionDays: props.retention?.toDays() || 0, + archiveName: this.physicalName, }); - archive.addPropertyOverride('archiveName', this.physicalName); - this.archiveArn = archive.attrArn; this.archiveName = archive.attrArchiveName; } diff --git a/packages/@aws-cdk/aws-events/test/test.event-bus.ts b/packages/@aws-cdk/aws-events/test/test.event-bus.ts index 615b69d44ffed..e90439fae43cd 100644 --- a/packages/@aws-cdk/aws-events/test/test.event-bus.ts +++ b/packages/@aws-cdk/aws-events/test/test.event-bus.ts @@ -293,7 +293,7 @@ export = { ], }, RetentionDays: 0, - archiveName: 'MyArchive', + ArchiveName: 'MyArchive', })); test.done(); @@ -369,7 +369,7 @@ export = { ], }, RetentionDays: 0, - archiveName: 'MyArchive', + ArchiveName: 'MyArchive', })); test.done(); From 2b1c96e634da1de484da9e9aed5575acb5971d03 Mon Sep 17 00:00:00 2001 From: DaWyz Date: Tue, 9 Feb 2021 18:56:00 -0800 Subject: [PATCH 5/5] Updating eventbus.archive signature + nits --- packages/@aws-cdk/aws-events/README.md | 8 ++++---- packages/@aws-cdk/aws-events/lib/archive.ts | 2 +- packages/@aws-cdk/aws-events/lib/event-bus.ts | 8 ++++---- packages/@aws-cdk/aws-events/test/test.event-bus.ts | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/@aws-cdk/aws-events/README.md b/packages/@aws-cdk/aws-events/README.md index f73a6536e1557..565b0537d091d 100644 --- a/packages/@aws-cdk/aws-events/README.md +++ b/packages/@aws-cdk/aws-events/README.md @@ -177,12 +177,12 @@ const bus = new EventBus(stack, 'bus', { eventBusName: 'MyCustomEventBus' }); -bus.archive( +bus.archive('MyArchive', { + archiveName: 'MyCustomEventBusArchive', + description: 'MyCustomerEventBus Archive', eventPattern: { account: [stack.account], }, - archiveName: 'MyCustomEventBusArchive', - description: 'MyCustomerEventBus Archive', retention: cdk.Duration.days(365), -) +}); ``` diff --git a/packages/@aws-cdk/aws-events/lib/archive.ts b/packages/@aws-cdk/aws-events/lib/archive.ts index dae8ebea82216..3da79df6682a2 100644 --- a/packages/@aws-cdk/aws-events/lib/archive.ts +++ b/packages/@aws-cdk/aws-events/lib/archive.ts @@ -67,7 +67,7 @@ export class Archive extends Resource { sourceArn: props.sourceEventBus.eventBusArn, description: props.description, eventPattern: props.eventPattern, - retentionDays: props.retention?.toDays() || 0, + retentionDays: props.retention?.toDays({ integral: true }) || 0, archiveName: this.physicalName, }); diff --git a/packages/@aws-cdk/aws-events/lib/event-bus.ts b/packages/@aws-cdk/aws-events/lib/event-bus.ts index 79969a663d539..cd0c7f913cbf6 100644 --- a/packages/@aws-cdk/aws-events/lib/event-bus.ts +++ b/packages/@aws-cdk/aws-events/lib/event-bus.ts @@ -46,7 +46,7 @@ export interface IEventBus extends IResource { * * @param props Properties of the archive */ - archive(props: BaseArchiveProps): Archive; + archive(id: string, props: BaseArchiveProps): Archive; } /** @@ -128,10 +128,10 @@ abstract class EventBusBase extends Resource implements IEventBus { */ public abstract readonly eventSourceName?: string; - public archive(props: BaseArchiveProps): Archive { - return new Archive(this, 'Archive', { + public archive(id: string, props: BaseArchiveProps): Archive { + return new Archive(this, id, { sourceEventBus: this, - description: props.description || Lazy.string({ produce: () => `Event Archive for ${this.eventBusName} Event Bus` }), + description: props.description || `Event Archive for ${this.eventBusName} Event Bus`, eventPattern: props.eventPattern, retention: props.retention, archiveName: props.archiveName, diff --git a/packages/@aws-cdk/aws-events/test/test.event-bus.ts b/packages/@aws-cdk/aws-events/test/test.event-bus.ts index e90439fae43cd..2e8434e147bb1 100644 --- a/packages/@aws-cdk/aws-events/test/test.event-bus.ts +++ b/packages/@aws-cdk/aws-events/test/test.event-bus.ts @@ -254,7 +254,7 @@ export = { // WHEN const event = new EventBus(stack, 'Bus'); - event.archive({ + event.archive('MyArchive', { eventPattern: { account: [stack.account], }, @@ -307,7 +307,7 @@ export = { const importedBus = EventBus.fromEventBusArn(stack, 'ImportedBus', bus.eventBusArn); - importedBus.archive({ + importedBus.archive('MyArchive', { eventPattern: { account: [stack.account], },