diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 81e95fb..f8ce09d 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -27,6 +27,21 @@ jobs:
java-version: '17'
distribution: 'temurin'
cache: gradle
+ - name: Restore gradle.properties
+ env:
+ GRADLE_PROPERTIES: ${{ secrets.GRADLE_PROPERTIES }}
+ shell: bash
+ run: |
+ mkdir -p ~/.gradle/
+ echo "GRADLE_USER_HOME=${HOME}/.gradle" >> $GITHUB_ENV
+ echo "${GRADLE_PROPERTIES}" > ~/.gradle/gradle.properties
+ - name: Restore gpg key
+ env:
+ GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
+ shell: bash
+ run: |
+ mkdir /home/runner/.gnupg
+ echo -n "${{ secrets.GPG_PRIVATE_KEY }}" | base64 --decode > /home/runner/.gnupg/secring.gpg
- name: Execute Gradle build
run: ./gradlew clean build --refresh-dependencies --info
- name: Upload test reports
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index 797f843..76809d6 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -5,7 +5,7 @@
# This workflow will build a Java project with Gradle and cache/restore any dependencies to improve the workflow execution time
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle
-name: Sonatype Publish
+name: Publish Central Portal
on:
workflow_dispatch:
diff --git a/.gitignore b/.gitignore
index bf8cfc9..b94b674 100644
--- a/.gitignore
+++ b/.gitignore
@@ -27,3 +27,5 @@ hs_err_pid*
/build/
gradle.properties
/bin/
+
+.idea
diff --git a/build.gradle b/build.gradle
index a27a9ae..4eef135 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,16 +1,16 @@
+import com.vanniktech.maven.publish.SonatypeHost
plugins {
id 'java'
- id 'maven-publish'
- id 'signing'
id 'checkstyle'
- id 'com.diffplug.spotless' version '6.25.0'
- id 'com.github.spotbugs' version '6.0.7'
- id 'com.github.ben-manes.versions' version '0.51.0'
+ id 'com.vanniktech.maven.publish' version '0.32.0'
+ id 'com.diffplug.spotless' version '7.0.3'
+ id 'com.github.spotbugs' version '6.1.11'
+ id 'com.github.ben-manes.versions' version '0.52.0'
}
group 'com.formkiq'
-version '2.5.0'
+version '2.6.0'
spotbugs {
excludeFilter = file("$rootDir/config/spotbugs/spotbugs-exclude.xml")
@@ -23,15 +23,15 @@ spotless {
}
spotbugsMain {
- reports {
- html {
- enabled = true
+ reports {
+ html {
+ required.set(true)
+ }
}
- }
-}
+}
checkstyle {
- toolVersion '8.29'
+ toolVersion = '10.12.1'
configFile file("config/checkstyle/checkstyle.xml")
configProperties = [project_loc: "${projectDir}"]
ignoreFailures = false
@@ -41,96 +41,61 @@ checkstyle {
checkstyleMain.dependsOn spotlessApply
repositories {
- jcenter()
-}
-
-check {
- dependsOn(tasks.publishToMavenLocal)
+ mavenLocal()
+ mavenCentral()
}
java {
- withJavadocJar()
- withSourcesJar()
-
toolchain {
languageVersion.set(JavaLanguageVersion.of(17))
}
}
-javadoc {
- if(JavaVersion.current().isJava9Compatible()) {
- options.addBooleanOption('html5', true)
- }
-}
-
-artifacts {
- archives jar
-
- archives javadocJar
- archives sourcesJar
-}
-
-afterEvaluate {
- tasks.getByName('spotlessCheck').dependsOn(tasks.getByName('spotlessApply'))
-}
-
-
dependencies {
implementation group: 'com.amazonaws', name: 'aws-lambda-java-core', version: '1.2.3'
- implementation group: 'com.google.code.gson', name: 'gson', version: '2.10.1'
- implementation group: 'com.amazonaws', name: 'aws-lambda-java-events', version: '3.11.4'
- testImplementation group: 'junit', name: 'junit', version:'4.+'
+ implementation group: 'com.google.code.gson', name: 'gson', version: '2.13.1'
+ implementation group: 'joda-time', name: 'joda-time', version: '2.14.0'
+
+ testImplementation group: 'com.amazonaws', name: 'aws-lambda-java-events', version: '3.15.0'
+ testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version:'5.11.4'
testImplementation group: 'org.mock-server', name: 'mockserver-netty', version: '5.15.0'
- testImplementation group: 'org.slf4j', name: 'slf4j-simple', version: '2.0.9'
+ testImplementation group: 'org.slf4j', name: 'slf4j-simple', version: '2.0.17'
}
-publishing {
- publications {
- mavenJava(MavenPublication) {
- artifactId = 'lambda-runtime-graalvm'
- from components.java
-
- pom {
- name = 'FormKiQ Lambda Runtime Graalvm'
- description = 'Lambda Runtime Graalvm'
- url = 'https://github.com/formkiq/lambda-runtime-graalvm'
- licenses {
- license {
- name = 'The Apache License, Version 2.0'
- url = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
- }
- }
- developers {
- developer {
- id = 'mfriesen'
- name = 'Mike Friesen'
- email = 'mike@formkiq.com'
- }
- }
- scm {
- connection = 'scm:git:git://github.com/formkiq/lambda-runtime-graalvm.git'
- developerConnection = 'scm:git:ssh://github.com/formkiq/lambda-runtime-graalvm.git'
- url = 'https://github.com/formkiq/lambda-runtime-graalvm.git'
- }
+mavenPublishing {
+
+ publishToMavenCentral(SonatypeHost.CENTRAL_PORTAL)
+
+ pom {
+ name = "FormKiQ Lambda Runtime Graalvm"
+ description = "Lambda Runtime Graalvm"
+ inceptionYear = "2020"
+ url = "https://github.com/formkiq/lambda-runtime-graalvm"
+ licenses {
+ license {
+ name = "The Apache License, Version 2.0"
+ url = "http://www.apache.org/licenses/LICENSE-2.0.txt"
+ distribution = "http://www.apache.org/licenses/LICENSE-2.0.txt"
}
- }
- }
- repositories {
- maven {
- credentials {
- username project.repoUser
- password project.repoPassword
- }
- url "https://oss.sonatype.org/service/local/staging/deploy/maven2/"
- //url "https://oss.sonatype.org/content/repositories/snapshots/"
- }
- }
+ }
+ developers {
+ developer {
+ id = 'mfriesen'
+ name = 'Mike Friesen'
+ }
+ }
+ scm {
+ url = 'https://github.com/formkiq/lambda-runtime-graalvm.git'
+ connection = 'scm:git:git://github.com/formkiq/lambda-runtime-graalvm.git'
+ developerConnection = 'scm:git:ssh://github.com/formkiq/lambda-runtime-graalvm.git'
+ }
+ }
}
-signing {
- sign publishing.publications.mavenJava
-}
+compileJava.dependsOn(tasks.spotlessApply)
+check.dependsOn(tasks.publishToMavenLocal)
-check {
- dependsOn(tasks.publishToMavenLocal)
+test {
+ failFast = true
+ useJUnitPlatform()
}
diff --git a/config/checkstyle/import-control.xml b/config/checkstyle/import-control.xml
index 2cd91e3..0fd4bc7 100644
--- a/config/checkstyle/import-control.xml
+++ b/config/checkstyle/import-control.xml
@@ -10,7 +10,8 @@
-
+
+
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 37aef8d..3c44eb1 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip
networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/src/main/java/com/formkiq/lambda/runtime/graalvm/AwsEventsExclusionStrategy.java b/src/main/java/com/formkiq/lambda/runtime/graalvm/AwsEventsExclusionStrategy.java
new file mode 100644
index 0000000..da44684
--- /dev/null
+++ b/src/main/java/com/formkiq/lambda/runtime/graalvm/AwsEventsExclusionStrategy.java
@@ -0,0 +1,27 @@
+package com.formkiq.lambda.runtime.graalvm;
+
+import com.google.gson.ExclusionStrategy;
+import com.google.gson.FieldAttributes;
+import java.nio.ByteBuffer;
+import java.util.Collection;
+import java.util.List;
+
+/** {@link ExclusionStrategy} for Aws Events. */
+public class AwsEventsExclusionStrategy implements ExclusionStrategy {
+
+ /** Skip Classes. */
+ private static final Collection> CLASSES = List.of(ByteBuffer.class);
+
+ /** Skip Fields. */
+ private static final Collection FIELDS = List.of("approximateCreationDateTime");
+
+ @Override
+ public boolean shouldSkipField(final FieldAttributes fieldAttributes) {
+ return FIELDS.contains(fieldAttributes.getName());
+ }
+
+ @Override
+ public boolean shouldSkipClass(final Class> clazz) {
+ return CLASSES.contains(clazz);
+ }
+}
diff --git a/src/main/java/com/formkiq/lambda/runtime/graalvm/AwsEventsFieldNamingStrategy.java b/src/main/java/com/formkiq/lambda/runtime/graalvm/AwsEventsFieldNamingStrategy.java
index 964cf64..941515a 100644
--- a/src/main/java/com/formkiq/lambda/runtime/graalvm/AwsEventsFieldNamingStrategy.java
+++ b/src/main/java/com/formkiq/lambda/runtime/graalvm/AwsEventsFieldNamingStrategy.java
@@ -1,76 +1,61 @@
package com.formkiq.lambda.runtime.graalvm;
-import com.amazonaws.services.lambda.runtime.events.DynamodbEvent;
-import com.amazonaws.services.lambda.runtime.events.SQSEvent;
-import com.amazonaws.services.lambda.runtime.events.models.dynamodb.AttributeValue;
-import com.amazonaws.services.lambda.runtime.events.models.dynamodb.StreamRecord;
-import com.amazonaws.services.lambda.runtime.events.models.s3.S3EventNotification;
+import com.google.gson.FieldNamingPolicy;
import com.google.gson.FieldNamingStrategy;
import java.lang.reflect.Field;
-import java.util.Map;
+import java.util.List;
+import java.util.Locale;
+import java.util.stream.Stream;
/** AWS Events Field {@link FieldNamingStrategy}. */
public class AwsEventsFieldNamingStrategy implements FieldNamingStrategy {
- /** {@link DynamodbEvent} Field Mapping. */
- private static final Map, Map> FIELD_MAPPING =
- Map.of(
- DynamodbEvent.class,
- Map.of("records", "Records"),
- StreamRecord.class,
- Map.of(
- "newImage",
- "NewImage",
- "oldImage",
- "OldImage",
- "keys",
- "Keys",
- "sequenceNumber",
- "SequenceNumber",
- "sizeBytes",
- "SizeBytes",
- "streamViewType",
- "StreamViewType"),
- AttributeValue.class,
- Map.of(
- "s",
- "S",
- "n",
- "N",
- "sS",
- "SS",
- "nS",
- "NS",
- "m",
- "M",
- "l",
- "L",
- "nULLValue",
- "NULLValue",
- "bOOL",
- "BOOL"),
- S3EventNotification.class,
- Map.of("records", "Records"),
- S3EventNotification.S3EventNotificationRecord.class,
- Map.of("eventTime", "EventTime"),
- S3EventNotification.ResponseElementsEntity.class,
- Map.of("xAmzId2", "x-amz-id-2", "xAmzRequestId", "x-amz-request-id"),
- SQSEvent.class,
- Map.of("records", "Records"),
- SQSEvent.SQSMessage.class,
- Map.of("eventSourceArn", "eventSourceARN"));
-
@Override
- public String translateName(final Field field) {
-
- String fieldName = field.getName();
+ public String translateName(final Field f) {
+ return f.getName();
+ }
- Map fields = FIELD_MAPPING.get(field.getDeclaringClass());
+ @Override
+ public List alternateNames(final Field field) {
+ String fieldName = translateName(field);
+
+ return Stream.of(
+ FieldNamingPolicy.UPPER_CAMEL_CASE.translateName(field),
+ separateCamelCaseIncludeDigit(fieldName, '-').toLowerCase(Locale.ENGLISH),
+ convertUpperCaseSuffix(fieldName, "Arn"),
+ fieldName.toLowerCase(),
+ fieldName.toUpperCase())
+ .filter(s -> !s.isBlank() && !s.equals(fieldName))
+ .distinct()
+ .toList();
+ }
- if (fields != null && fields.containsKey(fieldName)) {
- fieldName = fields.get(fieldName);
+ static String convertUpperCaseSuffix(final String input, final String ext) {
+ String s = "";
+ if (input.endsWith(ext)) {
+ s = input.substring(0, input.length() - ext.length()) + ext.toUpperCase();
}
+ return s;
+ }
- return fieldName;
+ /**
+ * Converts the field name that uses camel-case define word separation into separate words that
+ * are separated by the provided {@code separator}.
+ *
+ * @param name {@link String}
+ * @param separator char
+ * @return String
+ */
+ static String separateCamelCaseIncludeDigit(final String name, final char separator) {
+ StringBuilder translation = new StringBuilder();
+ for (int i = 0, length = name.length(); i < length; i++) {
+ char character = name.charAt(i);
+ if ((Character.isUpperCase(character) || Character.isDigit(character))
+ && !translation.isEmpty()) {
+ translation.append(separator);
+ }
+ translation.append(character);
+ }
+ return translation.toString();
}
}
diff --git a/src/main/java/com/formkiq/lambda/runtime/graalvm/DateTimeConverter.java b/src/main/java/com/formkiq/lambda/runtime/graalvm/DateTimeConverter.java
new file mode 100644
index 0000000..d33baa6
--- /dev/null
+++ b/src/main/java/com/formkiq/lambda/runtime/graalvm/DateTimeConverter.java
@@ -0,0 +1,33 @@
+package com.formkiq.lambda.runtime.graalvm;
+
+import com.google.gson.JsonDeserializationContext;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParseException;
+import com.google.gson.JsonPrimitive;
+import com.google.gson.JsonSerializationContext;
+import com.google.gson.JsonSerializer;
+import java.lang.reflect.Type;
+import org.joda.time.DateTime;
+import org.joda.time.format.DateTimeFormat;
+import org.joda.time.format.DateTimeFormatter;
+
+/** {@link JsonSerializer} for {@link DateTime}. */
+public class DateTimeConverter implements JsonSerializer, JsonDeserializer {
+ /** {@link DateTimeFormatter}. */
+ private static final DateTimeFormatter FORMATTER =
+ DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
+
+ @Override
+ public JsonElement serialize(
+ final DateTime src, final Type typeOfSrc, final JsonSerializationContext context) {
+ return new JsonPrimitive(FORMATTER.print(src));
+ }
+
+ @Override
+ public DateTime deserialize(
+ final JsonElement json, final Type typeOfT, final JsonDeserializationContext context)
+ throws JsonParseException {
+ return FORMATTER.parseDateTime(json.getAsString());
+ }
+}
diff --git a/src/main/java/com/formkiq/lambda/runtime/graalvm/LambdaLoggerSystemOut.java b/src/main/java/com/formkiq/lambda/runtime/graalvm/LambdaLoggerSystemOut.java
index 1066702..99019d5 100644
--- a/src/main/java/com/formkiq/lambda/runtime/graalvm/LambdaLoggerSystemOut.java
+++ b/src/main/java/com/formkiq/lambda/runtime/graalvm/LambdaLoggerSystemOut.java
@@ -13,14 +13,24 @@
package com.formkiq.lambda.runtime.graalvm;
import com.amazonaws.services.lambda.runtime.LambdaLogger;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Map;
/** Implementation of {@link LambdaLogger}. */
public class LambdaLoggerSystemOut implements LambdaLogger {
@Override
public void log(final String message) {
- System.out.println(message);
+ if (isJsonFormat()) {
+ Gson gson = new GsonBuilder().create();
+ System.out.println(gson.toJson(Map.of("level", "ERROR", "message", message)));
+ } else {
+ System.out.println(message);
+ }
System.out.flush();
}
@@ -30,7 +40,24 @@ public void log(final byte[] message) {
System.out.write(message);
} catch (IOException e) {
// NOTE: When actually running on AWS Lambda, an IOException would never happen
- e.printStackTrace();
}
}
+
+ private boolean isJsonFormat() {
+ return "JSON".equals(System.getenv("AWS_LAMBDA_LOG_FORMAT"));
+ }
+
+ /**
+ * Convert {@link Exception} to {@link String}.
+ *
+ * @param ex {@link Exception}
+ * @return String
+ */
+ public static String toString(final Exception ex) {
+ StringWriter sw = new StringWriter();
+ try (PrintWriter pw = new PrintWriter(sw)) {
+ ex.printStackTrace(pw);
+ }
+ return sw.toString();
+ }
}
diff --git a/src/main/java/com/formkiq/lambda/runtime/graalvm/LambdaRuntime.java b/src/main/java/com/formkiq/lambda/runtime/graalvm/LambdaRuntime.java
index 5253930..dbeaa18 100644
--- a/src/main/java/com/formkiq/lambda/runtime/graalvm/LambdaRuntime.java
+++ b/src/main/java/com/formkiq/lambda/runtime/graalvm/LambdaRuntime.java
@@ -15,8 +15,6 @@
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.lambda.runtime.RequestStreamHandler;
-import com.google.gson.ExclusionStrategy;
-import com.google.gson.FieldAttributes;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.io.ByteArrayInputStream;
@@ -28,15 +26,13 @@
import java.lang.reflect.Parameter;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
-import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.text.MessageFormat;
-import java.util.Arrays;
-import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;
+import org.joda.time.DateTime;
/** Wrapper for the AWS Lambda Runtime. */
public class LambdaRuntime {
@@ -93,7 +89,7 @@ private static Method findRequestHandlerMethod(final Class> clazz, final Strin
public static void handleInitError(
final Map env, final Exception ex, final Context context) throws IOException {
- ex.printStackTrace();
+ context.getLogger().log(LambdaLoggerSystemOut.toString(ex));
String runtimeApi = env.get("AWS_LAMBDA_RUNTIME_API");
@@ -121,7 +117,8 @@ public static void handleInvocationException(
final String requestId,
final Exception ex,
final Context context) {
- ex.printStackTrace();
+
+ context.getLogger().log(LambdaLoggerSystemOut.toString(ex));
String runtimeApi = env.get("AWS_LAMBDA_RUNTIME_API");
@@ -136,7 +133,7 @@ public static void handleInvocationException(
try {
HttpClient.post(initErrorUrl, error);
} catch (IOException e) {
- e.printStackTrace();
+ context.getLogger().log(LambdaLoggerSystemOut.toString(e));
}
}
}
@@ -196,9 +193,9 @@ private static void invokeClass(
while (true) {
// Get next Lambda Event
- Context context = null;
String eventBody = null;
String requestId = UUID.randomUUID().toString();
+ Context context = new LambdaContext(requestId);
if (runtimeUrl != null) {
HttpResponse event = HttpClient.get(runtimeUrl);
@@ -209,7 +206,6 @@ private static void invokeClass(
System.setProperty("com.amazonaws.xray.traceHeader", xamazTraceId);
}
- context = new LambdaContext(requestId);
eventBody = event.getBody();
}
@@ -254,7 +250,7 @@ private static String invokeLambdaRequestHandler(
final Object handler, final String methodName, final Context context, final String payload)
throws Exception {
- String value = null;
+ String value;
ByteArrayOutputStream output = new ByteArrayOutputStream();
if (methodName != null) {
@@ -286,7 +282,7 @@ private static String invokeLambdaRequestHandler(
* @throws IllegalAccessException IllegalAccessException
* @throws ClassNotFoundException ClassNotFoundException
*/
- @SuppressWarnings({"unchecked", "rawtypes"})
+ @SuppressWarnings("rawtypes")
private static String invokeMethod(
final Object object, final String methodName, final String payload, final Context context)
throws IllegalAccessException,
@@ -301,7 +297,7 @@ private static String invokeMethod(
Gson gson = buildJsonProvider();
Object input = convertToObject(gson, payload, parameterType);
- Object value = null;
+ Object value;
if (methodName == null && object instanceof RequestHandler) {
value = ((RequestHandler) object).handleRequest(input, context);
} else {
@@ -330,19 +326,8 @@ static Object convertToObject(
static Gson buildJsonProvider() {
return new GsonBuilder()
.setFieldNamingStrategy(new AwsEventsFieldNamingStrategy())
- .setExclusionStrategies(
- new ExclusionStrategy() {
- @Override
- public boolean shouldSkipField(final FieldAttributes f) {
- return false;
- }
-
- @Override
- public boolean shouldSkipClass(final Class> clazz) {
- Collection> list = Arrays.asList(ByteBuffer.class);
- return list.contains(clazz);
- }
- })
+ .registerTypeAdapter(DateTime.class, new DateTimeConverter())
+ .setExclusionStrategies(new AwsEventsExclusionStrategy())
.create();
}
@@ -362,8 +347,7 @@ private static Class> getParameterType(final Object object, final Method metho
if (Object.class.equals(parameterType)) {
Type[] types = object.getClass().getGenericInterfaces();
- if (types.length > 0 && types[0] instanceof ParameterizedType) {
- ParameterizedType p = (ParameterizedType) types[0];
+ if (types.length > 0 && types[0] instanceof ParameterizedType p) {
if (p.getActualTypeArguments().length > 0) {
String typeName = p.getActualTypeArguments()[0].getTypeName();
if (typeName.startsWith("java.util.Map")) {
@@ -398,7 +382,7 @@ private static String invokeRequestStreamHandler(
InputStream input = new ByteArrayInputStream(payload.getBytes(StandardCharsets.UTF_8));
handler.handleRequest(input, output, context);
- return new String(output.toByteArray(), StandardCharsets.UTF_8);
+ return output.toString(StandardCharsets.UTF_8);
}
/**
diff --git a/src/test/java/com/formkiq/lambda/runtime/graalvm/LambdaRuntimeTest.java b/src/test/java/com/formkiq/lambda/runtime/graalvm/LambdaRuntimeTest.java
index 753192a..fe42866 100644
--- a/src/test/java/com/formkiq/lambda/runtime/graalvm/LambdaRuntimeTest.java
+++ b/src/test/java/com/formkiq/lambda/runtime/graalvm/LambdaRuntimeTest.java
@@ -12,16 +12,18 @@
*/
package com.formkiq.lambda.runtime.graalvm;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockserver.integration.ClientAndServer.startClientAndServer;
import static org.mockserver.model.HttpRequest.request;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
import com.amazonaws.services.lambda.runtime.events.DynamodbEvent;
import com.amazonaws.services.lambda.runtime.events.DynamodbEvent.DynamodbStreamRecord;
+import com.amazonaws.services.lambda.runtime.events.IamPolicyResponseV1;
import com.amazonaws.services.lambda.runtime.events.S3Event;
import com.amazonaws.services.lambda.runtime.events.SQSEvent;
import com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage;
@@ -31,13 +33,14 @@
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.io.IOUtils;
-import org.junit.AfterClass;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.Test;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
import org.mockserver.integration.ClientAndServer;
import org.mockserver.mock.action.ExpectationResponseCallback;
@@ -57,32 +60,28 @@ public class LambdaRuntimeTest {
private static ClientAndServer mockServer;
/** {@link InvocationResponseHandler}. */
- private static InvocationResponseHandler invocationResponseHandler =
+ private static final InvocationResponseHandler INVOCATION_RESPONSE_HANDLER =
new InvocationResponseHandler();
/** {@link InvocationNextHandler}. */
- private static InvocationNextHandler invocationNextHandler = new InvocationNextHandler();
+ private static final InvocationNextHandler INVOCATION_NEXT_HANDLER = new InvocationNextHandler();
- /**
- * before class.
- *
- * @throws Exception Exception
- */
- @BeforeClass
- public static void beforeClass() throws Exception {
+ /** before class. */
+ @BeforeAll
+ public static void beforeClass() {
- mockServer = startClientAndServer(Integer.valueOf(SERVER_PORT));
+ mockServer = startClientAndServer(SERVER_PORT);
- add("GET", "/2018-06-01/runtime/invocation/next", invocationNextHandler);
+ add("GET", "/2018-06-01/runtime/invocation/next", INVOCATION_NEXT_HANDLER);
add(
"POST",
"/2018-06-01/runtime/invocation/" + REQUEST_ID + "/response",
- invocationResponseHandler);
- add("POST", "/2018-06-01/runtime/init/error", invocationResponseHandler);
+ INVOCATION_RESPONSE_HANDLER);
+ add("POST", "/2018-06-01/runtime/init/error", INVOCATION_RESPONSE_HANDLER);
}
/** After Class. */
- @AfterClass
+ @AfterAll
public static void stopServer() {
mockServer.stop();
}
@@ -93,11 +92,9 @@ public static void stopServer() {
* @param method {@link String}
* @param path {@link String}
* @param response {@link ExpectationResponseCallback}
- * @throws IOException IOException
*/
private static void add(
- final String method, final String path, final ExpectationResponseCallback response)
- throws IOException {
+ final String method, final String path, final ExpectationResponseCallback response) {
mockServer.when(request().withMethod(method).withPath(path)).respond(response);
}
@@ -116,9 +113,9 @@ private Map createEnv(final String handler) {
}
/** before. */
- @Before
+ @BeforeEach
public void before() {
- invocationNextHandler.setResponseContent("test");
+ INVOCATION_NEXT_HANDLER.setResponseContent("test");
}
/**
@@ -135,7 +132,7 @@ public void testInvoke01() throws Exception {
LambdaRuntime.invoke(env);
// then
- assertEquals("test data result", invocationResponseHandler.getResponse());
+ assertEquals("test data result", INVOCATION_RESPONSE_HANDLER.getResponse());
}
/**
@@ -154,7 +151,7 @@ public void testInvoke02() throws Exception {
// then
String expected =
"{\"errorMessage\":\"Could not find handler method\",\"errorType\":\"InitError\"}";
- assertEquals(expected, invocationResponseHandler.getResponse());
+ assertEquals(expected, INVOCATION_RESPONSE_HANDLER.getResponse());
}
/**
@@ -173,7 +170,7 @@ public void testInvoke03() throws Exception {
// then
String expected =
"{\"errorMessage\":\"Could not find handler method\",\"errorType\":\"InitError\"}";
- assertEquals(expected, invocationResponseHandler.getResponse());
+ assertEquals(expected, INVOCATION_RESPONSE_HANDLER.getResponse());
}
/**
@@ -184,7 +181,7 @@ public void testInvoke03() throws Exception {
@Test
public void testInvoke04() throws Exception {
// given
- invocationNextHandler.setResponseContent("{\"data\":\"test\"}");
+ INVOCATION_NEXT_HANDLER.setResponseContent("{\"data\":\"test\"}");
Map env = createEnv(TestRequestInputMapVoidHandler.class.getName());
// when
@@ -192,7 +189,7 @@ public void testInvoke04() throws Exception {
// then
String expected = "";
- assertEquals(expected, invocationResponseHandler.getResponse());
+ assertEquals(expected, INVOCATION_RESPONSE_HANDLER.getResponse());
}
/**
@@ -210,7 +207,7 @@ public void testInvoke05() throws Exception {
// then
String expected = "this is a test string";
- assertEquals(expected, invocationResponseHandler.getResponse());
+ assertEquals(expected, INVOCATION_RESPONSE_HANDLER.getResponse());
}
/**
@@ -228,7 +225,7 @@ public void testInvoke06() throws Exception {
// then
String expected = "98";
- assertEquals(expected, invocationResponseHandler.getResponse());
+ assertEquals(expected, INVOCATION_RESPONSE_HANDLER.getResponse());
}
/**
@@ -246,7 +243,7 @@ public void testInvoke07() throws Exception {
// then
String expected = "{\"test\":\"123\"}";
- assertEquals(expected, invocationResponseHandler.getResponse());
+ assertEquals(expected, INVOCATION_RESPONSE_HANDLER.getResponse());
}
/**
@@ -266,7 +263,7 @@ public void testInvoke08() throws Exception {
// then
String expected = "this is a test string";
- assertEquals(expected, invocationResponseHandler.getResponse());
+ assertEquals(expected, INVOCATION_RESPONSE_HANDLER.getResponse());
}
/**
@@ -285,7 +282,7 @@ public void testInvoke09() throws Exception {
// then
String expected = "this is a run string";
- assertEquals(expected, invocationResponseHandler.getResponse());
+ assertEquals(expected, INVOCATION_RESPONSE_HANDLER.getResponse());
}
/**
@@ -311,7 +308,7 @@ public void testInvoke10() throws Exception {
// then
String expected = "this is a run string";
- assertEquals(expected, invocationResponseHandler.getResponse());
+ assertEquals(expected, INVOCATION_RESPONSE_HANDLER.getResponse());
}
/**
@@ -323,7 +320,7 @@ public void testInvoke10() throws Exception {
@Test
public void testInvoke11() throws Exception {
// given
- invocationNextHandler.setResponseContent("{\"body\":\"this is some data\"}");
+ INVOCATION_NEXT_HANDLER.setResponseContent("{\"body\":\"this is some data\"}");
Map env = createEnv(TestRequestApiGatewayProxyHandler.class.getName());
// when
@@ -331,7 +328,7 @@ public void testInvoke11() throws Exception {
// then
String expected = "{\"body\":\"this is some data\"}";
- assertEquals(expected, invocationResponseHandler.getResponse());
+ assertEquals(expected, INVOCATION_RESPONSE_HANDLER.getResponse());
assertNotNull(System.getProperty("com.amazonaws.xray.traceHeader"));
}
@@ -359,7 +356,7 @@ public void testApiGatewayProxyRequestEvent01() throws Exception {
final int expected = 18;
assertEquals(expected, event.getMultiValueHeaders().size());
- assertFalse(event.getIsBase64Encoded().booleanValue());
+ assertFalse(event.getIsBase64Encoded());
}
/**
@@ -434,7 +431,7 @@ public void testS3Event01() throws Exception {
assertEquals("us-east-1", record.getAwsRegion());
assertEquals("ObjectCreated:Put", record.getEventName());
assertEquals("aws:s3", record.getEventSource());
- assertNull(record.getEventTime());
+ assertNotNull(record.getEventTime());
assertEquals("2.0", record.getEventVersion());
assertEquals("EXAMPLE", record.getUserIdentity().getPrincipalId());
@@ -457,14 +454,14 @@ public void testS3Event01() throws Exception {
}
/**
- * Test invoke Lambda with {@link SqsEvent}.
+ * Test invoke Lambda with SqsEvent.
*
* @throws Exception Exception
*/
@Test
public void testSqsEvent01() throws Exception {
// given
- String payload = loadContent("/SqsEvent/event01.json");
+ String payload = loadContent("/SQSEvent/event01.json");
Gson gson = LambdaRuntime.buildJsonProvider();
@@ -492,8 +489,58 @@ public void testSqsEvent01() throws Exception {
assertNotNull(record.getReceiptHandle());
}
+ /**
+ * Test invoke Lambda with {@link
+ * com.amazonaws.services.lambda.runtime.events.IamPolicyResponseV1}.
+ *
+ * @throws Exception Exception
+ */
+ @Test
+ public void testIamPolicyResponseV1Event01() throws Exception {
+ // given
+ String payload = loadContent("/IamPolicy/event01.json");
+
+ Gson gson = LambdaRuntime.buildJsonProvider();
+
+ // when
+ IamPolicyResponseV1 event =
+ (IamPolicyResponseV1)
+ LambdaRuntime.convertToObject(gson, payload, IamPolicyResponseV1.class);
+
+ // then
+ assertEquals("user-12345", event.getPrincipalId());
+ assertEquals("unique-usage-key-001", event.getUsageIdentifierKey());
+
+ Map policyDocument = event.getPolicyDocument();
+ assertNotNull(policyDocument);
+ assertEquals(2, policyDocument.size());
+
+ assertEquals("2012-10-17", policyDocument.get("Version"));
+
+ Map[] statementMap = (Map[]) policyDocument.get("Statement");
+ assertEquals(1, statementMap.length);
+ Map statement = statementMap[0];
+ final int expect4 = 4;
+ assertEquals(expect4, statement.size());
+ assertNull(statement.get("Condition"));
+ assertEquals("execute-api:Invoke", statement.get("Action"));
+ assertEquals(
+ "arn:aws:execute-api:region:account-id:api-id/stage/method/resource-path",
+ String.join(",", Arrays.asList((String[]) statement.get("Resource"))));
+ assertEquals("Allow", statement.get("Effect"));
+
+ Map context = event.getContext();
+ assertNotNull(context);
+ final int expect3 = 3;
+ assertEquals(expect3, context.size());
+ assertEquals("stringValue", context.get("stringKey"));
+ assertEquals("2.0", String.valueOf(context.get("numberKey")));
+ assertTrue((Boolean) context.get("booleanKey"));
+ }
+
private String loadContent(final String filename) throws IOException {
try (InputStream is = getClass().getResourceAsStream(filename)) {
+ assertNotNull(is);
return IOUtils.toString(is, StandardCharsets.UTF_8);
}
}
diff --git a/src/test/resources/IamPolicy/event01.json b/src/test/resources/IamPolicy/event01.json
new file mode 100644
index 0000000..86c4e7e
--- /dev/null
+++ b/src/test/resources/IamPolicy/event01.json
@@ -0,0 +1,19 @@
+{
+ "principalId": "user-12345",
+ "policyDocument": {
+ "Version": "2012-10-17",
+ "Statement": [
+ {
+ "Effect": "Allow",
+ "Action": "execute-api:Invoke",
+ "Resource": ["arn:aws:execute-api:region:account-id:api-id/stage/method/resource-path"]
+ }
+ ]
+ },
+ "context": {
+ "stringKey": "stringValue",
+ "numberKey": 2,
+ "booleanKey": true
+ },
+ "usageIdentifierKey": "unique-usage-key-001"
+}
\ No newline at end of file