From 96dd98c0c00c5b2dc233a8a0fd64cfbf3fa260fa Mon Sep 17 00:00:00 2001 From: Steve Hu Date: Mon, 26 Oct 2026 22:50:13 -0400 Subject: [PATCH 1/3] Share the permissive Lambda runtime mapper --- .../aws/lambda/LambdaRuntimeMappers.java | 18 ++++++++++++++++++ .../com/networknt/aws/lambda/Runtime.java | 5 +---- .../Jackson3RuntimeCompatibilityTest.java | 19 ++++++++++--------- 3 files changed, 29 insertions(+), 13 deletions(-) create mode 100644 custom-runtime/src/main/java/com/networknt/aws/lambda/LambdaRuntimeMappers.java diff --git a/custom-runtime/src/main/java/com/networknt/aws/lambda/LambdaRuntimeMappers.java b/custom-runtime/src/main/java/com/networknt/aws/lambda/LambdaRuntimeMappers.java new file mode 100644 index 0000000..401abd6 --- /dev/null +++ b/custom-runtime/src/main/java/com/networknt/aws/lambda/LambdaRuntimeMappers.java @@ -0,0 +1,18 @@ +package com.networknt.aws.lambda; + +import com.networknt.jackson.JacksonDefaults; +import tools.jackson.databind.DeserializationFeature; +import tools.jackson.databind.ObjectMapper; + +/** Shared immutable event mapper for the JVM and native Lambda runtimes. */ +public final class LambdaRuntimeMappers { + private static final ObjectMapper MAPPER = JacksonDefaults.builder() + .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES).build(); + + private LambdaRuntimeMappers() { } + + /** Keeps the framework defaults while accepting future AWS event properties. */ + public static ObjectMapper mapper() { + return MAPPER; + } +} diff --git a/custom-runtime/src/main/java/com/networknt/aws/lambda/Runtime.java b/custom-runtime/src/main/java/com/networknt/aws/lambda/Runtime.java index 0b99f2d..3d7713b 100644 --- a/custom-runtime/src/main/java/com/networknt/aws/lambda/Runtime.java +++ b/custom-runtime/src/main/java/com/networknt/aws/lambda/Runtime.java @@ -3,7 +3,6 @@ import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; -import com.networknt.jackson.JacksonDefaults; import com.networknt.utility.NioUtils; import java.io.IOException; import java.lang.reflect.Method; @@ -11,14 +10,12 @@ import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.net.HttpURLConnection; -import tools.jackson.databind.DeserializationFeature; import tools.jackson.databind.ObjectMapper; public class Runtime { private static final String REQUEST_ID_HEADER = "lambda-runtime-aws-request-id"; - private static final ObjectMapper OBJECT_MAPPER = JacksonDefaults.builder() - .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES).build(); + private static final ObjectMapper OBJECT_MAPPER = LambdaRuntimeMappers.mapper(); public static void main(String[] args) throws IOException { while(true) { diff --git a/custom-runtime/src/test/java/com/networknt/aws/lambda/Jackson3RuntimeCompatibilityTest.java b/custom-runtime/src/test/java/com/networknt/aws/lambda/Jackson3RuntimeCompatibilityTest.java index 387d202..1134d10 100644 --- a/custom-runtime/src/test/java/com/networknt/aws/lambda/Jackson3RuntimeCompatibilityTest.java +++ b/custom-runtime/src/test/java/com/networknt/aws/lambda/Jackson3RuntimeCompatibilityTest.java @@ -3,7 +3,6 @@ import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; import org.junit.jupiter.api.Test; -import tools.jackson.databind.ObjectMapper; import java.util.List; import java.util.Map; @@ -14,15 +13,17 @@ import static org.junit.jupiter.api.Assertions.assertTrue; class Jackson3RuntimeCompatibilityTest { - private ObjectMapper runtimeMapper() throws Exception { - var field = Runtime.class.getDeclaredField("OBJECT_MAPPER"); - field.setAccessible(true); - return (ObjectMapper) field.get(null); + @Test + void reusesOneImmutableMapperWithoutChangingFrameworkDefaults() { + org.junit.jupiter.api.Assertions.assertSame(LambdaRuntimeMappers.mapper(), LambdaRuntimeMappers.mapper()); + assertFalse(LambdaRuntimeMappers.mapper().isEnabled(tools.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)); + assertTrue(com.networknt.jackson.JacksonDefaults.mapper().isEnabled(tools.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)); } + @Test void bindsAwsGatewayEventsWithoutJackson2Types() throws Exception { - var mapper = runtimeMapper(); + var mapper = LambdaRuntimeMappers.mapper(); var request = mapper.readValue("{\"path\":\"/pets\",\"httpMethod\":\"POST\"," + "\"body\":\"雪\",\"isBase64Encoded\":true,\"requestContext\":{\"requestId\":\"fixture\"}," + "\"multiValueHeaders\":{\"X-Test\":[\"one\",\"two\"]}}", APIGatewayProxyRequestEvent.class); @@ -40,12 +41,12 @@ void preservesTheMeasuredResponseWireShape() throws Exception { .withMultiValueHeaders(Map.of("Set-Cookie", List.of("a=1", "b=2"))); assertEquals("{\"statusCode\":201,\"headers\":{\"Content-Type\":\"application/json\"}," + "\"multiValueHeaders\":{\"Set-Cookie\":[\"a=1\",\"b=2\"]},\"body\":\"雪\\nbody\"," - + "\"isBase64Encoded\":true}", runtimeMapper().writeValueAsString(response)); + + "\"isBase64Encoded\":true}", LambdaRuntimeMappers.mapper().writeValueAsString(response)); } @Test void retainsBuilderBindingAndNullPrimitiveHandling() throws Exception { - var mapper = runtimeMapper(); + var mapper = LambdaRuntimeMappers.mapper(); var response = mapper.readValue("{\"code\":null,\"message\":\"fixture error\"}", DefaultResponse.class); assertEquals(0, response.getCode()); assertEquals("{\"code\":0,\"message\":\"fixture error\"}", mapper.writeValueAsString(response)); @@ -56,7 +57,7 @@ void retainsBuilderBindingAndNullPrimitiveHandling() throws Exception { @Test void retainsPermissiveRuntimeBinding() throws Exception { - var mapper = runtimeMapper(); + var mapper = LambdaRuntimeMappers.mapper(); var request = mapper.readValue("{\"futureField\":1,\"isBase64Encoded\":false} {}", APIGatewayProxyRequestEvent.class); assertNull(request.getPath()); assertFalse(request.getIsBase64Encoded()); From 33e2aa6f122af5f27dd46c7c213673654c395a04 Mon Sep 17 00:00:00 2001 From: Steve Hu Date: Tue, 27 Oct 2026 08:50:25 -0400 Subject: [PATCH 2/3] Clarify the Lambda mapper boundary and simplify its policy test --- .../networknt/aws/lambda/LambdaRuntimeMappers.java | 8 ++++++-- .../aws/lambda/Jackson3RuntimeCompatibilityTest.java | 12 +++++++----- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/custom-runtime/src/main/java/com/networknt/aws/lambda/LambdaRuntimeMappers.java b/custom-runtime/src/main/java/com/networknt/aws/lambda/LambdaRuntimeMappers.java index 401abd6..0cc8e8b 100644 --- a/custom-runtime/src/main/java/com/networknt/aws/lambda/LambdaRuntimeMappers.java +++ b/custom-runtime/src/main/java/com/networknt/aws/lambda/LambdaRuntimeMappers.java @@ -4,14 +4,18 @@ import tools.jackson.databind.DeserializationFeature; import tools.jackson.databind.ObjectMapper; -/** Shared immutable event mapper for the JVM and native Lambda runtimes. */ +/** + * Shared immutable mapper exclusively for AWS Lambda event payloads in the JVM and native runtimes. + * Intentionally accepts unknown event properties, making it more permissive than the framework mapper. + * Do not use it for configuration binding or validation. + */ public final class LambdaRuntimeMappers { private static final ObjectMapper MAPPER = JacksonDefaults.builder() .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES).build(); private LambdaRuntimeMappers() { } - /** Keeps the framework defaults while accepting future AWS event properties. */ + /** Returns the event-only mapper, accepting future AWS event properties. */ public static ObjectMapper mapper() { return MAPPER; } diff --git a/custom-runtime/src/test/java/com/networknt/aws/lambda/Jackson3RuntimeCompatibilityTest.java b/custom-runtime/src/test/java/com/networknt/aws/lambda/Jackson3RuntimeCompatibilityTest.java index 1134d10..01dfdc9 100644 --- a/custom-runtime/src/test/java/com/networknt/aws/lambda/Jackson3RuntimeCompatibilityTest.java +++ b/custom-runtime/src/test/java/com/networknt/aws/lambda/Jackson3RuntimeCompatibilityTest.java @@ -2,7 +2,9 @@ import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; +import com.networknt.jackson.JacksonDefaults; import org.junit.jupiter.api.Test; +import tools.jackson.databind.DeserializationFeature; import java.util.List; import java.util.Map; @@ -14,13 +16,13 @@ class Jackson3RuntimeCompatibilityTest { @Test - void reusesOneImmutableMapperWithoutChangingFrameworkDefaults() { - org.junit.jupiter.api.Assertions.assertSame(LambdaRuntimeMappers.mapper(), LambdaRuntimeMappers.mapper()); - assertFalse(LambdaRuntimeMappers.mapper().isEnabled(tools.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)); - assertTrue(com.networknt.jackson.JacksonDefaults.mapper().isEnabled(tools.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)); + void acceptsUnknownEventPropertiesWithoutChangingFreshFrameworkMapper() { + var runtimeMapper = LambdaRuntimeMappers.mapper(); + var frameworkMapper = JacksonDefaults.builder().build(); + assertFalse(runtimeMapper.isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)); + assertTrue(frameworkMapper.isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)); } - @Test void bindsAwsGatewayEventsWithoutJackson2Types() throws Exception { var mapper = LambdaRuntimeMappers.mapper(); From ddcfc1caecef8f41ca366501cf5c13aa2e33c482 Mon Sep 17 00:00:00 2001 From: Steve Hu Date: Tue, 27 Oct 2026 08:53:07 -0400 Subject: [PATCH 3/3] Name the runtime mapper test after its feature-policy comparison --- .../networknt/aws/lambda/Jackson3RuntimeCompatibilityTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom-runtime/src/test/java/com/networknt/aws/lambda/Jackson3RuntimeCompatibilityTest.java b/custom-runtime/src/test/java/com/networknt/aws/lambda/Jackson3RuntimeCompatibilityTest.java index 01dfdc9..1c60aa0 100644 --- a/custom-runtime/src/test/java/com/networknt/aws/lambda/Jackson3RuntimeCompatibilityTest.java +++ b/custom-runtime/src/test/java/com/networknt/aws/lambda/Jackson3RuntimeCompatibilityTest.java @@ -16,7 +16,7 @@ class Jackson3RuntimeCompatibilityTest { @Test - void acceptsUnknownEventPropertiesWithoutChangingFreshFrameworkMapper() { + void runtimeAndFrameworkMappersHaveSeparateUnknownPropertyPolicies() { var runtimeMapper = LambdaRuntimeMappers.mapper(); var frameworkMapper = JacksonDefaults.builder().build(); assertFalse(runtimeMapper.isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES));