Skip to content
Open
Show file tree
Hide file tree
Changes from 8 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
5 changes: 5 additions & 0 deletions function-aws-api-proxy/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,18 @@ plugins {

dependencies {
annotationProcessor libs.micronaut.graal
annotationProcessor libs.micronaut.serde

compileOnly libs.micronaut.security

implementation libs.micronaut.http.netty

implementation libs.projectreactor

implementation libs.micronaut.serde
implementation libs.micronaut.serde.jackson


api libs.micronaut.http.server
api(libs.managed.aws.serverless.core) {
exclude group:'javax.servlet', module:'javax.servlet-api'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,7 @@

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectReader;
import com.fasterxml.jackson.databind.ObjectWriter;
import io.micronaut.json.JsonMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -119,7 +117,7 @@ protected AbstractLambdaContainerHandler(Class<RequestType> requestClass,
*
* @return Return the object mapper.
*/
protected abstract ObjectMapper objectMapper();
protected abstract JsonMapper objectMapper();
Copy link
Contributor

Choose a reason for hiding this comment

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

There is a import io.micronaut.serde.ObjectMapper;. Why not return it instead?


/**
* Gets a writer for the given response class.
Expand All @@ -133,7 +131,7 @@ protected AbstractLambdaContainerHandler(Class<RequestType> requestClass,
* @param requestClass The request class
* @return The reader
*/
protected abstract ObjectReader readerFor(Class<RequestType> requestClass);
protected abstract ObjectReader<RequestType> readerFor(Class<RequestType> requestClass);

/**
* Get the container response.
Expand Down Expand Up @@ -258,4 +256,17 @@ public void proxyStream(InputStream input, OutputStream output, Context context)
public static ContainerConfig getContainerConfig() {
return config;
}

@FunctionalInterface
interface ObjectWriter {

void writeValue(OutputStream os, Object value) throws IOException;
}

@FunctionalInterface
interface ObjectReader<T> {

T readValue(InputStream input) throws IOException;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,15 @@
import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
import com.amazonaws.serverless.proxy.model.ErrorModel;
import com.amazonaws.serverless.proxy.model.Headers;
import com.fasterxml.jackson.core.JsonProcessingException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;

/**
* Default {@link ExceptionHandler} implementation.
Expand Down Expand Up @@ -94,11 +95,12 @@ public void handle(Throwable ex, OutputStream stream) throws IOException {
* @return The error json
*/
protected String getErrorJson(String message) {
try {
return environment
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
environment
.getObjectMapper()
.writeValueAsString(new ErrorModel(message));
} catch (JsonProcessingException e) {
.writeValue(baos, new ErrorModel(message));
return baos.toString(StandardCharsets.UTF_8.name());
} catch (IOException e) {
LOG.error("Could not produce error JSON", e);
return "{ \"message\": \"" + message + "\" }";
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@
*/
package io.micronaut.function.aws.proxy;

import com.fasterxml.jackson.databind.ObjectMapper;
import io.micronaut.context.ApplicationContextProvider;
import io.micronaut.function.aws.MicronautLambdaContext;
import io.micronaut.jackson.codec.JsonMediaTypeCodec;
import io.micronaut.json.JsonMapper;
import io.micronaut.json.codec.JsonMediaTypeCodec;
import io.micronaut.web.router.Router;

/**
Expand All @@ -40,9 +40,9 @@ public interface MicronautLambdaContainerContext extends ApplicationContextProvi
JsonMediaTypeCodec getJsonCodec();

/**
* @return The Jackson's {@link ObjectMapper}
* @return The {@link JsonMapper}
*/
default ObjectMapper getObjectMapper() {
return getJsonCodec().getObjectMapper();
default JsonMapper getObjectMapper() {
Copy link
Contributor

Choose a reason for hiding this comment

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

There is a import io.micronaut.serde.ObjectMapper; why not use instead of JsonMapper?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

JsonCodec returns a JsonMapper, which isn't an ObjectMapper 😔

return getJsonCodec().getJsonMapper();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,18 @@
import com.amazonaws.serverless.proxy.AwsProxySecurityContextWriter;
import com.amazonaws.serverless.proxy.internal.jaxrs.AwsProxySecurityContext;
import com.amazonaws.serverless.proxy.internal.testutils.Timer;
import com.amazonaws.serverless.proxy.model.*;
import com.amazonaws.serverless.proxy.model.AlbContext;
import com.amazonaws.serverless.proxy.model.ApiGatewayAuthorizerContext;
import com.amazonaws.serverless.proxy.model.ApiGatewayRequestIdentity;
import com.amazonaws.serverless.proxy.model.AwsProxyRequest;
import com.amazonaws.serverless.proxy.model.AwsProxyRequestContext;
import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
import com.amazonaws.serverless.proxy.model.CognitoAuthorizerClaims;
import com.amazonaws.serverless.proxy.model.ContainerConfig;
import com.amazonaws.serverless.proxy.model.ErrorModel;
import com.amazonaws.serverless.proxy.model.Headers;
import com.amazonaws.serverless.proxy.model.MultiValuedTreeMap;
import com.amazonaws.services.lambda.runtime.Context;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectReader;
import com.fasterxml.jackson.databind.ObjectWriter;
import io.micronaut.context.ApplicationContext;
import io.micronaut.context.ApplicationContextBuilder;
import io.micronaut.context.ApplicationContextProvider;
Expand Down Expand Up @@ -55,8 +61,11 @@
import io.micronaut.http.server.exceptions.response.ErrorContext;
import io.micronaut.http.server.exceptions.response.ErrorResponseProcessor;
import io.micronaut.inject.qualifiers.Qualifiers;
import io.micronaut.jackson.codec.JsonMediaTypeCodec;
import io.micronaut.json.JsonMapper;
import io.micronaut.json.codec.JsonMediaTypeCodec;
import io.micronaut.scheduling.executor.ExecutorSelector;
import io.micronaut.serde.ObjectMapper;
import io.micronaut.serde.annotation.SerdeImport;
import io.micronaut.web.router.MethodBasedRouteMatch;
import io.micronaut.web.router.RouteInfo;
import io.micronaut.web.router.RouteMatch;
Expand All @@ -77,6 +86,7 @@
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
Expand Down Expand Up @@ -107,6 +117,14 @@
AwsProxySecurityContext.class
}
)
@SerdeImport(AwsProxyResponse.class)
@SerdeImport(AwsProxyRequest.class)
@SerdeImport(AwsProxyRequestContext.class)
@SerdeImport(MultiValuedTreeMap.class)
@SerdeImport(Headers.class)
@SerdeImport(ApiGatewayRequestIdentity.class)
@SerdeImport(ApiGatewayAuthorizerContext.class)
@SerdeImport(AlbContext.class)
public final class MicronautLambdaContainerHandler
extends AbstractLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse, MicronautAwsProxyRequest<?>, MicronautAwsProxyResponse<?>> implements ApplicationContextProvider, Closeable, AutoCloseable {

Expand Down Expand Up @@ -202,18 +220,18 @@ public ApplicationContext getApplicationContext() {
}

@Override
protected ObjectMapper objectMapper() {
protected JsonMapper objectMapper() {
Copy link
Contributor

Choose a reason for hiding this comment

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

why not use import io.micronaut.serde.ObjectMapper instead?

return lambdaContainerEnvironment.getObjectMapper();
}

@Override
protected ObjectWriter writerFor(Class<AwsProxyResponse> responseClass) {
return objectMapper().writerFor(responseClass);
return (outputStream, value) -> outputStream.write(objectMapper().writeValueAsBytes(value));
}

@Override
protected ObjectReader readerFor(Class<AwsProxyRequest> requestClass) {
return objectMapper().readerFor(requestClass);
protected ObjectReader<AwsProxyRequest> readerFor(Class<AwsProxyRequest> requestClass) {
return input -> objectMapper().readValue(input, Argument.of(requestClass));
}

@Override
Expand Down Expand Up @@ -422,12 +440,10 @@ private void decodeRequestBody(MicronautAwsProxyRequest<?> containerRequest, Rou
bodyArgument = bodyArgument.getFirstTypeVariable().orElse(Argument.OBJECT_ARGUMENT);
}
final Object decoded = lambdaContainerEnvironment.getJsonCodec().decode(bodyArgument, body.get());
((MicronautAwsProxyRequest) containerRequest)
.setDecodedBody(decoded);
((MicronautAwsProxyRequest) containerRequest).setDecodedBody(decoded);
} else {
final JsonNode jsonNode = lambdaContainerEnvironment.getJsonCodec().decode(JsonNode.class, body.get());
((MicronautAwsProxyRequest) containerRequest)
.setDecodedBody(jsonNode);
final Map<?, ?> node = lambdaContainerEnvironment.getJsonCodec().decode(Map.class, body.get());
((MicronautAwsProxyRequest) containerRequest).setDecodedBody(node);
Comment on lines +449 to +450
Copy link
Contributor Author

Choose a reason for hiding this comment

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

We can't decode to a serde JsonNode here (it fails to create one), so I fell back to a Map. This will break if the body is not a map (ie: a simple json string), but I'm not sure if Lambdas can receive simple strings

}
}
}
Expand Down Expand Up @@ -467,7 +483,7 @@ private void applyRouteConfig(MicronautAwsProxyResponse<?> containerResponse, Ro
if (!containerResponse.getContentType().isPresent()) {
finalRoute.getAnnotationMetadata().getValue(Produces.class, String.class).ifPresent(containerResponse::contentType);
}
finalRoute.getAnnotationMetadata().getValue(Status.class, HttpStatus.class).ifPresent(httpStatus -> containerResponse.status(httpStatus));
finalRoute.getAnnotationMetadata().getValue(Status.class, HttpStatus.class).ifPresent(containerResponse::status);
}

private void handlePossibleErrorStatus(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import io.micronaut.http.annotation.Get
import io.micronaut.http.annotation.Header
import io.micronaut.http.annotation.Post
import io.micronaut.http.annotation.Status
import io.micronaut.serde.annotation.Serdeable
import org.apache.commons.fileupload.FileItem
import org.reactivestreams.Publisher
import spock.lang.AutoCleanup
Expand Down Expand Up @@ -243,6 +244,7 @@ class BodySpec extends Specification {
}

@Canonical
@Serdeable
static class Point {
Integer x,y
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import io.micronaut.http.annotation.Body
import io.micronaut.http.annotation.Consumes
import io.micronaut.http.annotation.Controller
import io.micronaut.http.annotation.Post
import io.micronaut.serde.annotation.Serdeable
import spock.lang.AutoCleanup
import spock.lang.Shared
import spock.lang.Specification
Expand Down Expand Up @@ -53,6 +54,7 @@ class ConsumesSpec extends Specification {
}
}

@Serdeable
static class Pojo {
String name
}
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ projectVersion=3.3.0-SNAPSHOT
projectGroup=io.micronaut.aws

micronautDocsVersion=2.0.0
micronautVersion=3.3.4
micronautVersion=3.4.0
micronautTestVersion=3.0.5

groovyVersion=3.0.10
Expand Down
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ micronaut-mongo-sync = { module = 'io.micronaut.mongodb:micronaut-mongo-sync' }
micronaut-runtime = { module = 'io.micronaut:micronaut-runtime' }
micronaut-runtime-groovy = { module = 'io.micronaut.groovy:micronaut-runtime-groovy' }
micronaut-security = { module = 'io.micronaut.security:micronaut-security' }
micronaut-serde = { module = 'io.micronaut.serde:micronaut-serde-api' }
micronaut-serde-jackson = { module = 'io.micronaut.serde:micronaut-serde-jackson' }
micronaut-test-junit5 = { module = 'io.micronaut.test:micronaut-test-junit5' }
micronaut-validation = { module = 'io.micronaut:micronaut-validation' }
micronaut-views-handlebars = { module = 'io.micronaut.views:micronaut-views-handlebars' }
Expand Down