Skip to content

fix: envelope is not taken into account with built-in types #960

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
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
10 changes: 5 additions & 5 deletions powertools-validation/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@
<artifactId>json-schema-validator</artifactId>
<version>1.0.73</version>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-lambda-java-serialization</artifactId>
</dependency>

<!-- Test dependencies -->
<dependency>
Expand All @@ -87,11 +91,7 @@
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-lambda-java-serialization</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
*/
package software.amazon.lambda.powertools.validation;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
Expand All @@ -21,9 +22,12 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

import com.amazonaws.services.lambda.runtime.serialization.PojoSerializer;
import com.amazonaws.services.lambda.runtime.serialization.events.LambdaEventSerializers;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.JsonNodeType;
import com.fasterxml.jackson.databind.node.NullNode;
import com.networknt.schema.JsonSchema;
import com.networknt.schema.ValidationMessage;
import io.burt.jmespath.Expression;
Expand Down Expand Up @@ -65,9 +69,15 @@ public static void validate(Object obj, JsonSchema jsonSchema, String envelope)
}
JsonNode subNode;
try {
JsonNode jsonNode = ValidationConfig.get().getObjectMapper().valueToTree(obj);
PojoSerializer pojoSerializer = LambdaEventSerializers.serializerFor(obj.getClass(), ClassLoader.getSystemClassLoader());
ByteArrayOutputStream out = new ByteArrayOutputStream();
pojoSerializer.toJson(obj, out);
JsonNode jsonNode = ValidationConfig.get().getObjectMapper().readTree(out.toString("UTF-8"));
Expression<JsonNode> expression = ValidationConfig.get().getJmesPath().compile(envelope);
subNode = expression.search(jsonNode);
if (subNode == null || subNode instanceof NullNode) {
throw new ValidationException("Envelope not found in the object");
}
} catch (Exception e) {
throw new ValidationException("Cannot find envelope <"+envelope+"> in the object <"+obj+">", e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import software.amazon.lambda.powertools.validation.Validation;
import software.amazon.lambda.powertools.validation.ValidationConfig;

Expand All @@ -36,6 +38,8 @@
*/
@Aspect
public class ValidationAspect {
private static final Logger LOG = LoggerFactory.getLogger(ValidationAspect.class);

@SuppressWarnings({"EmptyMethod"})
@Pointcut("@annotation(validation)")
public void callAt(Validation validation) {
Expand All @@ -59,7 +63,9 @@ && placedOnRequestHandler(pjp)) {
JsonSchema inboundJsonSchema = getJsonSchema(validation.inboundSchema(), true);

Object obj = pjp.getArgs()[0];
if (obj instanceof APIGatewayProxyRequestEvent) {
if (validation.envelope() != null && !validation.envelope().isEmpty()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could there be cases where customer functionality changes because of this reordering?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potentially, if they have an envelope specified (which is not used today). It will be used after update.

validate(obj, inboundJsonSchema, validation.envelope());
} else if (obj instanceof APIGatewayProxyRequestEvent) {
APIGatewayProxyRequestEvent event = (APIGatewayProxyRequestEvent) obj;
validate(event.getBody(), inboundJsonSchema);
} else if (obj instanceof APIGatewayV2HTTPEvent) {
Expand Down Expand Up @@ -105,7 +111,7 @@ && placedOnRequestHandler(pjp)) {
KinesisAnalyticsStreamsInputPreprocessingEvent event = (KinesisAnalyticsStreamsInputPreprocessingEvent) obj;
event.getRecords().forEach(record -> validate(decode(record.getData()), inboundJsonSchema));
} else {
validate(obj, inboundJsonSchema, validation.envelope());
LOG.warn("Unhandled event type {}, please use the 'envelope' parameter to specify what to validate", obj.getClass().getName());
}
}
}
Expand All @@ -131,7 +137,7 @@ && placedOnRequestHandler(pjp)) {
KinesisAnalyticsInputPreprocessingResponse response = (KinesisAnalyticsInputPreprocessingResponse) result;
response.getRecords().forEach(record -> validate(decode(record.getData()), outboundJsonSchema));
} else {
validate(result, outboundJsonSchema, validation.envelope());
LOG.warn("Unhandled response type {}, please use the 'envelope' parameter to specify what to validate", result.getClass().getName());
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright 2020 Amazon.com, Inc. or its affiliates.
* Licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package software.amazon.lambda.powertools.validation.handlers;

import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.lambda.runtime.events.SQSEvent;
import software.amazon.lambda.powertools.validation.Validation;

public class SQSWithCustomEnvelopeHandler implements RequestHandler<SQSEvent, String> {

@Override
@Validation(inboundSchema = "classpath:/schema_v7.json", envelope = "Records[*].powertools_json(body).powertools_json(Message)")
public String handleRequest(SQSEvent input, Context context) {
return "OK";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright 2020 Amazon.com, Inc. or its affiliates.
* Licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package software.amazon.lambda.powertools.validation.handlers;

import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.lambda.runtime.events.SQSEvent;
import software.amazon.lambda.powertools.validation.Validation;

public class SQSWithWrongEnvelopeHandler implements RequestHandler<SQSEvent, String> {

@Override
// real event contains Records with big R (https://docs.aws.amazon.com/lambda/latest/dg/with-sqs.html)
@Validation(inboundSchema = "classpath:/schema_v7.json", envelope = "records[*].powertools_json(body).powertools_json(Message)")
public String handleRequest(SQSEvent input, Context context) {
return "OK";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,24 @@ public void validate_SQS() {
assertThat(handler.handleRequest(event, context)).isEqualTo("OK");
}

@Test
public void validate_SQS_CustomEnvelopeTakePrecedence() {
PojoSerializer<SQSEvent> pojoSerializer = LambdaEventSerializers.serializerFor(SQSEvent.class, ClassLoader.getSystemClassLoader());
SQSEvent event = pojoSerializer.fromJson(this.getClass().getResourceAsStream("/sqs_message.json"));

SQSWithCustomEnvelopeHandler handler = new SQSWithCustomEnvelopeHandler();
assertThat(handler.handleRequest(event, context)).isEqualTo("OK");
}

@Test
public void validate_SQS_WrongEnvelope_shouldThrowValidationException() {
PojoSerializer<SQSEvent> pojoSerializer = LambdaEventSerializers.serializerFor(SQSEvent.class, ClassLoader.getSystemClassLoader());
SQSEvent event = pojoSerializer.fromJson(this.getClass().getResourceAsStream("/sqs_message.json"));

SQSWithWrongEnvelopeHandler handler = new SQSWithWrongEnvelopeHandler();
assertThatExceptionOfType(ValidationException.class).isThrownBy(() -> handler.handleRequest(event, context));
}

@Test
public void validate_Kinesis() {
PojoSerializer<KinesisEvent> pojoSerializer = LambdaEventSerializers.serializerFor(KinesisEvent.class, ClassLoader.getSystemClassLoader());
Expand Down
22 changes: 22 additions & 0 deletions powertools-validation/src/test/resources/sqs_message.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"Records": [
{
"messageId": "d9144555-9a4f-4ec3-99a0-fc4e625a8db2",
"receiptHandle": "7kam5bfzbDsjtcjElvhSbxeLJbeey3A==",
"body": "{\n \"Message\": \"{\\n \\\"id\\\": 43242,\\n \\\"name\\\": \\\"FooBar XY\\\",\\n \\\"price\\\": 258\\n}\"}",
"attributes": {
"ApproximateReceiveCount": "1",
"SentTimestamp": "1601975709495",
"SenderId": "AROAIFU457DVZ5L2J53F2",
"ApproximateFirstReceiveTimestamp": "1601975709499"
},
"messageAttributes": {

},
"md5OfBody": "0f96e88a291edb4429f2f7b9fdc3df96",
"eventSource": "aws:sqs",
"eventSourceARN": "arn:aws:sqs:eu-central-1:123456789012:TestLambda",
"awsRegion": "eu-central-1"
}
]
}