Skip to content

Commit c85d8f1

Browse files
committed
aws testing endpoint
aws test event server hot replacement lambda hot replacement lambda cleanup hot replacement lambda cleanup 2 lambda http finish add gson to benchmarks do not enable mock lambda if LambdaClint final dev mode touches lambda lambda native testing comment comment finish it process resources fix banned dependency fix banned dependency fixes debug lambda test mode finish mock event initial process res logging remove lambda fixes lambda continuous testing test
1 parent 8ff7cfe commit c85d8f1

File tree

70 files changed

+2046
-380
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

70 files changed

+2046
-380
lines changed

bom/application/pom.xml

+15
Original file line numberDiff line numberDiff line change
@@ -1928,6 +1928,21 @@
19281928
<artifactId>quarkus-amazon-common</artifactId>
19291929
<version>${project.version}</version>
19301930
</dependency>
1931+
<dependency>
1932+
<groupId>io.quarkus</groupId>
1933+
<artifactId>quarkus-amazon-lambda-event-server</artifactId>
1934+
<version>${project.version}</version>
1935+
</dependency>
1936+
<dependency>
1937+
<groupId>io.quarkus</groupId>
1938+
<artifactId>quarkus-amazon-lambda-http-event-server</artifactId>
1939+
<version>${project.version}</version>
1940+
</dependency>
1941+
<dependency>
1942+
<groupId>io.quarkus</groupId>
1943+
<artifactId>quarkus-amazon-lambda-rest-event-server</artifactId>
1944+
<version>${project.version}</version>
1945+
</dependency>
19311946
<dependency>
19321947
<groupId>io.quarkus</groupId>
19331948
<artifactId>quarkus-amazon-common-deployment</artifactId>

devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/amazon-lambda-example/codestart.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,4 @@ language:
1111
dependencies:
1212
- io.quarkus:quarkus-amazon-lambda
1313
test-dependencies:
14-
- io.quarkus:quarkus-test-amazon-lambda
14+
- io.rest-assured:rest-assured
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,31 @@
11
package org.acme.lambda;
22

3-
import org.junit.jupiter.api.Assertions;
43
import org.junit.jupiter.api.Test;
54

6-
import io.quarkus.amazon.lambda.test.LambdaClient;
75
import io.quarkus.test.junit.QuarkusTest;
86

7+
import static io.restassured.RestAssured.given;
8+
import static org.hamcrest.CoreMatchers.containsString;
9+
910
@QuarkusTest
1011
public class LambdaHandlerTest {
1112

1213
@Test
1314
public void testSimpleLambdaSuccess() throws Exception {
15+
// you test your lambas by invoking on http://localhost:8081
16+
// this works in dev mode too
17+
1418
Person in = new Person();
1519
in.setName("Stu");
16-
String out = LambdaClient.invoke(String.class, in);
17-
Assertions.assertEquals("Hello Stu", out);
20+
given()
21+
.contentType("application/json")
22+
.accept("application/json")
23+
.body(in)
24+
.when()
25+
.post()
26+
.then()
27+
.statusCode(200)
28+
.body(containsString("Hello Stu"));
1829
}
1930

2031
}

devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/amazon-lambda-example/java/src/test/resources/application.yml

-3
This file was deleted.

devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/funqy-amazon-lambda-example/codestart.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,4 @@ language:
1414
dependencies:
1515
- io.quarkus:quarkus-funqy-amazon-lambda
1616
test-dependencies:
17-
- io.quarkus:quarkus-test-amazon-lambda
17+
- io.rest-assured:rest-assured
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,31 @@
11
package org.acme.funqy;
22

3-
import io.quarkus.amazon.lambda.test.LambdaClient;
4-
import io.quarkus.test.junit.QuarkusTest;
5-
import org.junit.jupiter.api.Assertions;
63
import org.junit.jupiter.api.Test;
4+
import io.quarkus.test.junit.QuarkusTest;
5+
6+
import static io.restassured.RestAssured.given;
7+
import static org.hamcrest.CoreMatchers.containsString;
78

89
@QuarkusTest
910
public class FunqyTest {
11+
1012
@Test
11-
public void testSimpleLambdaSuccess() throws Exception {
12-
Person friend = new Person("Bill");
13-
String out = LambdaClient.invoke(String.class, friend);
14-
Assertions.assertEquals("Hello Bill", out);
13+
public void testFunqyLambda() throws Exception {
14+
// you test your lambas by invoking on http://localhost:8081
15+
// this works in dev mode too
16+
17+
Person in = new Person();
18+
in.setName("Bill");
19+
given()
20+
.contentType("application/json")
21+
.accept("application/json")
22+
.body(in)
23+
.when()
24+
.post()
25+
.then()
26+
.statusCode(200)
27+
.body(containsString("Hello Bill"));
1528
}
29+
1630
}
31+

devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/examples/funqy-amazon-lambda-example/java/src/test/resources/application.yml

-3
This file was deleted.

docs/src/main/asciidoc/amazon-lambda-http.adoc

+77-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ Quarkus has a different extension for each Gateway API. The HTTP Gateway API is
1919
The REST Gateway API is implemented within the `quarkus-amazon-lambda-rest` extension. If you are confused on which Gateway product to use,
2020
Amazon has a https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-vs-rest.html[great guide] to help you navigate this decision.
2121

22+
Like most Quarkus extensions, the Quarkus AWS Lambda HTTP/REST extensions support Live Coding.
2223

2324
include::./status-include.adoc[]
2425

@@ -105,7 +106,82 @@ are in the the build directory: `target/` for Maven, `build/` for Gradle.
105106
* `sam.jvm.yaml` - sam cli deployment script
106107
* `sam.native.yaml` - sam cli deployment script for native
107108

108-
== Simulate Amazon Lambda Deployment
109+
== Live Coding and Simulating AWS Lambda Environment Locally
110+
111+
In dev and test mode, Quarkus will start a mock AWS Lambda event server
112+
that will convert HTTP requests to the corresponding API Gateway event types and post them to the underlying
113+
Quarkus HTTP lambda environment for processing. This simulates the AWS Lambda environment
114+
as much as possible locally without requiring tools like Docker and SAM CLI.
115+
116+
When using Quarkus Dev Mode just invoke HTTP requests on `http://localhost:8080`
117+
as you normally would when testing your REST endpoints. This request will hit the Mock Event Server and will
118+
be converted to the API Gateway json message that is consumed by the Quarkus Lambda Poll loop.
119+
120+
For testing, Quarkus starts up a separate Mock Event server under port 8081. The default port for Rest Assured is
121+
automatically set to 8081 by Quarkus, so you don't have to worry about setting this up.
122+
123+
If you want to simulate more complex
124+
API Gateway events in your tests, then manually do an HTTP POST to `http://localhost:8080/\_lambda_` (port 8081 in test mode) with
125+
the raw API Gateway json events. These events will be placed directly on the Quarkus Lambda poll loop
126+
for processing. Here's an example of that:
127+
128+
[source,java]
129+
----
130+
import static io.restassured.RestAssured.given;
131+
import static org.hamcrest.CoreMatchers.equalTo;
132+
133+
import com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPEvent;
134+
135+
import io.quarkus.test.junit.QuarkusTest;
136+
137+
@QuarkusTest
138+
public class AmazonLambdaSimpleTestCase {
139+
@Test
140+
public void testJaxrsCognitoJWTSecurityContext() throws Exception {
141+
APIGatewayV2HTTPEvent request = request("/security/username");
142+
request.getRequestContext().setAuthorizer(new APIGatewayV2HTTPEvent.RequestContext.Authorizer());
143+
request.getRequestContext().getAuthorizer().setJwt(new APIGatewayV2HTTPEvent.RequestContext.Authorizer.JWT());
144+
request.getRequestContext().getAuthorizer().getJwt().setClaims(new HashMap<>());
145+
request.getRequestContext().getAuthorizer().getJwt().getClaims().put("cognito:username", "Bill");
146+
147+
given()
148+
.contentType("application/json")
149+
.accept("application/json")
150+
.body(request)
151+
.when()
152+
.post("/_lambda_")
153+
.then()
154+
.statusCode(200)
155+
.body("body", equalTo("Bill"));
156+
}
157+
----
158+
159+
The above example simulates sending a Cognito principal with an HTTP request to your HTTP Lambda.
160+
161+
If you want to hand code raw events for the AWS HTTP API, the AWS Lambda library has the request event type which is
162+
`com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPEvent` and the response event type
163+
of `com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPResponse`. This corresponds
164+
to the `quarkus-amazon-lambda-http` extension and the AWS HTTP API.
165+
166+
If you want to hand code raw events for the AWS REST API, Quarkus has its own implementation: `io.quarkus.amazon.lambda.http.model.AwsProxyRequest`
167+
and `io.quarkus.amazon.lambda.http.model.AwsProxyResponse`. This corresponds
168+
to `quarkus-amazon-lambda-rest` extension and the AWS REST API.
169+
170+
The mock event server is also started for `@NativeImageTest` unit tests so will work
171+
with native binaries too. All this provides similar functionality to the SAM CLI local testing, without the overhead of Docker.
172+
173+
Finally, if port 8080 or port 8081 is not available on your computer, you can modify the dev
174+
and test mode ports with application.properties
175+
176+
[source, subs=attributes+]
177+
----
178+
quarkus.lambda.mock-event-server.dev-port=8082
179+
quarkus.lambda.mock-event-server.test-port=8083
180+
----
181+
182+
183+
184+
== Simulate Amazon Lambda Deployment with SAM CLI
109185

110186
The AWS SAM CLI allows you to run your lambda's locally on your laptop in a simulated Lambda environment. This requires Docker to be installed.
111187
After you have built your Maven project, execute this command:

docs/src/main/asciidoc/amazon-lambda.adoc

+44-66
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ Your lambdas can use injection annotations from CDI or Spring and other Quarkus
1414
Quarkus lambdas can be deployed using the Amazon Java Runtime, or you can build a native executable and use
1515
Amazon's Custom Runtime if you want a smaller memory footprint and faster cold boot startup time.
1616

17+
Quarkus's integration with lambdas also supports Quarkus's Live Coding development cycle. You an
18+
bring up your Quarkus lambda project in dev or test mode and code on your project live.
19+
1720
include::./status-include.adoc[]
1821

1922
== Prerequisites
@@ -330,96 +333,71 @@ dependencies {
330333
----
331334

332335

333-
== Integration Testing
334-
The Quarkus Amazon Lambda extension has a matching test framework that provides functionality to execute standard JUnit tests on your AWS Lambda function,
335-
via the integration layer that Quarkus provides. This is true for both JVM and native modes.
336-
It provides similar functionality to the SAM CLI, without the overhead of Docker.
337-
338-
To illustrate, the project generated by the Maven archetype, generates a JUnit test for the `RequestHandler<?, ?>` implementation, which is shown below.
339-
The test replicates the execution environment, for the function that is selected for invocation, as described <<choose, above>>.
336+
== Live Coding and Unit/Integration Testing
337+
To mirror the AWS Lambda environment as closely as possible in a dev environment,
338+
the Quarkus Amazon Lambda extension boots up a mock AWS Lambda event server in Quarkus Dev and Test mode.
339+
This mock event server simulates a true AWS Lambda environment.
340340

341-
To use the integration tests in your project there is a required property, in `src/test/resources/application.properties`. If not included, the integration tests will be in a constant loop.
341+
While running in Quarkus Dev Mode, you can feed events to it by doing an HTTP POST to `http://localhost:8080`.
342+
The mock event server will receive the events and your lambda will be invoked. You can perform live coding on your lambda
343+
and changes will automatically be recompiled and available the next invocation you make. Here's an example:
342344

343-
[source,properties]
344-
----
345-
quarkus.lambda.enable-polling-jvm-mode=true
345+
[source,bash]
346346
----
347+
$ mvn clean quarkus:dev
347348
348-
NOTE: If you are following along with the example Maven archetype project for AWS Lambda in this guide,
349-
it includes the required property `quarkus.lambda.enable-polling-jvm-mode=true` in the test `application.properties`.
350-
351-
[source,java]
349+
$ curl -d "{\"name\":\"John\"}" -X POST http://localhost:8080
352350
----
353-
@QuarkusTest
354-
public class LambdaHandlerTest {
355351

356-
@Test
357-
public void testSimpleLambdaSuccess() throws Exception {
358-
InputObject in = new InputObject();
359-
in.setGreeting("Hello");
360-
in.setName("Stu");
352+
For your unit tests, you can also invoke on the mock event server using any HTTP client you want. Here's an example
353+
using rest-assured. Quarkus starts up a separate Mock Event server under port 8081.
354+
The default port for Rest Assured is automatically set to 8081 by Quarkus so you can invoke
355+
on this endpoint.
361356

362-
OutputObject out = LambdaClient.invoke(OutputObject.class, in);
363357

364-
Assertions.assertEquals("Hello Stu", out.getResult());
365-
Assertions.assertTrue(out.getRequestId().matches("aws-request-\\d"), "Expected requestId as 'aws-request-<number>'");
366-
}
367-
}
358+
[source,java]
368359
----
360+
import org.junit.jupiter.api.Test;
369361
370-
Similarly, if you are using a `RequestStreamHandler` implementation, you can add a matching JUnit test, like below,
371-
which aligns to the generated `StreamLambda` class in the generated project.
372-
373-
Obviously, these two types of tests are mutually exclusive. You must have a test that corresponds to the implemented AWS Lambda interfaces,
374-
whether `RequestHandler<?, ?>` or `RequestStreamHandler`.
362+
import io.quarkus.test.junit.QuarkusTest;
375363
376-
Two versions of the Test for `RequestStreamHandler` are presented below. You can use either, depending on
377-
the needs of your Unit test. The first is obviously simpler and quicker. Using Java streams can require more coding.
364+
import static io.restassured.RestAssured.given;
365+
import static org.hamcrest.CoreMatchers.containsString;
378366
379-
[source,java]
380-
----
381367
@QuarkusTest
382-
public class LambdaStreamHandlerTest {
383-
384-
private static Logger LOG = Logger.getLogger(LambdaStreamHandlerTest.class);
368+
public class LambdaHandlerTest {
385369
386370
@Test
387371
public void testSimpleLambdaSuccess() throws Exception {
388-
String out = LambdaClient.invoke(String.class, "lowercase");
389-
Assertions.assertEquals("LOWERCASE", out);
390-
}
391-
392-
@Test
393-
public void testInputStreamSuccess() {
394-
try {
395-
String input = "{ \"name\": \"Bill\", \"greeting\": \"hello\"}";
396-
InputStream inputStream = new ByteArrayInputStream(input.getBytes());
397-
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
398-
399-
LambdaClient.invoke(inputStream, outputStream);
400-
401-
ByteArrayInputStream out = new ByteArrayInputStream(outputStream.toByteArray());
402-
StringBuilder response = new StringBuilder();
403-
int i = 0;
404-
while ((i = out.read()) != -1) {
405-
response.append((char)i);
406-
}
407-
408-
Assertions.assertTrue(response.toString().contains("BILL"));
409-
} catch (Exception e) {
410-
Assertions.fail(e.getMessage());
411-
}
372+
Person in = new Person();
373+
in.setName("Stu");
374+
given()
375+
.contentType("application/json")
376+
.accept("application/json")
377+
.body(in)
378+
.when()
379+
.post()
380+
.then()
381+
.statusCode(200)
382+
.body(containsString("Hello Stu"));
412383
}
413-
414384
}
415385
----
416386

417-
If your code uses CDI injection, this too will be executed, along with mocking functionality, see the link:getting-started-testing[Test Guide] for more details.
387+
The mock event server is also started for `@NativeImageTest` unit tests so will work
388+
with native binaries too. All this provides similar functionality to the SAM CLI local testing, without the overhead of Docker.
418389

419-
To add JUnit functionality for native tests, add the `@NativeImageTest` annotation to a subclass of your test class, which will execute against your native image, and can be leveraged in an IDE.
390+
Finally, if port 8080 or port 8081 is not available on your computer, you can modify the dev
391+
and test mode ports with application.properties
420392

393+
[source, subs=attributes+]
394+
----
395+
quarkus.lambda.mock-event-server.dev-port=8082
396+
quarkus.lambda.mock-event-server.test-port=8083
397+
----
421398

422399
== Testing with the SAM CLI
400+
If you do not want to use the mock event server, you can test your lambdas with SAM CLI.
423401

424402
The https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html[AWS SAM CLI]
425403
allows you to run your lambdas locally on your laptop in a simulated Lambda environment. This requires

extensions/amazon-lambda-http/deployment/pom.xml

+4
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@
3535
<groupId>io.quarkus</groupId>
3636
<artifactId>quarkus-amazon-lambda-http</artifactId>
3737
</dependency>
38+
<dependency>
39+
<groupId>io.quarkus</groupId>
40+
<artifactId>quarkus-amazon-lambda-http-event-server</artifactId>
41+
</dependency>
3842
</dependencies>
3943
<build>
4044
<plugins>

extensions/amazon-lambda-http/deployment/src/main/java/io/quarkus/amazon/lambda/http/deployment/AmazonLambdaHttpProcessor.java

+2-4
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,10 @@
1515
import io.quarkus.arc.deployment.AdditionalBeanBuildItem;
1616
import io.quarkus.deployment.annotations.BuildProducer;
1717
import io.quarkus.deployment.annotations.BuildStep;
18-
import io.quarkus.deployment.builditem.LaunchModeBuildItem;
1918
import io.quarkus.deployment.builditem.SystemPropertyBuildItem;
2019
import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem;
2120
import io.quarkus.deployment.pkg.builditem.ArtifactResultBuildItem;
2221
import io.quarkus.deployment.pkg.builditem.OutputTargetBuildItem;
23-
import io.quarkus.runtime.LaunchMode;
2422
import io.quarkus.vertx.http.deployment.RequireVirtualHttpBuildItem;
2523
import io.vertx.core.file.impl.FileResolver;
2624

@@ -41,8 +39,8 @@ public void setupSecurity(BuildProducer<AdditionalBeanBuildItem> additionalBeans
4139
}
4240

4341
@BuildStep
44-
public RequireVirtualHttpBuildItem requestVirtualHttp(LaunchModeBuildItem launchMode) {
45-
return launchMode.getLaunchMode() == LaunchMode.NORMAL ? RequireVirtualHttpBuildItem.MARKER : null;
42+
public RequireVirtualHttpBuildItem requestVirtualHttp() {
43+
return RequireVirtualHttpBuildItem.ALWAYS_VIRTUAL;
4644
}
4745

4846
@BuildStep

0 commit comments

Comments
 (0)