Skip to content

Commit

Permalink
Amazon Lambda - Support decorators
Browse files Browse the repository at this point in the history
We need to ignore decorators and abstract classes when selecting the
request handlers.

Fixes quarkusio#34824
  • Loading branch information
gsmet authored and bschuhmann committed Nov 16, 2024
1 parent f2a4885 commit dcc29ac
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 4 deletions.
5 changes: 5 additions & 0 deletions extensions/amazon-lambda/deployment/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>io.quarkus</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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;
Expand Down Expand Up @@ -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<ClassInfo> 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);
Expand All @@ -75,11 +88,13 @@ List<AmazonLambdaBuildItem> discover(CombinedIndexBuildItem combinedIndexBuildIt
BuildProducer<ReflectiveHierarchyBuildItem> reflectiveHierarchy,
BuildProducer<ReflectiveClassBuildItem> reflectiveClassBuildItemBuildProducer) throws BuildException {

Collection<ClassInfo> allKnownImplementors = combinedIndexBuildItem.getIndex().getAllKnownImplementors(REQUEST_HANDLER);
List<ClassInfo> 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(
Expand Down
Original file line number Diff line number Diff line change
@@ -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<InputPerson, String> {

@Override
public String handleRequest(InputPerson input, Context context) {
return "Hey " + input.getName();
}
}

@Priority(10)
@Decorator
public static class RequestHandlerDecorator<I, O> implements RequestHandler<I, O> {

@Inject
Logger logger;

@Inject
@Any
@Delegate
RequestHandler<I, O> delegate;

@Override
public O handleRequest(I i, Context context) {
logger.info("handling request with id " + context.getAwsRequestId());
return delegate.handleRequest(i, context);
}
}
}

0 comments on commit dcc29ac

Please sign in to comment.