From f943d99dfdf45e583ae60574d3ce0efeb9a0d6dc Mon Sep 17 00:00:00 2001 From: Mike Friesen Date: Fri, 15 Nov 2024 20:32:34 -0600 Subject: [PATCH 01/13] Improved aws-lambda-java-events support --- .gitignore | 2 + build.gradle | 15 ++- config/checkstyle/import-control.xml | 3 +- .../graalvm/AwsEventsExclusionStrategy.java | 27 +++++ .../graalvm/AwsEventsFieldNamingStrategy.java | 83 ++++--------- .../runtime/graalvm/DateTimeConverter.java | 33 ++++++ .../lambda/runtime/graalvm/LambdaRuntime.java | 32 ++--- .../runtime/graalvm/LambdaRuntimeTest.java | 111 +++++++++++++----- src/test/resources/IamPolicy/event01.json | 19 +++ 9 files changed, 202 insertions(+), 123 deletions(-) create mode 100644 src/main/java/com/formkiq/lambda/runtime/graalvm/AwsEventsExclusionStrategy.java create mode 100644 src/main/java/com/formkiq/lambda/runtime/graalvm/DateTimeConverter.java create mode 100644 src/test/resources/IamPolicy/event01.json 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..6a0ecd4 100644 --- a/build.gradle +++ b/build.gradle @@ -10,7 +10,7 @@ plugins { } group 'com.formkiq' -version '2.5.0' +version '2.5.1-SNAPSHOT' spotbugs { excludeFilter = file("$rootDir/config/spotbugs/spotbugs-exclude.xml") @@ -31,7 +31,7 @@ spotbugsMain { } checkstyle { - toolVersion '8.29' + toolVersion = '10.12.1' configFile file("config/checkstyle/checkstyle.xml") configProperties = [project_loc: "${projectDir}"] ignoreFailures = false @@ -41,7 +41,11 @@ checkstyle { checkstyleMain.dependsOn spotlessApply repositories { - jcenter() + mavenLocal() + mavenCentral() + maven { + url "https://oss.sonatype.org/content/repositories/snapshots/" + } } check { @@ -77,8 +81,9 @@ afterEvaluate { 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' + implementation group: 'com.formkiq.code.gson', name: 'gson', version: '2.11.1-SNAPSHOT' + implementation group: 'joda-time', name: 'joda-time', version: '2.13.0' + testImplementation group: 'com.amazonaws', name: 'aws-lambda-java-events', version: '3.11.4' testImplementation group: 'junit', name: 'junit', version:'4.+' testImplementation group: 'org.mock-server', name: 'mockserver-netty', version: '5.15.0' testImplementation group: 'org.slf4j', name: 'slf4j-simple', version: '2.0.9' 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/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..7c32f50 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,37 @@ 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.FieldNamingStrategy; import java.lang.reflect.Field; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.List; import java.util.Map; /** 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")); + /** Field Mapping. */ + private static final Map FIELD_MAPPING = + Map.of("xAmzId2", "x-amz-id-2", "xAmzRequestId", "x-amz-request-id"); @Override - public String translateName(final Field field) { - + public List translateName(final Field field) { String fieldName = field.getName(); + String text = fieldName.endsWith("Arn") ? fieldName.replaceAll("Arn", "ARN") : ""; + + return FIELD_MAPPING.containsKey(fieldName) + ? Collections.singletonList(FIELD_MAPPING.get(fieldName)) + : new LinkedHashSet<>( + List.of( + fieldName, + text, + upperCaseFirstLetter(fieldName), + fieldName.toLowerCase(), + fieldName.toUpperCase())) + .stream().filter(s -> !s.isBlank()).toList(); + } - Map fields = FIELD_MAPPING.get(field.getDeclaringClass()); - - if (fields != null && fields.containsKey(fieldName)) { - fieldName = fields.get(fieldName); - } - - return fieldName; + private static String upperCaseFirstLetter(final String s) { + return s != null && !s.isEmpty() ? Character.toUpperCase(s.charAt(0)) + s.substring(1) : s; } } 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/LambdaRuntime.java b/src/main/java/com/formkiq/lambda/runtime/graalvm/LambdaRuntime.java index 5253930..d325c84 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 { @@ -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..b4189cb 100644 --- a/src/test/java/com/formkiq/lambda/runtime/graalvm/LambdaRuntimeTest.java +++ b/src/test/java/com/formkiq/lambda/runtime/graalvm/LambdaRuntimeTest.java @@ -16,12 +16,14 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import static org.junit.Assert.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,6 +33,7 @@ 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; @@ -57,28 +60,24 @@ 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 - */ + /** before class. */ @BeforeClass - public static void beforeClass() throws Exception { + 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. */ @@ -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); } @@ -118,7 +115,7 @@ private Map createEnv(final String handler) { /** before. */ @Before 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,7 +454,7 @@ public void testS3Event01() throws Exception { } /** - * Test invoke Lambda with {@link SqsEvent}. + * Test invoke Lambda with SqsEvent. * * @throws Exception Exception */ @@ -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 From 4a9d291b184596e4c3ee0f40cc995fd41de283b8 Mon Sep 17 00:00:00 2001 From: Mike Friesen Date: Fri, 15 Nov 2024 20:42:08 -0600 Subject: [PATCH 02/13] added generation GRADLE_PROPERTIES --- .github/workflows/main.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 81e95fb..a0e376d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -27,6 +27,14 @@ 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: Execute Gradle build run: ./gradlew clean build --refresh-dependencies --info - name: Upload test reports From cbbf20e8ec7513ab3dd9e71dd0f9106545c344f9 Mon Sep 17 00:00:00 2001 From: Mike Friesen Date: Fri, 15 Nov 2024 21:07:27 -0600 Subject: [PATCH 03/13] update --- .github/workflows/main.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a0e376d..f8ce09d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -35,6 +35,13 @@ jobs: 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 From c6ad19709a2db1c5fe21e2be6d99c363b0b53c1d Mon Sep 17 00:00:00 2001 From: Mike Friesen Date: Fri, 15 Nov 2024 21:15:34 -0600 Subject: [PATCH 04/13] update --- .../com/formkiq/lambda/runtime/graalvm/LambdaRuntimeTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 b4189cb..7df5d38 100644 --- a/src/test/java/com/formkiq/lambda/runtime/graalvm/LambdaRuntimeTest.java +++ b/src/test/java/com/formkiq/lambda/runtime/graalvm/LambdaRuntimeTest.java @@ -461,7 +461,7 @@ public void testS3Event01() throws Exception { @Test public void testSqsEvent01() throws Exception { // given - String payload = loadContent("/SqsEvent/event01.json"); + String payload = loadContent("/SQSEvent/event01.json"); Gson gson = LambdaRuntime.buildJsonProvider(); From 0ccf7d7125b83cedd35df5c1de6e96e7080316b2 Mon Sep 17 00:00:00 2001 From: Mike Friesen Date: Fri, 15 Nov 2024 23:39:56 -0600 Subject: [PATCH 05/13] enabled snapshot repo --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 6a0ecd4..8e2ec63 100644 --- a/build.gradle +++ b/build.gradle @@ -126,8 +126,8 @@ publishing { username project.repoUser password project.repoPassword } - url "https://oss.sonatype.org/service/local/staging/deploy/maven2/" - //url "https://oss.sonatype.org/content/repositories/snapshots/" +// url "https://oss.sonatype.org/service/local/staging/deploy/maven2/" + url "https://oss.sonatype.org/content/repositories/snapshots/" } } } From 7a1aae2eda104c8a0891bb18f47e9fea41d5fc89 Mon Sep 17 00:00:00 2001 From: Mike Friesen Date: Fri, 16 May 2025 10:21:53 -0500 Subject: [PATCH 06/13] Updated to gradle 8.1.14 --- gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 079850f9c14551b23605ce336e58fc211e4c7f2c Mon Sep 17 00:00:00 2001 From: Mike Friesen Date: Fri, 16 May 2025 10:22:24 -0500 Subject: [PATCH 07/13] Added support for AwsEvent Field Name Strategy --- build.gradle | 22 ++++--- .../graalvm/AwsEventsFieldNamingStrategy.java | 66 +++++++++++++------ .../runtime/graalvm/LambdaRuntimeTest.java | 24 +++---- 3 files changed, 71 insertions(+), 41 deletions(-) diff --git a/build.gradle b/build.gradle index 8e2ec63..112b440 100644 --- a/build.gradle +++ b/build.gradle @@ -4,9 +4,9 @@ plugins { 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.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' @@ -81,12 +81,13 @@ afterEvaluate { dependencies { implementation group: 'com.amazonaws', name: 'aws-lambda-java-core', version: '1.2.3' - implementation group: 'com.formkiq.code.gson', name: 'gson', version: '2.11.1-SNAPSHOT' - implementation group: 'joda-time', name: 'joda-time', version: '2.13.0' - testImplementation 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 { @@ -139,3 +140,8 @@ signing { check { dependsOn(tasks.publishToMavenLocal) } + +test { + failFast = true + useJUnitPlatform() +} 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 7c32f50..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,37 +1,61 @@ package com.formkiq.lambda.runtime.graalvm; +import com.google.gson.FieldNamingPolicy; import com.google.gson.FieldNamingStrategy; import java.lang.reflect.Field; -import java.util.Collections; -import java.util.LinkedHashSet; import java.util.List; -import java.util.Map; +import java.util.Locale; +import java.util.stream.Stream; /** AWS Events Field {@link FieldNamingStrategy}. */ public class AwsEventsFieldNamingStrategy implements FieldNamingStrategy { - /** Field Mapping. */ - private static final Map FIELD_MAPPING = - Map.of("xAmzId2", "x-amz-id-2", "xAmzRequestId", "x-amz-request-id"); + @Override + public String translateName(final Field f) { + return f.getName(); + } @Override - public List translateName(final Field field) { - String fieldName = field.getName(); - String text = fieldName.endsWith("Arn") ? fieldName.replaceAll("Arn", "ARN") : ""; + 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(); + } - return FIELD_MAPPING.containsKey(fieldName) - ? Collections.singletonList(FIELD_MAPPING.get(fieldName)) - : new LinkedHashSet<>( - List.of( - fieldName, - text, - upperCaseFirstLetter(fieldName), - fieldName.toLowerCase(), - fieldName.toUpperCase())) - .stream().filter(s -> !s.isBlank()).toList(); + 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; } - private static String upperCaseFirstLetter(final String s) { - return s != null && !s.isEmpty() ? Character.toUpperCase(s.charAt(0)) + s.substring(1) : s; + /** + * 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/test/java/com/formkiq/lambda/runtime/graalvm/LambdaRuntimeTest.java b/src/test/java/com/formkiq/lambda/runtime/graalvm/LambdaRuntimeTest.java index 7df5d38..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,11 +12,11 @@ */ 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.Assert.assertTrue; +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; @@ -37,10 +37,10 @@ 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; @@ -67,7 +67,7 @@ public class LambdaRuntimeTest { private static final InvocationNextHandler INVOCATION_NEXT_HANDLER = new InvocationNextHandler(); /** before class. */ - @BeforeClass + @BeforeAll public static void beforeClass() { mockServer = startClientAndServer(SERVER_PORT); @@ -81,7 +81,7 @@ public static void beforeClass() { } /** After Class. */ - @AfterClass + @AfterAll public static void stopServer() { mockServer.stop(); } @@ -113,7 +113,7 @@ private Map createEnv(final String handler) { } /** before. */ - @Before + @BeforeEach public void before() { INVOCATION_NEXT_HANDLER.setResponseContent("test"); } From 87a16682ce6856541f93dcb1acc3ba5e128d8118 Mon Sep 17 00:00:00 2001 From: Mike Friesen Date: Fri, 16 May 2025 10:51:05 -0500 Subject: [PATCH 08/13] #21 - Add JSON logging support --- .../graalvm/LambdaLoggerSystemOut.java | 25 +++++++++++++++++-- .../lambda/runtime/graalvm/LambdaRuntime.java | 10 ++++---- 2 files changed, 28 insertions(+), 7 deletions(-) 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..525b328 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,18 @@ 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")); + } + + 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 d325c84..dbeaa18 100644 --- a/src/main/java/com/formkiq/lambda/runtime/graalvm/LambdaRuntime.java +++ b/src/main/java/com/formkiq/lambda/runtime/graalvm/LambdaRuntime.java @@ -89,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"); @@ -117,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"); @@ -132,7 +133,7 @@ public static void handleInvocationException( try { HttpClient.post(initErrorUrl, error); } catch (IOException e) { - e.printStackTrace(); + context.getLogger().log(LambdaLoggerSystemOut.toString(e)); } } } @@ -192,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); @@ -205,7 +206,6 @@ private static void invokeClass( System.setProperty("com.amazonaws.xray.traceHeader", xamazTraceId); } - context = new LambdaContext(requestId); eventBody = event.getBody(); } From 87cb19b4d8855d5889f2607bda137beeb7ad5a8b Mon Sep 17 00:00:00 2001 From: Mike Friesen Date: Fri, 16 May 2025 10:52:25 -0500 Subject: [PATCH 09/13] Changed version to 2.6.0-SNAPSHOT --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 112b440..3ad9dbe 100644 --- a/build.gradle +++ b/build.gradle @@ -10,7 +10,7 @@ plugins { } group 'com.formkiq' -version '2.5.1-SNAPSHOT' +version '2.6.0-SNAPSHOT' spotbugs { excludeFilter = file("$rootDir/config/spotbugs/spotbugs-exclude.xml") From 59e687012322d58b07e661362662af5535106ec5 Mon Sep 17 00:00:00 2001 From: Mike Friesen Date: Fri, 16 May 2025 13:21:19 -0500 Subject: [PATCH 10/13] Added comment --- .../lambda/runtime/graalvm/LambdaLoggerSystemOut.java | 6 ++++++ 1 file changed, 6 insertions(+) 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 525b328..99019d5 100644 --- a/src/main/java/com/formkiq/lambda/runtime/graalvm/LambdaLoggerSystemOut.java +++ b/src/main/java/com/formkiq/lambda/runtime/graalvm/LambdaLoggerSystemOut.java @@ -47,6 +47,12 @@ 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)) { From e0c3551f4f5c5ba7d1e6d77e29542a9d3bb67671 Mon Sep 17 00:00:00 2001 From: Mike Friesen Date: Sat, 17 May 2025 19:54:58 -0500 Subject: [PATCH 11/13] Migrated from OSSRH to Central Portal --- build.gradle | 118 ++++++++++++++++----------------------------------- 1 file changed, 36 insertions(+), 82 deletions(-) diff --git a/build.gradle b/build.gradle index 3ad9dbe..0e194ed 100644 --- a/build.gradle +++ b/build.gradle @@ -1,9 +1,9 @@ +import com.vanniktech.maven.publish.SonatypeHost plugins { id 'java' - id 'maven-publish' - id 'signing' id 'checkstyle' + 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' @@ -23,12 +23,12 @@ spotless { } spotbugsMain { - reports { - html { - enabled = true + reports { + html { + required.set(true) + } } - } -} +} checkstyle { toolVersion = '10.12.1' @@ -43,42 +43,14 @@ checkstyleMain.dependsOn spotlessApply repositories { mavenLocal() mavenCentral() - maven { - url "https://oss.sonatype.org/content/repositories/snapshots/" - } -} - -check { - dependsOn(tasks.publishToMavenLocal) } 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.13.1' @@ -90,57 +62,39 @@ dependencies { 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' - } - } - } - } - 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/" - } - } -} +mavenPublishing { -signing { - sign publishing.publications.mavenJava -} + publishToMavenCentral(SonatypeHost.CENTRAL_PORTAL) -check { - dependsOn(tasks.publishToMavenLocal) + 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" + } + } + 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' + } + } } +compileJava.dependsOn(tasks.spotlessApply) +check.dependsOn(tasks.publishToMavenLocal) + test { failFast = true useJUnitPlatform() From db0b81bb364265afa399c800633e875f2ecf87a4 Mon Sep 17 00:00:00 2001 From: Mike Friesen Date: Sat, 17 May 2025 20:05:13 -0500 Subject: [PATCH 12/13] Updated Publish action name --- .github/workflows/publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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: From a38f7d1463b6f0bd9a6557865a870c8dcde4f217 Mon Sep 17 00:00:00 2001 From: Mike Friesen Date: Sun, 18 May 2025 19:00:55 -0500 Subject: [PATCH 13/13] Updated version to 2.6.0 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 0e194ed..4eef135 100644 --- a/build.gradle +++ b/build.gradle @@ -10,7 +10,7 @@ plugins { } group 'com.formkiq' -version '2.6.0-SNAPSHOT' +version '2.6.0' spotbugs { excludeFilter = file("$rootDir/config/spotbugs/spotbugs-exclude.xml")