Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions event-unwrappers/s3-in-sns.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
'use strict';

var _ = require('underscore');

/**
* Converts an SNS event that contains S3 events as its message into a list of
* objects representing each S3 item included in the message(s).
*
* Format of each S3 event object returned by this function:
* {
* region: 'us-east-1',
* time: '2017-02-27T23:16:36.367Z',
* name: 'ObjectCreated:Put',
* bucket: { // whatever is passed in the raw S3 message bucket object
* name: 'some-bucket-name',
* arn: 'arn:....:some-bucket-name',
* ownerIdentity: { principalId: 'ABCDEFGHIJ' },
* },
* object: { // whatever is passed in the raw S3 message object object
* key: 'the-s3-object-key.foo',
* size: 1234, // file size
* eTag: '2g4d34e9e7g513geg542958891434e1e',
* versionId: '6CSRvM11Ku9dnTY4TgHdOP1tG1XYo1jj',
* sequencer: '1158G4B16A1FC5422D'
* },
* }
*
* @param object evt the event that your Lambda function was invoked with
* @return array of simplified events, one event for each S3 event in the message(s)
*/
module.exports = function(evt) {
return _.chain(evt.Records)
.map(function(snsRecord) {
if (snsRecord.EventSource !== 'aws:sns') {
throw new Error('The event must come from SNS, but appears to come from ' + snsRecord.EventSource);
}

return JSON.parse(snsRecord.Sns.Message);
})
.pluck('Records')
.flatten()
.map(function(s3Record) {
if (s3Record.eventSource !== 'aws:s3') {
throw new Error('The messages in the event must come from S3, but appear to come from ' + s3Record.eventSource);
}

return {
region: s3Record.awsRegion,
time: s3Record.eventTime,
name: s3Record.eventName,
bucket: s3Record.s3.bucket,
object: s3Record.s3.object,
};
})
.value();
};
126 changes: 126 additions & 0 deletions tests/event-unwrappers/s3-in-sns.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
'use strict';

var expect = require('expect.js'),
unwrapper = require('../../event-unwrappers/s3-in-sns'),
SAMPLE_EVENT;

SAMPLE_EVENT = {
Records: [
{
EventSource: 'aws:sns',
EventVersion: '1.0',
EventSubscriptionArn: 'arn:aws:sns:us-east-1:1234567890:some-topic-name:08d701fa-2d0d-432e-9a21-ea3986e31223',
Sns: {
Type: 'Notification',
MessageId: 'bc37cd54-0279-4e97-8727-2facae3817d3',
TopicArn: 'arn:aws:sns:us-east-1:1234567890:some-topic-name',
Subject: 'Amazon S3 Notification',
// eslint-disable-next-line max-len
Message: '{"Records":[{"eventVersion":"2.0","eventSource":"aws:s3","awsRegion":"us-east-1","eventTime":"2017-02-27T23:19:31.970Z","eventName":"ObjectCreated:Put","userIdentity":{"principalId":"AWS:ABCDEFGHIJKLMNOPQRSTUV:cli"},"requestParameters":{"sourceIPAddress":"8.8.8.8"},"responseElements":{"x-amz-request-id":"ABCDEFGHIJK123","x-amz-id-2":"ABCDEFGHJKLMNOPQRSTUV"},"s3":{"s3SchemaVersion":"1.0","configurationId":"8c9d1f41-bdd8-42be-a2ab-6a0a98b29b21","bucket":{"name":"some-bucket-name","ownerIdentity":{"principalId":"ABCDEFGHIJKL"},"arn":"arn:aws:s3:::some-bucket-name"},"object":{"key":"some-object-key-1.foo","size":1539,"eTag":"f51f20b3552d4293ba39d836a31b49a1","versionId":"3019dda287184f33944042866943875a","sequencer":"0e042b98180d"}}}]}',
Timestamp: '2017-02-27T23:19:32.053Z',
SignatureVersion: '1',
Signature: 'Bt/ABCDEFGH==',
SigningCertUrl: 'https://sns.us-east-1.amazonaws.com/SimpleNotificationService-874eba81258f454ba8bf74bf476a6f4f.pem',
UnsubscribeUrl: 'https://sns.us-east-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-east-1:1234567890:some-topic-name:08d701fa-2d0d-432e-9a21-ea3986e31223',
MessageAttributes: {}
}
},
{
EventSource: 'aws:sns',
EventVersion: '1.0',
EventSubscriptionArn: 'arn:aws:sns:us-east-1:1234567890:some-topic-name:08d701fa-2d0d-432e-9a21-ea3986e31223',
Sns: {
Type: 'Notification',
MessageId: 'bc37cd54-0279-4e97-8727-2facae3817d3',
TopicArn: 'arn:aws:sns:us-east-1:1234567890:some-topic-name',
Subject: 'Amazon S3 Notification',
// eslint-disable-next-line max-len
Message: '{"Records":[{"eventVersion":"2.0","eventSource":"aws:s3","awsRegion":"us-east-1","eventTime":"2017-02-27T23:19:31.970Z","eventName":"ObjectCreated:Put","userIdentity":{"principalId":"AWS:ABCDEFGHIJKLMNOPQRSTUV:cli"},"requestParameters":{"sourceIPAddress":"8.8.8.8"},"responseElements":{"x-amz-request-id":"ABCDEFGHIJK123","x-amz-id-2":"ABCDEFGHJKLMNOPQRSTUV"},"s3":{"s3SchemaVersion":"1.0","configurationId":"8c9d1f41-bdd8-42be-a2ab-6a0a98b29b21","bucket":{"name":"some-bucket-name","ownerIdentity":{"principalId":"ABCDEFGHIJKL"},"arn":"arn:aws:s3:::some-bucket-name"},"object":{"key":"some-object-key-2.foo","size":1539,"eTag":"f51f20b3552d4293ba39d836a31b49a1","versionId":"3019dda287184f33944042866943875a","sequencer":"0e042b98180d"}}},{"eventVersion":"2.0","eventSource":"aws:s3","awsRegion":"us-east-1","eventTime":"2017-02-27T23:19:31.970Z","eventName":"ObjectCreated:Put","userIdentity":{"principalId":"AWS:ABCDEFGHIJKLMNOPQRSTUV:cli"},"requestParameters":{"sourceIPAddress":"8.8.8.8"},"responseElements":{"x-amz-request-id":"ABCDEFGHIJK123","x-amz-id-2":"ABCDEFGHJKLMNOPQRSTUV"},"s3":{"s3SchemaVersion":"1.0","configurationId":"8c9d1f41-bdd8-42be-a2ab-6a0a98b29b21","bucket":{"name":"some-bucket-name","ownerIdentity":{"principalId":"ABCDEFGHIJKL"},"arn":"arn:aws:s3:::some-bucket-name"},"object":{"key":"some-object-key-3.foo","size":1539,"eTag":"f51f20b3552d4293ba39d836a31b49a1","versionId":"3019dda287184f33944042866943875a","sequencer":"0e042b98180d"}}}]}',
Timestamp: '2017-02-27T23:19:32.053Z',
SignatureVersion: '1',
Signature: 'Bt/ABCDEFGH==',
SigningCertUrl: 'https://sns.us-east-1.amazonaws.com/SimpleNotificationService-874eba81258f454ba8bf74bf476a6f4f.pem',
UnsubscribeUrl: 'https://sns.us-east-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-east-1:1234567890:some-topic-name:08d701fa-2d0d-432e-9a21-ea3986e31223',
MessageAttributes: {}
}
},
],
};

