From dcc29ac2df2788b122a22329ca36914a753f69c0 Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Tue, 29 Oct 2024 11:37:41 +0100 Subject: [PATCH] Amazon Lambda - Support decorators We need to ignore decorators and abstract classes when selecting the request handlers. Fixes #34824 --- extensions/amazon-lambda/deployment/pom.xml | 5 ++ .../deployment/AmazonLambdaProcessor.java | 23 ++++- .../testing/LambdaWithDecoratorTest.java | 83 +++++++++++++++++++ 3 files changed, 107 insertions(+), 4 deletions(-) create mode 100644 extensions/amazon-lambda/deployment/src/test/java/io/quarkus/amazon/lambda/deployment/testing/LambdaWithDecoratorTest.java diff --git a/extensions/amazon-lambda/deployment/pom.xml b/extensions/amazon-lambda/deployment/pom.xml index 24060850709a8a..083bdd130e5319 100644 --- a/extensions/amazon-lambda/deployment/pom.xml +++ b/extensions/amazon-lambda/deployment/pom.xml @@ -29,6 +29,11 @@ rest-assured test + + org.assertj + assertj-core + test + io.quarkus diff --git a/extensions/amazon-lambda/deployment/src/main/java/io/quarkus/amazon/lambda/deployment/AmazonLambdaProcessor.java b/extensions/amazon-lambda/deployment/src/main/java/io/quarkus/amazon/lambda/deployment/AmazonLambdaProcessor.java index 33d9a166154822..d7b631b826e24a 100644 --- a/extensions/amazon-lambda/deployment/src/main/java/io/quarkus/amazon/lambda/deployment/AmazonLambdaProcessor.java +++ b/extensions/amazon-lambda/deployment/src/main/java/io/quarkus/amazon/lambda/deployment/AmazonLambdaProcessor.java @@ -3,13 +3,13 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.function.Predicate; import jakarta.inject.Named; @@ -29,6 +29,7 @@ import io.quarkus.amazon.lambda.runtime.LambdaBuildTimeConfig; import io.quarkus.arc.deployment.AdditionalBeanBuildItem; import io.quarkus.arc.deployment.BeanContainerBuildItem; +import io.quarkus.arc.processor.DotNames; import io.quarkus.builder.BuildException; import io.quarkus.deployment.Feature; import io.quarkus.deployment.annotations.BuildProducer; @@ -58,6 +59,18 @@ public final class AmazonLambdaProcessor { private static final DotName NAMED = DotName.createSimple(Named.class.getName()); private static final Logger log = Logger.getLogger(AmazonLambdaProcessor.class); + private static final Predicate INCLUDE_HANDLER_PREDICATE = new Predicate<>() { + + @Override + public boolean test(ClassInfo classInfo) { + if (classInfo.isAbstract() || classInfo.hasAnnotation(DotNames.DECORATOR)) { + return false; + } + + return true; + } + }; + @BuildStep FeatureBuildItem feature() { return new FeatureBuildItem(Feature.AMAZON_LAMBDA); @@ -75,11 +88,13 @@ List discover(CombinedIndexBuildItem combinedIndexBuildIt BuildProducer reflectiveHierarchy, BuildProducer reflectiveClassBuildItemBuildProducer) throws BuildException { - Collection allKnownImplementors = combinedIndexBuildItem.getIndex().getAllKnownImplementors(REQUEST_HANDLER); + List allKnownImplementors = new ArrayList<>( + combinedIndexBuildItem.getIndex().getAllKnownImplementors(REQUEST_HANDLER) + .stream().filter(INCLUDE_HANDLER_PREDICATE).toList()); allKnownImplementors.addAll(combinedIndexBuildItem.getIndex() - .getAllKnownImplementors(REQUEST_STREAM_HANDLER)); + .getAllKnownImplementors(REQUEST_STREAM_HANDLER).stream().filter(INCLUDE_HANDLER_PREDICATE).toList()); allKnownImplementors.addAll(combinedIndexBuildItem.getIndex() - .getAllKnownSubclasses(SKILL_STREAM_HANDLER)); + .getAllKnownSubclasses(SKILL_STREAM_HANDLER).stream().filter(INCLUDE_HANDLER_PREDICATE).toList()); if (allKnownImplementors.size() > 0 && providedLambda.isPresent()) { throw new BuildException( diff --git a/extensions/amazon-lambda/deployment/src/test/java/io/quarkus/amazon/lambda/deployment/testing/LambdaWithDecoratorTest.java b/extensions/amazon-lambda/deployment/src/test/java/io/quarkus/amazon/lambda/deployment/testing/LambdaWithDecoratorTest.java new file mode 100644 index 00000000000000..e1c286131eb58c --- /dev/null +++ b/extensions/amazon-lambda/deployment/src/test/java/io/quarkus/amazon/lambda/deployment/testing/LambdaWithDecoratorTest.java @@ -0,0 +1,83 @@ +package io.quarkus.amazon.lambda.deployment.testing; + +import static io.restassured.RestAssured.given; +import static org.assertj.core.api.Assertions.assertThat; +import static org.hamcrest.CoreMatchers.containsString; + +import java.util.logging.Level; +import java.util.logging.LogRecord; + +import jakarta.annotation.Priority; +import jakarta.decorator.Decorator; +import jakarta.decorator.Delegate; +import jakarta.enterprise.inject.Any; +import jakarta.inject.Inject; + +import org.jboss.logging.Logger; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; + +import io.quarkus.amazon.lambda.deployment.testing.model.InputPerson; +import io.quarkus.test.QuarkusUnitTest; + +class LambdaWithDecoratorTest { + + @RegisterExtension + static final QuarkusUnitTest test = new QuarkusUnitTest().setArchiveProducer(() -> ShrinkWrap + .create(JavaArchive.class) + .addClasses(LambdaWithDecorator.class, RequestHandlerDecorator.class, InputPerson.class)) + .setLogRecordPredicate(record -> record.getLevel().intValue() == Level.INFO.intValue() + && record.getMessage().contains("handling request with id")) + .assertLogRecords(records -> assertThat(records) + .extracting(LogRecord::getMessage) + .isNotEmpty()); + + @Test + public void testLambdaWithDecorator() throws Exception { + // you test your lambdas by invoking on http://localhost:8081 + // this works in dev mode too + + InputPerson in = new InputPerson("Stu"); + given() + .contentType("application/json") + .accept("application/json") + .body(in) + .when() + .post() + .then() + .statusCode(200) + .body(containsString("Hey Stu")); + } + + public static class LambdaWithDecorator implements RequestHandler { + + @Override + public String handleRequest(InputPerson input, Context context) { + return "Hey " + input.getName(); + } + } + + @Priority(10) + @Decorator + public static class RequestHandlerDecorator implements RequestHandler { + + @Inject + Logger logger; + + @Inject + @Any + @Delegate + RequestHandler delegate; + + @Override + public O handleRequest(I i, Context context) { + logger.info("handling request with id " + context.getAwsRequestId()); + return delegate.handleRequest(i, context); + } + } +}