diff --git a/aws-lambda-java-events/src/main/java/com/amazonaws/services/lambda/runtime/events/SESEvent.java b/aws-lambda-java-events/src/main/java/com/amazonaws/services/lambda/runtime/events/SESEvent.java
new file mode 100644
index 00000000..576ddb45
--- /dev/null
+++ b/aws-lambda-java-events/src/main/java/com/amazonaws/services/lambda/runtime/events/SESEvent.java
@@ -0,0 +1,113 @@
+package com.amazonaws.services.lambda.runtime.events;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.joda.time.DateTime;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Represent an event received from SES when it receives an incoming message.
+ *
+ * See documentation.
+ */
+@Data
+@AllArgsConstructor
+@Builder(setterPrefix = "with")
+@NoArgsConstructor
+public class SESEvent {
+
+ private List records;
+
+ @Data
+ @AllArgsConstructor
+ @Builder(setterPrefix = "with")
+ @NoArgsConstructor
+ public static class Record {
+ private String eventSource;
+ private String eventVersion;
+ private Ses ses;
+ }
+
+ @Data
+ @AllArgsConstructor
+ @Builder(setterPrefix = "with")
+ @NoArgsConstructor
+ public static class Ses {
+ private Mail mail;
+ private Receipt receipt;
+ }
+
+ @Data
+ @AllArgsConstructor
+ @Builder(setterPrefix = "with")
+ @NoArgsConstructor
+ public static class Mail {
+ private DateTime timestamp;
+ private String source;
+ private String messageId;
+ private String[] destination;
+ private boolean headersTruncated;
+ private List headers;
+ private CommonHeaders commonHeaders;
+ }
+
+ @Data
+ @AllArgsConstructor
+ @Builder(setterPrefix = "with")
+ @NoArgsConstructor
+ public static class Header {
+ private String name;
+ private String value;
+ }
+
+ @Data
+ @AllArgsConstructor
+ @Builder(setterPrefix = "with")
+ @NoArgsConstructor
+ public static class CommonHeaders {
+ private String returnPath;
+ private String[] from;
+ private String date;
+ private String[] to;
+ private String messageId;
+ private String subject;
+ }
+
+ @Data
+ @AllArgsConstructor
+ @Builder(setterPrefix = "with")
+ @NoArgsConstructor
+ public static class Receipt {
+ private DateTime timestamp;
+ private long processingTimeMillis;
+ private String[] recipients;
+ private Action action;
+ private Verdict spamVerdict;
+ private Verdict virusVerdict;
+ private Verdict spfVerdict;
+ private Verdict dkimVerdict;
+ private Verdict dmarcVerdict;
+ }
+
+ @Data
+ @AllArgsConstructor
+ @Builder(setterPrefix = "with")
+ @NoArgsConstructor
+ public static class Action {
+ private String type;
+ private String functionArn;
+ private String invocationType;
+ }
+
+ @Data
+ @AllArgsConstructor
+ @Builder(setterPrefix = "with")
+ @NoArgsConstructor
+ public static class Verdict {
+ private String status;
+ }
+}
diff --git a/aws-lambda-java-serialization/src/main/java/com/amazonaws/services/lambda/runtime/serialization/events/LambdaEventSerializers.java b/aws-lambda-java-serialization/src/main/java/com/amazonaws/services/lambda/runtime/serialization/events/LambdaEventSerializers.java
index 4cb4c431..1f431d91 100644
--- a/aws-lambda-java-serialization/src/main/java/com/amazonaws/services/lambda/runtime/serialization/events/LambdaEventSerializers.java
+++ b/aws-lambda-java-serialization/src/main/java/com/amazonaws/services/lambda/runtime/serialization/events/LambdaEventSerializers.java
@@ -2,17 +2,7 @@
package com.amazonaws.services.lambda.runtime.serialization.events;
-import com.amazonaws.services.lambda.runtime.serialization.events.mixins.CloudFormationCustomResourceEventMixin;
-import com.amazonaws.services.lambda.runtime.serialization.events.mixins.CloudFrontEventMixin;
-import com.amazonaws.services.lambda.runtime.serialization.events.mixins.CloudWatchLogsEventMixin;
-import com.amazonaws.services.lambda.runtime.serialization.events.mixins.CodeCommitEventMixin;
-import com.amazonaws.services.lambda.runtime.serialization.events.mixins.ConnectEventMixin;
-import com.amazonaws.services.lambda.runtime.serialization.events.mixins.DynamodbEventMixin;
-import com.amazonaws.services.lambda.runtime.serialization.events.mixins.KinesisEventMixin;
-import com.amazonaws.services.lambda.runtime.serialization.events.mixins.SNSEventMixin;
-import com.amazonaws.services.lambda.runtime.serialization.events.mixins.SQSEventMixin;
-import com.amazonaws.services.lambda.runtime.serialization.events.mixins.ScheduledEventMixin;
-import com.amazonaws.services.lambda.runtime.serialization.events.mixins.SecretsManagerRotationEventMixin;
+import com.amazonaws.services.lambda.runtime.serialization.events.mixins.*;
import com.amazonaws.services.lambda.runtime.serialization.factories.JacksonFactory;
import com.amazonaws.services.lambda.runtime.serialization.PojoSerializer;
import com.amazonaws.services.lambda.runtime.serialization.util.ReflectUtil;
@@ -73,6 +63,7 @@ public class LambdaEventSerializers {
"com.amazonaws.services.s3.event.S3EventNotification",
"com.amazonaws.services.lambda.runtime.events.models.s3.S3EventNotification",
"com.amazonaws.services.lambda.runtime.events.S3Event",
+ "com.amazonaws.services.lambda.runtime.events.SESEvent",
"com.amazonaws.services.lambda.runtime.events.SNSEvent",
"com.amazonaws.services.lambda.runtime.events.SQSEvent")
.collect(Collectors.toList());
@@ -136,6 +127,8 @@ public class LambdaEventSerializers {
ScheduledEventMixin.class),
new SimpleEntry<>("com.amazonaws.services.lambda.runtime.events.SecretsManagerRotationEvent",
SecretsManagerRotationEventMixin.class),
+ new SimpleEntry<>("com.amazonaws.services.lambda.runtime.events.SESEvent",
+ SESEventMixin.class),
new SimpleEntry<>("com.amazonaws.services.lambda.runtime.events.SNSEvent",
SNSEventMixin.class),
new SimpleEntry<>("com.amazonaws.services.lambda.runtime.events.SNSEvent$SNSRecord",
diff --git a/aws-lambda-java-serialization/src/main/java/com/amazonaws/services/lambda/runtime/serialization/events/mixins/SESEventMixin.java b/aws-lambda-java-serialization/src/main/java/com/amazonaws/services/lambda/runtime/serialization/events/mixins/SESEventMixin.java
new file mode 100644
index 00000000..79f2034b
--- /dev/null
+++ b/aws-lambda-java-serialization/src/main/java/com/amazonaws/services/lambda/runtime/serialization/events/mixins/SESEventMixin.java
@@ -0,0 +1,12 @@
+package com.amazonaws.services.lambda.runtime.serialization.events.mixins;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.util.List;
+
+public abstract class SESEventMixin {
+
+ // needed because Jackson expects "records" instead of "Records"
+ @JsonProperty("Records") abstract List> getRecords();
+ @JsonProperty("Records") abstract void setRecords(List> records);
+}
diff --git a/aws-lambda-java-tests/src/main/java/com/amazonaws/services/lambda/runtime/tests/EventLoader.java b/aws-lambda-java-tests/src/main/java/com/amazonaws/services/lambda/runtime/tests/EventLoader.java
index 0216d5e3..2fb6de37 100644
--- a/aws-lambda-java-tests/src/main/java/com/amazonaws/services/lambda/runtime/tests/EventLoader.java
+++ b/aws-lambda-java-tests/src/main/java/com/amazonaws/services/lambda/runtime/tests/EventLoader.java
@@ -97,6 +97,10 @@ public static ScheduledEvent loadScheduledEvent(String filename) {
return loadEvent(filename, ScheduledEvent.class);
}
+ public static SESEvent loadSESEvent(String filename) {
+ return loadEvent(filename, SESEvent.class);
+ }
+
public static SNSEvent loadSNSEvent(String filename) {
return loadEvent(filename, SNSEvent.class);
}
diff --git a/aws-lambda-java-tests/src/test/java/com/amazonaws/services/lambda/runtime/tests/EventLoaderTest.java b/aws-lambda-java-tests/src/test/java/com/amazonaws/services/lambda/runtime/tests/EventLoaderTest.java
index 61d9eaf1..b097b3c1 100644
--- a/aws-lambda-java-tests/src/test/java/com/amazonaws/services/lambda/runtime/tests/EventLoaderTest.java
+++ b/aws-lambda-java-tests/src/test/java/com/amazonaws/services/lambda/runtime/tests/EventLoaderTest.java
@@ -300,4 +300,13 @@ public void testLoadSecretsManagerRotationEvent() {
.returns("arn:aws:secretsmanager:eu-central-1:123456789012:secret:/powertools/secretparam-xBPaJ5", from(SecretsManagerRotationEvent::getSecretId))
.returns("CreateSecret", from(SecretsManagerRotationEvent::getStep));
}
+
+ @Test
+ public void testLoadSESEvent() {
+ SESEvent event = EventLoader.loadSESEvent("ses_event.json");
+ assertThat(event).isNotNull();
+ assertThat(event.getRecords()).hasSize(1);
+ assertThat(event.getRecords().get(0).getSes().getMail().getDestination()[0]).isEqualTo("recipient@example.com");
+ assertThat(event.getRecords().get(0).getSes().getMail().getHeaders()).hasSize(29);
+ }
}
diff --git a/aws-lambda-java-tests/src/test/resources/ses_event.json b/aws-lambda-java-tests/src/test/resources/ses_event.json
new file mode 100644
index 00000000..b4b3c90d
--- /dev/null
+++ b/aws-lambda-java-tests/src/test/resources/ses_event.json
@@ -0,0 +1,136 @@
+{
+ "Records": [{
+ "eventSource": "aws:ses",
+ "eventVersion": "1.0",
+ "ses": {
+ "mail": {
+ "timestamp": "2019-08-05T21:30:02.028Z",
+ "source": "prvs=144d0cba7=sender@example.com",
+ "messageId": "EXAMPLE7c191be45-e9aedb9a-02f9-4d12-a87d-dd0099a07f8a-000000",
+ "destination": ["recipient@example.com"],
+ "headersTruncated": false,
+ "headers": [{
+ "name": "Return-Path",
+ "value": ""
+ }, {
+ "name": "Received",
+ "value": "from smtp.example.com [203.0.113.0]) by inbound-smtp.us-east-1.amazonaws.com with SMTP id bsvpsoklfhu7u50iur7h0kk9a2ou0r7iexample for recipient@example.com;Mon, 05 Aug 2019 21:30:02 +0000 (UTC)"
+ }, {
+ "name": "X-SES-Spam-Verdict",
+ "value": "PASS"
+ }, {
+ "name": "X-SES-Virus-Verdict",
+ "value": "PASS"
+ }, {
+ "name": "Received-SPF",
+ "value": "pass (spfCheck: domain of example.com designates 203.0.113.0 as permitted sender) client-ip=203.0.113.0; envelope-from=prvs=144d0cba42=sender@example.com; helo=smtp.example.com;"
+ }, {
+ "name": "Authentication-Results",
+ "value": "amazonses.com; spf=pass (spfCheck: domain of example.com designates 203.0.113.0as permitted sender) client-ip=203.0.113.0; envelope-from=prvs=144d0cba42=sender@example.com; helo=smtp.example.com; dkim=pass header.i=@example.com; dmarc=none header.from=example.com;"
+ }, {
+ "name": "X-SES-RECEIPT",
+ "value": "AEFBQUFBQUFBQUFHbFo0VU81VzVuYmRDNm51nhTVWpabDh6J4V2l5cG5PSHFtNzlBeUk90example"
+ }, {
+ "name": "X-SES-DKIM-SIGNATURE",
+ "value": "a=rsa-sha256; q=dns/txt; b=Cm1emU30VcD6example=; c=relaxed/simple; s=6gbrjpgwjs5zn6fwqknexample; d=amazonses.com; t=1567719002; v=1; bh=DSofsjAoUvyZj6YsBDP5enpRO1otGb7Nes0Qexample=; h=From:To:Cc:Bcc:Subject:Date:Message-ID:MIME-Version:Content-Type:X-SES-RECEIPT;"
+ }, {
+ "name": "DKIM-Signature",
+ "value": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=example.com; i=@example.com; q=dns/txt; s=example12345; t=1567719001; x=1599255001; h=from:to:subject:date:message-id:references:in-reply-to:mime-version; bh=sjAoUvyZj6YsBDP5enpRO1otGb7s0Qexample=; b=EQw2D4RLOW2IHE9OgfEA4WXp+AENJtaD2+63wmd5J+d+t/xoaiKUGClOS7WhpyOmlipryOz+iOhxUv350xJIHjLTi9Jsnlw76mRK8o4770TaUz620joCVN21n4cxsrRZpv+1kS0EcAxaF30pmwlni+XT4emsVxn7zO0I8example=;"
+ }, {
+ "name": "Received",
+ "value": "from mail.example.com (mail.example.com [203.0.113.0]) by email-inbound-relay-1d-9ec21598.us-east-1.example.com (Postfix) with ESMTPS id 57F83A2042 for ; Mon, 5 Aug 2019 21:29:58 +0000 (UTC)"
+ }, {
+ "name": "From",
+ "value": "\"Doe, John\" "
+ }, {
+ "name": "To",
+ "value": "\"recipient@example.com\" "
+ }, {
+ "name": "Subject",
+ "value": "This is a test"
+ }, {
+ "name": "Thread-Topic",
+ "value": "This is a test"
+ }, {
+ "name": "Thread-Index",
+ "value": "AQHVZDAaQ58yKI8q7kaAjkhC5stGexample"
+ }, {
+ "name": "Date",
+ "value": "Mon, 5 Aug 2019 21:29:57 +0000"
+ }, {
+ "name": "Message-ID",
+ "value": ""
+ }, {
+ "name": "References",
+ "value": "<1FCED16B-F6B0-4506-A6F0-594DFexample@example.com>"
+ }, {
+ "name": "In-Reply-To",
+ "value": "<1FCED16B-F6B0-4506-A6F0-594DFexample@example.com>"
+ }, {
+ "name": "Accept-Language",
+ "value": "en-US"
+ }, {
+ "name": "Content-Language",
+ "value": "en-US"
+ }, {
+ "name": "X-MS-Has-Attach",
+ "value": ""
+ }, {
+ "name": "X-MS-TNEF-Correlator",
+ "value": ""
+ }, {
+ "name": "x-ms-exchange-messagesentrepresentingtype",
+ "value": "1"
+ }, {
+ "name": "x-ms-exchange-transport-fromentityheader",
+ "value": "Hosted"
+ }, {
+ "name": "x-originating-ip",
+ "value": "[203.0.113.0]"
+ }, {
+ "name": "Content-Type",
+ "value": "multipart/alternative; boundary=\"_000_F8098FDD49A344F6112B195BDAexamplecom_\""
+ }, {
+ "name": "MIME-Version",
+ "value": "1.0"
+ }, {
+ "name": "Precedence",
+ "value": "Bulk"
+ }],
+ "commonHeaders": {
+ "returnPath": "prvs=144d0cba7=sender@example.com",
+ "from": ["\"Doe, John\" "],
+ "date": "Mon, 5 Aug 2019 21:29:57 +0000",
+ "to": ["\"recipient@example.com\" "],
+ "messageId": "",
+ "subject": "This is a test"
+ }
+ },
+ "receipt": {
+ "timestamp": "2019-08-05T21:30:02.028Z",
+ "processingTimeMillis": 1205,
+ "recipients": ["recipient@example.com"],
+ "spamVerdict": {
+ "status": "PASS"
+ },
+ "virusVerdict": {
+ "status": "PASS"
+ },
+ "spfVerdict": {
+ "status": "PASS"
+ },
+ "dkimVerdict": {
+ "status": "PASS"
+ },
+ "dmarcVerdict": {
+ "status": "GRAY"
+ },
+ "action": {
+ "type": "Lambda",
+ "functionArn": "arn:aws:lambda:us-east-1:123456789012:function:IncomingEmail",
+ "invocationType": "Event"
+ }
+ }
+ }
+ }]
+}
\ No newline at end of file