describe('event-unwrappers/s3-in-sns', function() {

it('unwraps events as expected', function() {
var resp = unwrapper(SAMPLE_EVENT);

expect(resp).to.eql([
{
region: 'us-east-1',
time: '2017-02-27T23:19:31.970Z',
name: 'ObjectCreated:Put',
bucket: {
name: 'some-bucket-name',
ownerIdentity: { principalId: 'ABCDEFGHIJKL' },
arn: 'arn:aws:s3:::some-bucket-name',
},
object: {
key: 'some-object-key-1.foo',
size: 1539,
eTag: 'f51f20b3552d4293ba39d836a31b49a1',
versionId: '3019dda287184f33944042866943875a',
sequencer: '0e042b98180d',
},
},
{
region: 'us-east-1',
time: '2017-02-27T23:19:31.970Z',
name: 'ObjectCreated:Put',
bucket: {
name: 'some-bucket-name',
ownerIdentity: { principalId: 'ABCDEFGHIJKL' },
arn: 'arn:aws:s3:::some-bucket-name',
},
object: {
key: 'some-object-key-2.foo',
size: 1539,
eTag: 'f51f20b3552d4293ba39d836a31b49a1',
versionId: '3019dda287184f33944042866943875a',
sequencer: '0e042b98180d',
},
},
{
region: 'us-east-1',
time: '2017-02-27T23:19:31.970Z',
name: 'ObjectCreated:Put',
bucket: {
name: 'some-bucket-name',
ownerIdentity: { principalId: 'ABCDEFGHIJKL' },
arn: 'arn:aws:s3:::some-bucket-name',
},
object: {
key: 'some-object-key-3.foo',
size: 1539,
eTag: 'f51f20b3552d4293ba39d836a31b49a1',
versionId: '3019dda287184f33944042866943875a',
sequencer: '0e042b98180d',
},
},
]);
});

it('throws an error when the top-level event is not from SNS', function() {
var sample = JSON.parse(JSON.stringify(SAMPLE_EVENT));

sample.Records[1].EventSource = 'not-sns';

expect(unwrapper.bind(null, sample)).to.throwError();
});

it('throws an error when the embedded S3 events are not from S3', function() {
var sample = JSON.parse(JSON.stringify(SAMPLE_EVENT));

sample.Records[1].Sns.Message = sample.Records[1].Sns.Message.replace('"eventSource":"aws:s3"', '"eventSource":"aws:not-s3"');

expect(unwrapper.bind(null, sample)).to.throwError();
});

});