Skip to content

Commit 8cd07e6

Browse files
author
Elad Ben-Israel
authored
feat(aws-s3): Bucket Notifications (#201)
Adds support for S3 bucket notifications. The `bucket.onEvent` method will add a notification destination for a bucket. The s3.INotificationDestination interface is used to allow SNS, SQS and Lambda to implement notification destinations. This interface inverts the control and allows the destination to prepare to receive notifications. For example, it can modify it's policy appropriately. Since CloudFormation bucket notification support require two-phase deployments (due to the fact PutBucketNotification will fail if the destination policy has not been updated, and CloudFormation cannot create the policy until the bucket is created). The reason this is a limitation in CloudFormation is that they could not model the 1:1 relationship between the bucket and the notifications using the current semantics of CloudFormation. In the CDK, we can model this relationship by encapsulating the notifications custom resource behind a bucket. This means that users don't interact with this resource directly, but rather just subscribe to notifications on a bucket, and the resource (and accompanying handler) will be created as needed.
1 parent a283a39 commit 8cd07e6

File tree

11 files changed

+1199
-0
lines changed

11 files changed

+1199
-0
lines changed

packages/@aws-cdk/aws-s3/README.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,3 +143,40 @@ new Consumer(app, 'consume', {
143143

144144
process.stdout.write(app.run());
145145
```
146+
147+
### Bucket Notifications
148+
149+
The Amazon S3 notification feature enables you to receive notifications when
150+
certain events happen in your bucket as described under [S3 Bucket
151+
Notifications] of the S3 Developer Guide.
152+
153+
To subscribe for bucket notifications, use the `bucket.onEvent` method. The
154+
`bucket.onObjectCreated` and `bucket.onObjectRemoved` can also be used for these
155+
common use cases.
156+
157+
The following example will subscribe an SNS topic to be notified of all
158+
``s3:ObjectCreated:*` events:
159+
160+
```ts
161+
const myTopic = new sns.Topic(this, 'MyTopic');
162+
bucket.onEvent(s3.EventType.ObjectCreated, myTopic);
163+
```
164+
165+
This call will also ensure that the topic policy can accept notifications for
166+
this specific bucket.
167+
168+
The following destinations are currently supported:
169+
170+
* `sns.Topic`
171+
* `sqs.Queue`
172+
* `lambda.Function`
173+
174+
It is also possible to specify S3 object key filters when subscribing. The
175+
following example will notify `myQueue` when objects prefixed with `foo/` and
176+
have the `.jpg` suffix are removed from the bucket.
177+
178+
```ts
179+
bucket.onEvent(s3.EventType.ObjectRemoved, myQueue, { prefix: 'foo/', suffix: '.jpg' });
180+
```
181+
182+
[S3 Bucket Notifications]: https://docs.aws.amazon.com/AmazonS3/latest/dev/NotificationHowTo.html

packages/@aws-cdk/aws-s3/lib/bucket.ts

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import iam = require('@aws-cdk/aws-iam');
22
import kms = require('@aws-cdk/aws-kms');
33
import cdk = require('@aws-cdk/cdk');
44
import { BucketPolicy } from './bucket-policy';
5+
import { IBucketNotificationDestination } from './notification-dest';
6+
import { BucketNotifications } from './notifications-resource';
57
import perms = require('./perms');
68
import { LifecycleRule } from './rule';
79
import { BucketArn, BucketDomainName, BucketDualStackDomainName, cloudformation } from './s3.generated';
@@ -289,6 +291,7 @@ export class Bucket extends BucketRef {
289291
protected autoCreatePolicy = true;
290292
private readonly lifecycleRules: LifecycleRule[] = [];
291293
private readonly versioned?: boolean;
294+
private readonly notifications: BucketNotifications;
292295

293296
constructor(parent: cdk.Construct, name: string, props: BucketProps = {}) {
294297
super(parent, name);
@@ -316,6 +319,10 @@ export class Bucket extends BucketRef {
316319

317320
// Add all lifecycle rules
318321
(props.lifecycleRules || []).forEach(this.addLifecycleRule.bind(this));
322+
323+
// defines a BucketNotifications construct. Notice that an actual resource will only
324+
// be added if there are notifications added, so we don't need to condition this.
325+
this.notifications = new BucketNotifications(this, 'Notifications', { bucket: this });
319326
}
320327

321328
/**
@@ -333,6 +340,53 @@ export class Bucket extends BucketRef {
333340
this.lifecycleRules.push(rule);
334341
}
335342

343+
/**
344+
* Adds a bucket notification event destination.
345+
* @param event The event to trigger the notification
346+
* @param dest The notification destination (Lambda, SNS Topic or SQS Queue)
347+
*
348+
* @param filters S3 object key filter rules to determine which objects
349+
* trigger this event. Each filter must include a `prefix` and/or `suffix`
350+
* that will be matched against the s3 object key. Refer to the S3 Developer Guide
351+
* for details about allowed filter rules.
352+
*
353+
* @see https://docs.aws.amazon.com/AmazonS3/latest/dev/NotificationHowTo.html#notification-how-to-filtering
354+
*
355+
* @example
356+
*
357+
* bucket.onEvent(EventType.OnObjectCreated, myLambda, 'home/myusername/*')
358+
*
359+
* @see
360+
* https://docs.aws.amazon.com/AmazonS3/latest/dev/NotificationHowTo.html
361+
*/
362+
public onEvent(event: EventType, dest: IBucketNotificationDestination, ...filters: NotificationKeyFilter[]) {
363+
this.notifications.addNotification(event, dest, ...filters);
364+
}
365+
366+
/**
367+
* Subscribes a destination to receive notificatins when an object is
368+
* created in the bucket. This is identical to calling
369+
* `onEvent(EventType.ObjectCreated)`.
370+
*
371+
* @param dest The notification destination (see onEvent)
372+
* @param filters Filters (see onEvent)
373+
*/
374+
public onObjectCreated(dest: IBucketNotificationDestination, ...filters: NotificationKeyFilter[]) {
375+
return this.onEvent(EventType.ObjectCreated, dest, ...filters);
376+
}
377+
378+
/**
379+
* Subscribes a destination to receive notificatins when an object is
380+
* removed from the bucket. This is identical to calling
381+
* `onEvent(EventType.ObjectRemoved)`.
382+
*
383+
* @param dest The notification destination (see onEvent)
384+
* @param filters Filters (see onEvent)
385+
*/
386+
public onObjectRemoved(dest: IBucketNotificationDestination, ...filters: NotificationKeyFilter[]) {
387+
return this.onEvent(EventType.ObjectRemoved, dest, ...filters);
388+
}
389+
336390
/**
337391
* Set up key properties and return the Bucket encryption property from the
338392
* user's configuration.
@@ -485,6 +539,126 @@ export class S3Url extends cdk.Token {
485539

486540
}
487541

542+
/**
543+
* Notification event types.
544+
*/
545+
export enum EventType {
546+
/**
547+
* Amazon S3 APIs such as PUT, POST, and COPY can create an object. Using
548+
* these event types, you can enable notification when an object is created
549+
* using a specific API, or you can use the s3:ObjectCreated:* event type to
550+
* request notification regardless of the API that was used to create an
551+
* object.
552+
*/
553+
ObjectCreated = 's3:ObjectCreated:*',
554+
555+
/**
556+
* Amazon S3 APIs such as PUT, POST, and COPY can create an object. Using
557+
* these event types, you can enable notification when an object is created
558+
* using a specific API, or you can use the s3:ObjectCreated:* event type to
559+
* request notification regardless of the API that was used to create an
560+
* object.
561+
*/
562+
ObjectCreatedPut = 's3:ObjectCreated:Put',
563+
564+
/**
565+
* Amazon S3 APIs such as PUT, POST, and COPY can create an object. Using
566+
* these event types, you can enable notification when an object is created
567+
* using a specific API, or you can use the s3:ObjectCreated:* event type to
568+
* request notification regardless of the API that was used to create an
569+
* object.
570+
*/
571+
ObjectCreatedPost = 's3:ObjectCreated:Post',
572+
573+
/**
574+
* Amazon S3 APIs such as PUT, POST, and COPY can create an object. Using
575+
* these event types, you can enable notification when an object is created
576+
* using a specific API, or you can use the s3:ObjectCreated:* event type to
577+
* request notification regardless of the API that was used to create an
578+
* object.
579+
*/
580+
ObjectCreatedCopy = 's3:ObjectCreated:Copy',
581+
582+
/**
583+
* Amazon S3 APIs such as PUT, POST, and COPY can create an object. Using
584+
* these event types, you can enable notification when an object is created
585+
* using a specific API, or you can use the s3:ObjectCreated:* event type to
586+
* request notification regardless of the API that was used to create an
587+
* object.
588+
*/
589+
ObjectCreatedCompleteMultipartUpload = 's3:ObjectCreated:CompleteMultipartUpload',
590+
591+
/**
592+
* By using the ObjectRemoved event types, you can enable notification when
593+
* an object or a batch of objects is removed from a bucket.
594+
*
595+
* You can request notification when an object is deleted or a versioned
596+
* object is permanently deleted by using the s3:ObjectRemoved:Delete event
597+
* type. Or you can request notification when a delete marker is created for
598+
* a versioned object by using s3:ObjectRemoved:DeleteMarkerCreated. For
599+
* information about deleting versioned objects, see Deleting Object
600+
* Versions. You can also use a wildcard s3:ObjectRemoved:* to request
601+
* notification anytime an object is deleted.
602+
*
603+
* You will not receive event notifications from automatic deletes from
604+
* lifecycle policies or from failed operations.
605+
*/
606+
ObjectRemoved = 's3:ObjectRemoved:*',
607+
608+
/**
609+
* By using the ObjectRemoved event types, you can enable notification when
610+
* an object or a batch of objects is removed from a bucket.
611+
*
612+
* You can request notification when an object is deleted or a versioned
613+
* object is permanently deleted by using the s3:ObjectRemoved:Delete event
614+
* type. Or you can request notification when a delete marker is created for
615+
* a versioned object by using s3:ObjectRemoved:DeleteMarkerCreated. For
616+
* information about deleting versioned objects, see Deleting Object
617+
* Versions. You can also use a wildcard s3:ObjectRemoved:* to request
618+
* notification anytime an object is deleted.
619+
*
620+
* You will not receive event notifications from automatic deletes from
621+
* lifecycle policies or from failed operations.
622+
*/
623+
ObjectRemovedDelete = 's3:ObjectRemoved:Delete',
624+
625+
/**
626+
* By using the ObjectRemoved event types, you can enable notification when
627+
* an object or a batch of objects is removed from a bucket.
628+
*
629+
* You can request notification when an object is deleted or a versioned
630+
* object is permanently deleted by using the s3:ObjectRemoved:Delete event
631+
* type. Or you can request notification when a delete marker is created for
632+
* a versioned object by using s3:ObjectRemoved:DeleteMarkerCreated. For
633+
* information about deleting versioned objects, see Deleting Object
634+
* Versions. You can also use a wildcard s3:ObjectRemoved:* to request
635+
* notification anytime an object is deleted.
636+
*
637+
* You will not receive event notifications from automatic deletes from
638+
* lifecycle policies or from failed operations.
639+
*/
640+
ObjectRemovedDeleteMarkerCreated = 's3:ObjectRemoved:DeleteMarkerCreated',
641+
642+
/**
643+
* You can use this event type to request Amazon S3 to send a notification
644+
* message when Amazon S3 detects that an object of the RRS storage class is
645+
* lost.
646+
*/
647+
ReducedRedundancyLostObject = 's3:ReducedRedundancyLostObject',
648+
}
649+
650+
export interface NotificationKeyFilter {
651+
/**
652+
* S3 keys must have the specified prefix.
653+
*/
654+
prefix?: string;
655+
656+
/**
657+
* S3 keys must have the specified suffix.
658+
*/
659+
suffix?: string;
660+
}
661+
488662
class ImportedBucketRef extends BucketRef {
489663
public readonly bucketArn: BucketArn;
490664
public readonly bucketName: BucketName;

packages/@aws-cdk/aws-s3/lib/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
export * from './bucket';
22
export * from './bucket-policy';
33
export * from './rule';
4+
export * from './notification-dest';
45

56
// AWS::S3 CloudFormation Resources:
67
export * from './s3.generated';
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import cdk = require('@aws-cdk/cdk');
2+
import { Bucket } from './bucket';
3+
4+
/**
5+
* Implemented by constructs that can be used as bucket notification destinations.
6+
*/
7+
export interface IBucketNotificationDestination {
8+
/**
9+
* Registers this resource to receive notifications for the specified bucket.
10+
* @param bucket The bucket. Use the `path` of the bucket as a unique ID.
11+
*/
12+
asBucketNotificationDestination(bucket: Bucket): BucketNotificationDestinationProps;
13+
}
14+
15+
/**
16+
* Represents the properties of a notification destination.
17+
*/
18+
export interface BucketNotificationDestinationProps {
19+
/**
20+
* The notification type.
21+
*/
22+
readonly type: BucketNotificationDestinationType;
23+
24+
/**
25+
* The ARN of the destination (i.e. Lambda, SNS, SQS).
26+
*/
27+
readonly arn: cdk.Arn;
28+
}
29+
30+
/**
31+
* Supported types of notification destinations.
32+
*/
33+
export enum BucketNotificationDestinationType {
34+
Lambda,
35+
Queue,
36+
Topic
37+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './notifications-resource';

0 commit comments

Comments
 (0)