From a9d38ecbec39d8ab50528d1e0bae44d136869a3e Mon Sep 17 00:00:00 2001 From: Jerome Van Der Linden Date: Thu, 15 Oct 2020 13:06:00 +0200 Subject: [PATCH 1/3] adding event for SES --- .../lambda/runtime/events/SESEvent.java | 104 ++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 aws-lambda-java-events/src/main/java/com/amazonaws/services/lambda/runtime/events/SESEvent.java 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..75f9167e --- /dev/null +++ b/aws-lambda-java-events/src/main/java/com/amazonaws/services/lambda/runtime/events/SESEvent.java @@ -0,0 +1,104 @@ +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 + private static class Record { + private String eventSource; + private String eventVersion; + private Ses ses; + } + + @Data + @AllArgsConstructor + @Builder(setterPrefix = "with") + @NoArgsConstructor + private static class Ses { + private Mail mail; + private Receipt receipt; + } + + @Data + @AllArgsConstructor + @Builder(setterPrefix = "with") + @NoArgsConstructor + private static class Mail { + private DateTime timestamp; + private String source; + private String messageId; + private String[] destination; + private boolean headersTruncated; + private Map headers; + private CommonHeaders commonHeaders; + } + + @Data + @AllArgsConstructor + @Builder(setterPrefix = "with") + @NoArgsConstructor + private 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 + private 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 + private static class Action { + private String type; + private String functionArn; + private String invocationType; + } + + @Data + @AllArgsConstructor + @Builder(setterPrefix = "with") + @NoArgsConstructor + private static class Verdict { + private String status; + } +} From 9fa531e517a2ebcbb7b790b9cd073a4ec4d9b9c1 Mon Sep 17 00:00:00 2001 From: Jerome Van Der Linden Date: Thu, 15 Oct 2020 14:22:31 +0200 Subject: [PATCH 2/3] set inner classes public --- .../services/lambda/runtime/events/SESEvent.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) 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 index 75f9167e..4b46a102 100644 --- 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 @@ -20,13 +20,13 @@ @NoArgsConstructor public class SESEvent { - private List Records; + private List records; @Data @AllArgsConstructor @Builder(setterPrefix = "with") @NoArgsConstructor - private static class Record { + public static class Record { private String eventSource; private String eventVersion; private Ses ses; @@ -36,7 +36,7 @@ private static class Record { @AllArgsConstructor @Builder(setterPrefix = "with") @NoArgsConstructor - private static class Ses { + public static class Ses { private Mail mail; private Receipt receipt; } @@ -45,7 +45,7 @@ private static class Ses { @AllArgsConstructor @Builder(setterPrefix = "with") @NoArgsConstructor - private static class Mail { + public static class Mail { private DateTime timestamp; private String source; private String messageId; @@ -59,7 +59,7 @@ private static class Mail { @AllArgsConstructor @Builder(setterPrefix = "with") @NoArgsConstructor - private static class CommonHeaders { + public static class CommonHeaders { private String returnPath; private String[] from; private String date; @@ -72,7 +72,7 @@ private static class CommonHeaders { @AllArgsConstructor @Builder(setterPrefix = "with") @NoArgsConstructor - private static class Receipt { + public static class Receipt { private DateTime timestamp; private long processingTimeMillis; private String[] recipients; @@ -88,7 +88,7 @@ private static class Receipt { @AllArgsConstructor @Builder(setterPrefix = "with") @NoArgsConstructor - private static class Action { + public static class Action { private String type; private String functionArn; private String invocationType; @@ -98,7 +98,7 @@ private static class Action { @AllArgsConstructor @Builder(setterPrefix = "with") @NoArgsConstructor - private static class Verdict { + public static class Verdict { private String status; } } From 7ac26623433fc983039d16cfb3a2ed7d3b37732f Mon Sep 17 00:00:00 2001 From: Jerome Van Der Linden Date: Fri, 11 Dec 2020 14:02:18 +0100 Subject: [PATCH 3/3] complete SESEvent with mixin and tests --- .../lambda/runtime/events/SESEvent.java | 11 +- .../events/LambdaEventSerializers.java | 15 +- .../events/mixins/SESEventMixin.java | 12 ++ .../lambda/runtime/tests/EventLoader.java | 4 + .../lambda/runtime/tests/EventLoaderTest.java | 9 ++ .../src/test/resources/ses_event.json | 136 ++++++++++++++++++ 6 files changed, 175 insertions(+), 12 deletions(-) create mode 100644 aws-lambda-java-serialization/src/main/java/com/amazonaws/services/lambda/runtime/serialization/events/mixins/SESEventMixin.java create mode 100644 aws-lambda-java-tests/src/test/resources/ses_event.json 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 index 4b46a102..576ddb45 100644 --- 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 @@ -51,10 +51,19 @@ public static class Mail { private String messageId; private String[] destination; private boolean headersTruncated; - private Map headers; + 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") 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