Skip to content

Commit 3a06d1b

Browse files
committed
changes post review:
- javadoc - indentation - better edge cases handling
1 parent 09df58a commit 3a06d1b

File tree

1 file changed

+123
-53
lines changed

1 file changed

+123
-53
lines changed

powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/EventDeserializer.java

Lines changed: 123 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -26,90 +26,149 @@
2626
import static software.amazon.lambda.powertools.utilities.jmespath.Base64Function.decode;
2727
import static software.amazon.lambda.powertools.utilities.jmespath.Base64GZipFunction.decompress;
2828

29+
/**
30+
* Class that can be used to extract the meaningful part of an event and deserialize it into a Java object.<br/>
31+
* For example, extract the body of an API Gateway event, or messages from an SQS event.
32+
*/
2933
public class EventDeserializer {
3034

3135
private static final Logger LOG = LoggerFactory.getLogger(EventDeserializer.class);
3236

33-
public static EventPart extractDataFrom(Object obj) {
34-
if (obj instanceof String) {
35-
return new EventPart((String) obj);
36-
} else if (obj instanceof Map) {
37-
return new EventPart((Map<String, Object>) obj);
38-
} else if (obj instanceof APIGatewayProxyRequestEvent) {
39-
APIGatewayProxyRequestEvent event = (APIGatewayProxyRequestEvent) obj;
37+
/**
38+
* Extract the meaningful part of a Lambda Event object. Main events are built-in:
39+
* <ul>
40+
* <li>{@link APIGatewayProxyRequestEvent} -> body</li>
41+
* <li>{@link APIGatewayV2HTTPEvent} -> body</li>
42+
* <li>{@link SNSEvent} -> Records[0].Sns.Message</li>
43+
* <li>{@link SQSEvent} -> Records[*].body <i>(list)</i></li>
44+
* <li>{@link ScheduledEvent} -> detail</li>
45+
* <li>{@link ApplicationLoadBalancerRequestEvent} -> body</li>
46+
* <li>{@link CloudWatchLogsEvent} -> powertools_base64_gzip(data)</li>
47+
* <li>{@link CloudFormationCustomResourceEvent} -> resourceProperties</li>
48+
* <li>{@link KinesisEvent} -> Records[*].kinesis.powertools_base64(data) <i>(list)</i></li>
49+
* <li>{@link KinesisFirehoseEvent} -> Records[*].powertools_base64(data) <i>(list)</i></li>
50+
* <li>{@link KafkaEvent} -> records[*].values[*].powertools_base64(value) <i>(list)</i></li>
51+
* <li>{@link ActiveMQEvent} -> messages[*].powertools_base64(data) <i>(list)</i></li>
52+
* <li>{@link RabbitMQEvent} -> rmqMessagesByQueue[*].values[*].powertools_base64(data) <i>(list)</i></li>
53+
* <li>{@link KinesisAnalyticsFirehoseInputPreprocessingEvent} -> Records[*].kinesis.powertools_base64(data) <i>(list)</i></li>
54+
* <li>{@link KinesisAnalyticsStreamsInputPreprocessingEvent} > Records[*].kinesis.powertools_base64(data) <i>(list)</i></li>
55+
* <li>{@link String}</li>
56+
* <li>{@link Map}</li>
57+
* </ul>
58+
* To be used in conjunction with {@link EventPart#as(Class)} or {@link EventPart#asListOf(Class)}
59+
* for the deserialization.
60+
*
61+
* @param object the event of your Lambda function handler method
62+
* @return the part of the event which is meaningful (ex: body of the API Gateway).<br/>
63+
*/
64+
public static EventPart extractDataFrom(Object object) {
65+
if (object instanceof String) {
66+
return new EventPart((String) object);
67+
} else if (object instanceof Map) {
68+
return new EventPart((Map<String, Object>) object);
69+
} else if (object instanceof APIGatewayProxyRequestEvent) {
70+
APIGatewayProxyRequestEvent event = (APIGatewayProxyRequestEvent) object;
4071
return new EventPart(event.getBody());
41-
} else if (obj instanceof APIGatewayV2HTTPEvent) {
42-
APIGatewayV2HTTPEvent event = (APIGatewayV2HTTPEvent) obj;
72+
} else if (object instanceof APIGatewayV2HTTPEvent) {
73+
APIGatewayV2HTTPEvent event = (APIGatewayV2HTTPEvent) object;
4374
return new EventPart(event.getBody());
44-
} else if (obj instanceof SNSEvent) {
45-
SNSEvent event = (SNSEvent) obj;
75+
} else if (object instanceof SNSEvent) {
76+
SNSEvent event = (SNSEvent) object;
4677
return new EventPart(event.getRecords().get(0).getSNS().getMessage());
47-
} else if (obj instanceof SQSEvent) {
48-
SQSEvent event = (SQSEvent) obj;
49-
return new EventPart(event.getRecords().stream().map(SQSEvent.SQSMessage::getBody).collect(Collectors.toList()));
50-
} else if (obj instanceof ScheduledEvent) {
51-
ScheduledEvent event = (ScheduledEvent) obj;
78+
} else if (object instanceof SQSEvent) {
79+
SQSEvent event = (SQSEvent) object;
80+
return new EventPart(event.getRecords().stream()
81+
.map(SQSEvent.SQSMessage::getBody)
82+
.collect(Collectors.toList()));
83+
} else if (object instanceof ScheduledEvent) {
84+
ScheduledEvent event = (ScheduledEvent) object;
5285
return new EventPart(event.getDetail());
53-
} else if (obj instanceof ApplicationLoadBalancerRequestEvent) {
54-
ApplicationLoadBalancerRequestEvent event = (ApplicationLoadBalancerRequestEvent) obj;
86+
} else if (object instanceof ApplicationLoadBalancerRequestEvent) {
87+
ApplicationLoadBalancerRequestEvent event = (ApplicationLoadBalancerRequestEvent) object;
5588
return new EventPart(event.getBody());
56-
} else if (obj instanceof CloudWatchLogsEvent) {
57-
CloudWatchLogsEvent event = (CloudWatchLogsEvent) obj;
89+
} else if (object instanceof CloudWatchLogsEvent) {
90+
CloudWatchLogsEvent event = (CloudWatchLogsEvent) object;
5891
return new EventPart(decompress(decode(event.getAwsLogs().getData().getBytes(UTF_8))));
59-
} else if (obj instanceof CloudFormationCustomResourceEvent) {
60-
CloudFormationCustomResourceEvent event = (CloudFormationCustomResourceEvent) obj;
92+
} else if (object instanceof CloudFormationCustomResourceEvent) {
93+
CloudFormationCustomResourceEvent event = (CloudFormationCustomResourceEvent) object;
6194
return new EventPart(event.getResourceProperties());
62-
} else if (obj instanceof KinesisEvent) {
63-
KinesisEvent event = (KinesisEvent) obj;
64-
return new EventPart(event.getRecords().stream().map(r -> decode(r.getKinesis().getData())).collect(Collectors.toList()));
65-
} else if (obj instanceof KinesisFirehoseEvent) {
66-
KinesisFirehoseEvent event = (KinesisFirehoseEvent) obj;
67-
return new EventPart(event.getRecords().stream().map(r -> decode(r.getData())).collect(Collectors.toList()));
68-
} else if (obj instanceof KafkaEvent) {
69-
KafkaEvent event = (KafkaEvent) obj;
70-
return new EventPart(event.getRecords().values().stream().flatMap(List::stream).map(r -> decode(r.getValue())).collect(Collectors.toList()));
71-
} else if (obj instanceof ActiveMQEvent) {
72-
ActiveMQEvent event = (ActiveMQEvent) obj;
73-
return new EventPart(event.getMessages().stream().map(m -> decode(m.getData())).collect(Collectors.toList()));
74-
} else if (obj instanceof RabbitMQEvent) {
75-
RabbitMQEvent event = (RabbitMQEvent) obj;
76-
return new EventPart(event.getRmqMessagesByQueue().values().stream().flatMap(List::stream).map(r -> decode(r.getData())).collect(Collectors.toList()));
77-
} else if (obj instanceof KinesisAnalyticsFirehoseInputPreprocessingEvent) {
78-
KinesisAnalyticsFirehoseInputPreprocessingEvent event = (KinesisAnalyticsFirehoseInputPreprocessingEvent) obj;
79-
return new EventPart(event.getRecords().stream().map(r -> decode(r.getData())).collect(Collectors.toList()));
80-
} else if (obj instanceof KinesisAnalyticsStreamsInputPreprocessingEvent) {
81-
KinesisAnalyticsStreamsInputPreprocessingEvent event = (KinesisAnalyticsStreamsInputPreprocessingEvent) obj;
82-
return new EventPart(event.getRecords().stream().map(r -> decode(r.getData())).collect(Collectors.toList()));
95+
} else if (object instanceof KinesisEvent) {
96+
KinesisEvent event = (KinesisEvent) object;
97+
return new EventPart(event.getRecords().stream()
98+
.map(r -> decode(r.getKinesis().getData()))
99+
.collect(Collectors.toList()));
100+
} else if (object instanceof KinesisFirehoseEvent) {
101+
KinesisFirehoseEvent event = (KinesisFirehoseEvent) object;
102+
return new EventPart(event.getRecords().stream()
103+
.map(r -> decode(r.getData()))
104+
.collect(Collectors.toList()));
105+
} else if (object instanceof KafkaEvent) {
106+
KafkaEvent event = (KafkaEvent) object;
107+
return new EventPart(event.getRecords().values().stream()
108+
.flatMap(List::stream)
109+
.map(r -> decode(r.getValue()))
110+
.collect(Collectors.toList()));
111+
} else if (object instanceof ActiveMQEvent) {
112+
ActiveMQEvent event = (ActiveMQEvent) object;
113+
return new EventPart(event.getMessages().stream()
114+
.map(m -> decode(m.getData()))
115+
.collect(Collectors.toList()));
116+
} else if (object instanceof RabbitMQEvent) {
117+
RabbitMQEvent event = (RabbitMQEvent) object;
118+
return new EventPart(event.getRmqMessagesByQueue().values().stream()
119+
.flatMap(List::stream)
120+
.map(r -> decode(r.getData()))
121+
.collect(Collectors.toList()));
122+
} else if (object instanceof KinesisAnalyticsFirehoseInputPreprocessingEvent) {
123+
KinesisAnalyticsFirehoseInputPreprocessingEvent event = (KinesisAnalyticsFirehoseInputPreprocessingEvent) object;
124+
return new EventPart(event.getRecords().stream()
125+
.map(r -> decode(r.getData()))
126+
.collect(Collectors.toList()));
127+
} else if (object instanceof KinesisAnalyticsStreamsInputPreprocessingEvent) {
128+
KinesisAnalyticsStreamsInputPreprocessingEvent event = (KinesisAnalyticsStreamsInputPreprocessingEvent) object;
129+
return new EventPart(event.getRecords().stream()
130+
.map(r -> decode(r.getData()))
131+
.collect(Collectors.toList()));
83132
} else {
84-
// does not really make sense to use this EventLoader when you already have a typed object
133+
// does not really make sense to use this EventDeserializer when you already have a typed object
85134
// just not to throw an exception
86135
LOG.warn("Consider using your object directly instead of using EventDeserializer");
87-
return new EventPart(obj);
136+
return new EventPart(object);
88137
}
89138
}
90139

140+
/**
141+
* Meaningful part of a Lambda event.<br/>
142+
* Use {@link #extractDataFrom(Object)} to retrieve an instance of this class.
143+
*/
91144
public static class EventPart {
92145
private Map<String, Object> contentMap;
93146
private String content;
94147
private List<String> contentList;
95148
private Object contentObject;
96149

97-
public EventPart(List<String> contentList) {
150+
private EventPart(List<String> contentList) {
98151
this.contentList = contentList;
99152
}
100153

101-
public EventPart(String content) {
154+
private EventPart(String content) {
102155
this.content = content;
103156
}
104157

105-
public EventPart(Map<String, Object> contentMap) {
158+
private EventPart(Map<String, Object> contentMap) {
106159
this.contentMap = contentMap;
107160
}
108161

109-
public EventPart(Object content) {
162+
private EventPart(Object content) {
110163
this.contentObject = content;
111164
}
112165

166+
/**
167+
* Deserialize this part of event from JSON to an object of type T
168+
* @param clazz the target type for deserialization
169+
* @param <T> type of object to return
170+
* @return an Object of type T (deserialized from the content)
171+
*/
113172
public <T> T as(Class<T> clazz) {
114173
try {
115174
if (content != null) {
@@ -126,21 +185,32 @@ public <T> T as(Class<T> clazz) {
126185
return (T) contentObject;
127186
}
128187
if (contentList != null) {
129-
throw new EventDeserializationException("The content of this event is a list, consider using 'extractDataAsListOf' instead");
188+
throw new EventDeserializationException("The content of this event is a list, consider using 'asListOf' instead");
130189
}
131-
throw new EventDeserializationException("Event content is null");
190+
// should not occur, except if the event is malformed (missing fields)
191+
throw new IllegalStateException("Event content is null");
132192
} catch (IOException e) {
133193
throw new EventDeserializationException("Cannot load the event as " + clazz.getSimpleName(), e);
134194
}
135195
}
136196

197+
/**
198+
* Deserialize this part of event from JSON to a list of objects of type T
199+
* @param clazz the target type for deserialization
200+
* @param <T> type of object to return
201+
* @return a list of objects of type T (deserialized from the content)
202+
*/
137203
public <T> List<T> asListOf(Class<T> clazz) {
138204
if (contentList == null) {
139-
throw new EventDeserializationException("Event content is null");
205+
if (content != null || contentMap != null || contentObject != null) {
206+
throw new EventDeserializationException("The content of this event is not a list, consider using 'as' instead");
207+
}
208+
// should not occur, except if the event is really malformed
209+
throw new IllegalStateException("Event content is null");
140210
}
141211
return contentList.stream().map(s -> {
142212
try {
143-
return JsonConfig.get().getObjectMapper().reader().readValue(s, clazz);
213+
return s == null ? null : JsonConfig.get().getObjectMapper().reader().readValue(s, clazz);
144214
} catch (IOException e) {
145215
throw new EventDeserializationException("Cannot load the event as " + clazz.getSimpleName(), e);
146216
}

0 commit comments

Comments
 (0)