diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 08aeddcfe..46fab27cf 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -31,10 +31,11 @@ To send us a pull request, please: 1. Fork the repository. 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. -3. Ensure local tests pass. -4. Commit to your fork using clear commit messages. -5. Send us a pull request, answering any default questions in the pull request interface. -6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. +3. Ensure local tests pass: `mvn clean test` +4. Ensure your code is formatted with the provided [checkstyle.xml](https://github.com/aws-powertools/powertools-lambda-java/blob/main/checkstyle.xml): `mvn clean verify` +5. Commit to your fork using clear commit messages. +6. Send us a pull request, answering any default questions in the pull request interface. +7. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). diff --git a/checkstyle.xml b/checkstyle.xml new file mode 100644 index 000000000..34ef98ef2 --- /dev/null +++ b/checkstyle.xml @@ -0,0 +1,427 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/pom.xml b/examples/pom.xml index c9b8ea8ae..72f1dc03b 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -1,4 +1,18 @@ + + diff --git a/examples/powertools-examples-cloudformation/infra/cdk/src/main/java/com/myorg/PowertoolsExamplesCloudformationCdkApp.java b/examples/powertools-examples-cloudformation/infra/cdk/src/main/java/com/myorg/PowertoolsExamplesCloudformationCdkApp.java index 84060171b..f4a4d06d7 100644 --- a/examples/powertools-examples-cloudformation/infra/cdk/src/main/java/com/myorg/PowertoolsExamplesCloudformationCdkApp.java +++ b/examples/powertools-examples-cloudformation/infra/cdk/src/main/java/com/myorg/PowertoolsExamplesCloudformationCdkApp.java @@ -7,8 +7,9 @@ public class PowertoolsExamplesCloudformationCdkApp { public static void main(final String[] args) { App app = new App(); - new PowertoolsExamplesCloudformationCdkStack(app, "PowertoolsExamplesCloudformationCdkStack", StackProps.builder() - .build()); + new PowertoolsExamplesCloudformationCdkStack(app, "PowertoolsExamplesCloudformationCdkStack", + StackProps.builder() + .build()); app.synth(); } diff --git a/examples/powertools-examples-cloudformation/src/main/java/helloworld/App.java b/examples/powertools-examples-cloudformation/src/main/java/helloworld/App.java index c7744cd5a..ca3cb0ab7 100644 --- a/examples/powertools-examples-cloudformation/src/main/java/helloworld/App.java +++ b/examples/powertools-examples-cloudformation/src/main/java/helloworld/App.java @@ -41,7 +41,8 @@ public App() { protected Response create(CloudFormationCustomResourceEvent cloudFormationCustomResourceEvent, Context context) { // Validate the CloudFormation Custom Resource event Objects.requireNonNull(cloudFormationCustomResourceEvent, "cloudFormationCustomResourceEvent cannot be null."); - Objects.requireNonNull(cloudFormationCustomResourceEvent.getResourceProperties().get("BucketName"), "BucketName cannot be null."); + Objects.requireNonNull(cloudFormationCustomResourceEvent.getResourceProperties().get("BucketName"), + "BucketName cannot be null."); log.info(cloudFormationCustomResourceEvent); String bucketName = (String) cloudFormationCustomResourceEvent.getResourceProperties().get("BucketName"); @@ -70,7 +71,8 @@ protected Response create(CloudFormationCustomResourceEvent cloudFormationCustom protected Response update(CloudFormationCustomResourceEvent cloudFormationCustomResourceEvent, Context context) { // Validate the CloudFormation Custom Resource event Objects.requireNonNull(cloudFormationCustomResourceEvent, "cloudFormationCustomResourceEvent cannot be null."); - Objects.requireNonNull(cloudFormationCustomResourceEvent.getResourceProperties().get("BucketName"), "BucketName cannot be null."); + Objects.requireNonNull(cloudFormationCustomResourceEvent.getResourceProperties().get("BucketName"), + "BucketName cannot be null."); log.info(cloudFormationCustomResourceEvent); // Get the physicalResourceId. physicalResourceId is the value returned to CloudFormation in the Create request, and passed in on subsequent requests (e.g. UPDATE or DELETE) @@ -112,7 +114,8 @@ protected Response update(CloudFormationCustomResourceEvent cloudFormationCustom protected Response delete(CloudFormationCustomResourceEvent cloudFormationCustomResourceEvent, Context context) { // Validate the CloudFormation Custom Resource event Objects.requireNonNull(cloudFormationCustomResourceEvent, "cloudFormationCustomResourceEvent cannot be null."); - Objects.requireNonNull(cloudFormationCustomResourceEvent.getPhysicalResourceId(), "PhysicalResourceId cannot be null."); + Objects.requireNonNull(cloudFormationCustomResourceEvent.getPhysicalResourceId(), + "PhysicalResourceId cannot be null."); log.info(cloudFormationCustomResourceEvent); // Get the physicalResourceId. physicalResourceId is the value provided to CloudFormation in the Create request. @@ -142,7 +145,8 @@ protected Response delete(CloudFormationCustomResourceEvent cloudFormationCustom private boolean bucketExists(String bucketName) { try { - HeadBucketResponse headBucketResponse = s3Client.headBucket(HeadBucketRequest.builder().bucket(bucketName).build()); + HeadBucketResponse headBucketResponse = + s3Client.headBucket(HeadBucketRequest.builder().bucket(bucketName).build()); if (headBucketResponse.sdkHttpResponse().isSuccessful()) { return true; } @@ -157,7 +161,8 @@ private void createBucket(String bucketName) { S3Waiter waiter = s3Client.waiter(); CreateBucketRequest createBucketRequest = CreateBucketRequest.builder().bucket(bucketName).build(); s3Client.createBucket(createBucketRequest); - WaiterResponse waiterResponse = waiter.waitUntilBucketExists(HeadBucketRequest.builder().bucket(bucketName).build()); + WaiterResponse waiterResponse = + waiter.waitUntilBucketExists(HeadBucketRequest.builder().bucket(bucketName).build()); waiterResponse.matched().response().ifPresent(log::info); log.info("Bucket Created {}", bucketName); } diff --git a/examples/powertools-examples-core/src/main/java/helloworld/App.java b/examples/powertools-examples-core/src/main/java/helloworld/App.java index b45440114..94360cf59 100644 --- a/examples/powertools-examples-core/src/main/java/helloworld/App.java +++ b/examples/powertools-examples-core/src/main/java/helloworld/App.java @@ -1,12 +1,23 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package helloworld; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.net.URL; -import java.util.HashMap; -import java.util.Map; -import java.util.stream.Collectors; +import static software.amazon.lambda.powertools.metrics.MetricsUtils.metricsLogger; +import static software.amazon.lambda.powertools.metrics.MetricsUtils.withSingleMetric; +import static software.amazon.lambda.powertools.tracing.TracingUtils.putMetadata; +import static software.amazon.lambda.powertools.tracing.TracingUtils.withEntitySubsegment; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; @@ -14,21 +25,23 @@ import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; import com.amazonaws.xray.AWSXRay; import com.amazonaws.xray.entities.Entity; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URL; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Collectors; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import software.amazon.cloudwatchlogs.emf.model.DimensionSet; import software.amazon.cloudwatchlogs.emf.model.Unit; -import software.amazon.lambda.powertools.logging.LoggingUtils; import software.amazon.lambda.powertools.logging.Logging; +import software.amazon.lambda.powertools.logging.LoggingUtils; import software.amazon.lambda.powertools.metrics.Metrics; import software.amazon.lambda.powertools.tracing.CaptureMode; -import software.amazon.lambda.powertools.tracing.TracingUtils; import software.amazon.lambda.powertools.tracing.Tracing; - -import static software.amazon.lambda.powertools.metrics.MetricsUtils.metricsLogger; -import static software.amazon.lambda.powertools.metrics.MetricsUtils.withSingleMetric; -import static software.amazon.lambda.powertools.tracing.TracingUtils.putMetadata; -import static software.amazon.lambda.powertools.tracing.TracingUtils.withEntitySubsegment; +import software.amazon.lambda.powertools.tracing.TracingUtils; /** * Handler for requests to Lambda function. @@ -47,10 +60,11 @@ public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEv metricsLogger().putMetric("CustomMetric1", 1, Unit.COUNT); - withSingleMetric("CustomMetrics2", 1, Unit.COUNT, "Another", (metric) -> { - metric.setDimensions(DimensionSet.of("AnotherService", "CustomService")); - metric.setDimensions(DimensionSet.of("AnotherService1", "CustomService1")); - }); + withSingleMetric("CustomMetrics2", 1, Unit.COUNT, "Another", (metric) -> + { + metric.setDimensions(DimensionSet.of("AnotherService", "CustomService")); + metric.setDimensions(DimensionSet.of("AnotherService1", "CustomService1")); + }); LoggingUtils.appendKey("test", "willBeLogged"); @@ -62,11 +76,12 @@ public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEv TracingUtils.putAnnotation("Test", "New"); String output = String.format("{ \"message\": \"hello world\", \"location\": \"%s\" }", pageContents); - TracingUtils.withSubsegment("loggingResponse", subsegment -> { - String sampled = "log something out"; - log.info(sampled); - log.info(output); - }); + TracingUtils.withSubsegment("loggingResponse", subsegment -> + { + String sampled = "log something out"; + log.info(sampled); + log.info(output); + }); threadOption1(); @@ -91,10 +106,11 @@ private void threadOption1() throws InterruptedException { private void threadOption2() throws InterruptedException { Entity traceEntity = AWSXRay.getTraceEntity(); - Thread anotherThread = new Thread(() -> withEntitySubsegment("inlineLog", traceEntity, subsegment -> { - String var = "somethingToProcess"; - log.info("inside threaded logging inline {}", var); - })); + Thread anotherThread = new Thread(() -> withEntitySubsegment("inlineLog", traceEntity, subsegment -> + { + String var = "somethingToProcess"; + log.info("inside threaded logging inline {}", var); + })); anotherThread.start(); anotherThread.join(); } diff --git a/examples/powertools-examples-core/src/main/java/helloworld/AppStream.java b/examples/powertools-examples-core/src/main/java/helloworld/AppStream.java index aed048eef..401ef8c48 100644 --- a/examples/powertools-examples-core/src/main/java/helloworld/AppStream.java +++ b/examples/powertools-examples-core/src/main/java/helloworld/AppStream.java @@ -1,13 +1,26 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package helloworld; +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestStreamHandler; +import com.fasterxml.jackson.databind.ObjectMapper; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Map; - -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.RequestStreamHandler; -import com.fasterxml.jackson.databind.ObjectMapper; import software.amazon.lambda.powertools.logging.Logging; import software.amazon.lambda.powertools.metrics.Metrics; diff --git a/examples/powertools-examples-core/src/test/java/helloworld/AppTest.java b/examples/powertools-examples-core/src/test/java/helloworld/AppTest.java index b584ee944..70dad8d71 100644 --- a/examples/powertools-examples-core/src/test/java/helloworld/AppTest.java +++ b/examples/powertools-examples-core/src/test/java/helloworld/AppTest.java @@ -1,43 +1,59 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package helloworld; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; import com.amazonaws.xray.AWSXRay; import org.junit.After; import org.junit.Before; import org.junit.Test; -import static org.junit.Assert.*; - public class AppTest { - @Before - public void setup() { - if(null == System.getenv("LAMBDA_TASK_ROOT")) { - AWSXRay.beginSegment("test"); + @Before + public void setup() { + if (null == System.getenv("LAMBDA_TASK_ROOT")) { + AWSXRay.beginSegment("test"); + } } - } - @After - public void tearDown() { - if (AWSXRay.getCurrentSubsegmentOptional().isPresent()) { - AWSXRay.endSubsegment(); + @After + public void tearDown() { + if (AWSXRay.getCurrentSubsegmentOptional().isPresent()) { + AWSXRay.endSubsegment(); + } + + if (null == System.getenv("LAMBDA_TASK_ROOT")) { + AWSXRay.endSegment(); + } } - if(null == System.getenv("LAMBDA_TASK_ROOT")) { - AWSXRay.endSegment(); + @Test + public void successfulResponse() { + App app = new App(); + APIGatewayProxyResponseEvent result = app.handleRequest(null, null); + assertEquals(result.getStatusCode().intValue(), 200); + assertEquals(result.getHeaders().get("Content-Type"), "application/json"); + String content = result.getBody(); + assertNotNull(content); + assertTrue(content.contains("\"message\"")); + assertTrue(content.contains("\"hello world\"")); + assertTrue(content.contains("\"location\"")); } - } - - @Test - public void successfulResponse() { - App app = new App(); - APIGatewayProxyResponseEvent result = app.handleRequest(null, null); - assertEquals(result.getStatusCode().intValue(), 200); - assertEquals(result.getHeaders().get("Content-Type"), "application/json"); - String content = result.getBody(); - assertNotNull(content); - assertTrue(content.contains("\"message\"")); - assertTrue(content.contains("\"hello world\"")); - assertTrue(content.contains("\"location\"")); - } } diff --git a/examples/powertools-examples-idempotency/pom.xml b/examples/powertools-examples-idempotency/pom.xml index 0607fdd14..f24b2ffc4 100644 --- a/examples/powertools-examples-idempotency/pom.xml +++ b/examples/powertools-examples-idempotency/pom.xml @@ -1,3 +1,17 @@ + + 4.0.0 diff --git a/examples/powertools-examples-idempotency/src/main/java/helloworld/App.java b/examples/powertools-examples-idempotency/src/main/java/helloworld/App.java index 0a20aa3a4..ac2c7ef1b 100644 --- a/examples/powertools-examples-idempotency/src/main/java/helloworld/App.java +++ b/examples/powertools-examples-idempotency/src/main/java/helloworld/App.java @@ -1,9 +1,30 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package helloworld; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URL; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Collectors; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import software.amazon.awssdk.services.dynamodb.DynamoDbClient; @@ -14,14 +35,6 @@ import software.amazon.lambda.powertools.logging.Logging; import software.amazon.lambda.powertools.utilities.JsonConfig; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.net.URL; -import java.util.HashMap; -import java.util.Map; -import java.util.stream.Collectors; - public class App implements RequestHandler { private final static Logger log = LogManager.getLogger(App.class); @@ -32,7 +45,8 @@ public App() { public App(DynamoDbClient client) { Idempotency.config().withConfig( IdempotencyConfig.builder() - .withEventKeyJMESPath("powertools_json(body).address") // will retrieve the address field in the body which is a string transformed to json with `powertools_json` + .withEventKeyJMESPath( + "powertools_json(body).address") // will retrieve the address field in the body which is a string transformed to json with `powertools_json` .build()) .withPersistenceStore( DynamoDBPersistenceStore.builder() @@ -45,7 +59,7 @@ public App(DynamoDbClient client) { /** * This is our Lambda event handler. It accepts HTTP POST requests from API gateway and returns the contents of the given URL. Requests are made idempotent * by the idempotency library, and results are cached for the default 1h expiry time. - * + *

* You can test the endpoint like this: * *

@@ -89,13 +103,12 @@ public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEv
 
     /**
      * Helper to retrieve the contents of the given URL and return them as a string.
-     *
+     * 

* We could also put the @Idempotent annotation here if we only wanted this sub-operation to be idempotent. Putting * it on the handler, however, reduces total execution time and saves us time! * * @param address The URL to fetch * @return The contents of the given URL - * * @throws IOException */ private String getPageContents(String address) throws IOException { diff --git a/examples/powertools-examples-idempotency/src/test/java/helloworld/AppTest.java b/examples/powertools-examples-idempotency/src/test/java/helloworld/AppTest.java index 7a5304e36..7f097906a 100644 --- a/examples/powertools-examples-idempotency/src/test/java/helloworld/AppTest.java +++ b/examples/powertools-examples-idempotency/src/test/java/helloworld/AppTest.java @@ -1,3 +1,17 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package helloworld; import com.amazonaws.services.dynamodbv2.local.main.ServerRunner; @@ -5,6 +19,9 @@ import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; import com.amazonaws.services.lambda.runtime.tests.EventLoader; +import java.io.IOException; +import java.net.ServerSocket; +import java.net.URI; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; @@ -16,23 +33,24 @@ import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.model.*; - -import java.io.IOException; -import java.net.ServerSocket; -import java.net.URI; +import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition; +import software.amazon.awssdk.services.dynamodb.model.BillingMode; +import software.amazon.awssdk.services.dynamodb.model.CreateTableRequest; +import software.amazon.awssdk.services.dynamodb.model.KeySchemaElement; +import software.amazon.awssdk.services.dynamodb.model.KeyType; +import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType; public class AppTest { + private static DynamoDbClient client; @Mock private Context context; private App app; - private static DynamoDbClient client; @BeforeAll public static void setupDynamoLocal() { int port = getFreePort(); try { - DynamoDBProxyServer dynamoProxy = ServerRunner.createServerFromCommandLineArgs(new String[]{ + DynamoDBProxyServer dynamoProxy = ServerRunner.createServerFromCommandLineArgs(new String[] { "-inMemory", "-port", Integer.toString(port) @@ -79,7 +97,8 @@ void setUp() { @Test public void testApp() { - APIGatewayProxyResponseEvent response = app.handleRequest(EventLoader.loadApiGatewayRestEvent("event.json"), context); + APIGatewayProxyResponseEvent response = + app.handleRequest(EventLoader.loadApiGatewayRestEvent("event.json"), context); Assertions.assertNotNull(response); Assertions.assertTrue(response.getBody().contains("hello world")); } diff --git a/examples/powertools-examples-parameters/src/main/java/org/demo/parameters/MyObject.java b/examples/powertools-examples-parameters/src/main/java/org/demo/parameters/MyObject.java index 2cf145284..d406ae3df 100644 --- a/examples/powertools-examples-parameters/src/main/java/org/demo/parameters/MyObject.java +++ b/examples/powertools-examples-parameters/src/main/java/org/demo/parameters/MyObject.java @@ -1,3 +1,17 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package org.demo.parameters; public class MyObject { diff --git a/examples/powertools-examples-parameters/src/main/java/org/demo/parameters/ParametersFunction.java b/examples/powertools-examples-parameters/src/main/java/org/demo/parameters/ParametersFunction.java index f96352e86..5b691cfd9 100644 --- a/examples/powertools-examples-parameters/src/main/java/org/demo/parameters/ParametersFunction.java +++ b/examples/powertools-examples-parameters/src/main/java/org/demo/parameters/ParametersFunction.java @@ -1,15 +1,27 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package org.demo.parameters; +import static java.time.temporal.ChronoUnit.SECONDS; +import static software.amazon.lambda.powertools.parameters.transform.Transformer.base64; +import static software.amazon.lambda.powertools.parameters.transform.Transformer.json; + import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import software.amazon.lambda.powertools.parameters.ParamManager; -import software.amazon.lambda.powertools.parameters.SSMProvider; -import software.amazon.lambda.powertools.parameters.SecretsProvider; - import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; @@ -17,10 +29,11 @@ import java.util.HashMap; import java.util.Map; import java.util.stream.Collectors; - -import static java.time.temporal.ChronoUnit.SECONDS; -import static software.amazon.lambda.powertools.parameters.transform.Transformer.base64; -import static software.amazon.lambda.powertools.parameters.transform.Transformer.json; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import software.amazon.lambda.powertools.parameters.ParamManager; +import software.amazon.lambda.powertools.parameters.SSMProvider; +import software.amazon.lambda.powertools.parameters.SecretsProvider; public class ParametersFunction implements RequestHandler { private final static Logger log = LogManager.getLogger(ParametersFunction.class); @@ -34,8 +47,10 @@ public class ParametersFunction implements RequestHandler allValues = ssmProvider.getMultiple("/powertools-java/sample"); String b64value = ssmProvider.withTransformation(base64).get("/powertools-java/sample/keybase64"); - Map secretJson = secretsProvider.withTransformation(json).get("/powertools-java/userpwd", Map.class); - MyObject secretJsonObj = secretsProvider.withMaxAge(42, SECONDS).withTransformation(json).get("/powertools-java/secretcode", MyObject.class); + Map secretJson = + secretsProvider.withTransformation(json).get("/powertools-java/userpwd", Map.class); + MyObject secretJsonObj = secretsProvider.withMaxAge(42, SECONDS).withTransformation(json) + .get("/powertools-java/secretcode", MyObject.class); @Override public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent input, Context context) { @@ -72,9 +87,9 @@ public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent in } } - private String getPageContents(String address) throws IOException{ + private String getPageContents(String address) throws IOException { URL url = new URL(address); - try(BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream(), "UTF-8"))) { + try (BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream(), "UTF-8"))) { return br.lines().collect(Collectors.joining(System.lineSeparator())); } } diff --git a/examples/powertools-examples-serialization/src/main/java/org/demo/serialization/APIGatewayRequestDeserializationFunction.java b/examples/powertools-examples-serialization/src/main/java/org/demo/serialization/APIGatewayRequestDeserializationFunction.java index 8c33baed9..e70b37959 100644 --- a/examples/powertools-examples-serialization/src/main/java/org/demo/serialization/APIGatewayRequestDeserializationFunction.java +++ b/examples/powertools-examples-serialization/src/main/java/org/demo/serialization/APIGatewayRequestDeserializationFunction.java @@ -1,19 +1,33 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package org.demo.serialization; +import static software.amazon.lambda.powertools.utilities.EventDeserializer.extractDataFrom; + import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - import java.util.HashMap; import java.util.Map; - -import static software.amazon.lambda.powertools.utilities.EventDeserializer.extractDataFrom; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; -public class APIGatewayRequestDeserializationFunction implements RequestHandler { +public class APIGatewayRequestDeserializationFunction + implements RequestHandler { private final static Logger LOGGER = LogManager.getLogger(APIGatewayRequestDeserializationFunction.class); private static final Map HEADERS = new HashMap() {{ @@ -28,9 +42,9 @@ public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent ev LOGGER.info("product={}\n", product); return new APIGatewayProxyResponseEvent() - .withHeaders(HEADERS) - .withStatusCode(200) - .withBody("Received request for productId: " + product.getId()); + .withHeaders(HEADERS) + .withStatusCode(200) + .withBody("Received request for productId: " + product.getId()); } } diff --git a/examples/powertools-examples-serialization/src/main/java/org/demo/serialization/Product.java b/examples/powertools-examples-serialization/src/main/java/org/demo/serialization/Product.java index fb94a99f8..25bae34f6 100644 --- a/examples/powertools-examples-serialization/src/main/java/org/demo/serialization/Product.java +++ b/examples/powertools-examples-serialization/src/main/java/org/demo/serialization/Product.java @@ -1,3 +1,17 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package org.demo.serialization; public class Product { diff --git a/examples/powertools-examples-serialization/src/main/java/org/demo/serialization/SQSEventDeserializationFunction.java b/examples/powertools-examples-serialization/src/main/java/org/demo/serialization/SQSEventDeserializationFunction.java index 129fe0243..36dbed074 100644 --- a/examples/powertools-examples-serialization/src/main/java/org/demo/serialization/SQSEventDeserializationFunction.java +++ b/examples/powertools-examples-serialization/src/main/java/org/demo/serialization/SQSEventDeserializationFunction.java @@ -1,15 +1,28 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package org.demo.serialization; +import static software.amazon.lambda.powertools.utilities.EventDeserializer.extractDataFrom; + import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.lambda.runtime.events.SQSEvent; +import java.util.List; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import java.util.List; - -import static software.amazon.lambda.powertools.utilities.EventDeserializer.extractDataFrom; - public class SQSEventDeserializationFunction implements RequestHandler { diff --git a/examples/powertools-examples-serialization/src/test/java/org/demo/serialization/APIGatewayRequestDeserializationFunctionTest.java b/examples/powertools-examples-serialization/src/test/java/org/demo/serialization/APIGatewayRequestDeserializationFunctionTest.java index 5d5da7ecc..ec8cdbd33 100644 --- a/examples/powertools-examples-serialization/src/test/java/org/demo/serialization/APIGatewayRequestDeserializationFunctionTest.java +++ b/examples/powertools-examples-serialization/src/test/java/org/demo/serialization/APIGatewayRequestDeserializationFunctionTest.java @@ -1,5 +1,21 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package org.demo.serialization; +import static org.junit.jupiter.api.Assertions.assertEquals; + import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; @@ -8,8 +24,6 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import static org.junit.jupiter.api.Assertions.assertEquals; - class APIGatewayRequestDeserializationFunctionTest { @Mock diff --git a/examples/powertools-examples-serialization/src/test/java/org/demo/serialization/SQSEventDeserializationFunctionTest.java b/examples/powertools-examples-serialization/src/test/java/org/demo/serialization/SQSEventDeserializationFunctionTest.java index 6979a6868..b46af3052 100644 --- a/examples/powertools-examples-serialization/src/test/java/org/demo/serialization/SQSEventDeserializationFunctionTest.java +++ b/examples/powertools-examples-serialization/src/test/java/org/demo/serialization/SQSEventDeserializationFunctionTest.java @@ -1,17 +1,29 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package org.demo.serialization; +import static org.junit.jupiter.api.Assertions.assertEquals; + import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.events.SQSEvent; +import java.util.ArrayList; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import java.util.List; -import java.util.ArrayList; - -import static org.junit.jupiter.api.Assertions.assertEquals; - class SQSEventDeserializationFunctionTest { @Mock @@ -29,7 +41,7 @@ public void shouldReturnNumberOfReceivedMessages() { SQSEvent.SQSMessage message1 = messageWithBody("{ \"id\": 1234, \"name\": \"product\", \"price\": 42}"); SQSEvent.SQSMessage message2 = messageWithBody("{ \"id\": 12345, \"name\": \"product5\", \"price\": 45}"); SQSEvent event = new SQSEvent(); - event.setRecords(new ArrayList(){{ + event.setRecords(new ArrayList() {{ add(message1); add(message2); }}); diff --git a/examples/powertools-examples-sqs/src/main/java/org/demo/sqs/SqsMessageSender.java b/examples/powertools-examples-sqs/src/main/java/org/demo/sqs/SqsMessageSender.java index b90c50654..45856d198 100644 --- a/examples/powertools-examples-sqs/src/main/java/org/demo/sqs/SqsMessageSender.java +++ b/examples/powertools-examples-sqs/src/main/java/org/demo/sqs/SqsMessageSender.java @@ -1,11 +1,32 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ package org.demo.sqs; +import static java.util.stream.Collectors.toList; + import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.lambda.runtime.events.ScheduledEvent; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.joda.JodaModule; +import java.security.SecureRandom; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.stream.IntStream; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; @@ -17,15 +38,6 @@ import software.amazon.lambda.powertools.logging.Logging; import software.amazon.lambda.powertools.logging.LoggingUtils; -import java.security.SecureRandom; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Random; -import java.util.stream.IntStream; - -import static java.util.stream.Collectors.toList; - public class SqsMessageSender implements RequestHandler { private static final Logger log = LogManager.getLogger(SqsMessageSender.class); @@ -50,22 +62,23 @@ public String handleRequest(final ScheduledEvent input, final Context context) { // Push 5 messages on each invoke. List batchRequestEntries = IntStream.range(0, 5) - .mapToObj(value -> { - Map attributeValueHashMap = new HashMap<>(); - attributeValueHashMap.put("Key" + value, MessageAttributeValue.builder() - .dataType("String") - .stringValue("Value" + value) - .build()); - - byte[] array = new byte[7]; - random.nextBytes(array); - - return SendMessageBatchRequestEntry.builder() - .messageAttributes(attributeValueHashMap) - .id(input.getId() + value) - .messageBody("Sample Message " + value) - .build(); - }).collect(toList()); + .mapToObj(value -> + { + Map attributeValueHashMap = new HashMap<>(); + attributeValueHashMap.put("Key" + value, MessageAttributeValue.builder() + .dataType("String") + .stringValue("Value" + value) + .build()); + + byte[] array = new byte[7]; + random.nextBytes(array); + + return SendMessageBatchRequestEntry.builder() + .messageAttributes(attributeValueHashMap) + .id(input.getId() + value) + .messageBody("Sample Message " + value) + .build(); + }).collect(toList()); SendMessageBatchResponse sendMessageBatchResponse = sqsClient.sendMessageBatch(SendMessageBatchRequest.builder() .queueUrl(queueUrl) diff --git a/examples/powertools-examples-sqs/src/main/java/org/demo/sqs/SqsPoller.java b/examples/powertools-examples-sqs/src/main/java/org/demo/sqs/SqsPoller.java index bf2b7bdfe..9ad5c7868 100644 --- a/examples/powertools-examples-sqs/src/main/java/org/demo/sqs/SqsPoller.java +++ b/examples/powertools-examples-sqs/src/main/java/org/demo/sqs/SqsPoller.java @@ -1,10 +1,26 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package org.demo.sqs; -import java.util.Random; +import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.lambda.runtime.events.SQSEvent; +import java.security.SecureRandom; +import java.util.Random; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; @@ -13,18 +29,12 @@ import software.amazon.lambda.powertools.sqs.SqsBatch; import software.amazon.lambda.powertools.sqs.SqsMessageHandler; import software.amazon.lambda.powertools.sqs.SqsUtils; -import java.security.SecureRandom; - -import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; /** * Handler for requests to Lambda function. */ public class SqsPoller implements RequestHandler { - Logger log = LogManager.getLogger(SqsPoller.class); - Random random = new SecureRandom(); - static { // https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/lambda-optimize-starttime.html SqsUtils.overrideSqsClient(SqsClient.builder() @@ -32,6 +42,9 @@ public class SqsPoller implements RequestHandler { .build()); } + Logger log = LogManager.getLogger(SqsPoller.class); + Random random = new SecureRandom(); + @SqsBatch(value = BatchProcessor.class, nonRetryableExceptions = {IllegalArgumentException.class}) @Logging(logEvent = true) public String handleRequest(final SQSEvent input, final Context context) { @@ -45,14 +58,16 @@ public String process(SQSMessage message) { int nextInt = random.nextInt(100); - if(nextInt <= 10) { + if (nextInt <= 10) { log.info("Randomly picked message with id {} as business validation failure.", message.getMessageId()); - throw new IllegalArgumentException("Failed business validation. No point of retrying. Move me to DLQ." + message.getMessageId()); + throw new IllegalArgumentException( + "Failed business validation. No point of retrying. Move me to DLQ." + message.getMessageId()); } - if(nextInt > 90) { + if (nextInt > 90) { log.info("Randomly picked message with id {} as intermittent failure.", message.getMessageId()); - throw new RuntimeException("Failed due to intermittent issue. Will be sent back for retry." + message.getMessageId()); + throw new RuntimeException( + "Failed due to intermittent issue. Will be sent back for retry." + message.getMessageId()); } return "Success"; diff --git a/examples/powertools-examples-validation/pom.xml b/examples/powertools-examples-validation/pom.xml index 455fd66b8..1c7e33de0 100644 --- a/examples/powertools-examples-validation/pom.xml +++ b/examples/powertools-examples-validation/pom.xml @@ -1,3 +1,17 @@ + + 4.0.0 diff --git a/examples/powertools-examples-validation/src/main/java/org/demo/validation/InboundValidation.java b/examples/powertools-examples-validation/src/main/java/org/demo/validation/InboundValidation.java index 89ec538c9..d3b8e51e4 100644 --- a/examples/powertools-examples-validation/src/main/java/org/demo/validation/InboundValidation.java +++ b/examples/powertools-examples-validation/src/main/java/org/demo/validation/InboundValidation.java @@ -1,11 +1,23 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package org.demo.validation; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; -import software.amazon.lambda.powertools.validation.Validation; - import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; @@ -13,6 +25,7 @@ import java.util.HashMap; import java.util.Map; import java.util.stream.Collectors; +import software.amazon.lambda.powertools.validation.Validation; /** * Request handler for Lambda function which demonstrates validation of request message. @@ -20,7 +33,8 @@ public class InboundValidation implements RequestHandler { @Validation(inboundSchema = "classpath:/schema.json") - public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent apiGatewayProxyRequestEvent, Context context) { + public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent apiGatewayProxyRequestEvent, + Context context) { Map headers = new HashMap<>(); headers.put("Content-Type", "application/json"); diff --git a/examples/powertools-examples-validation/src/test/java/org/demo/validation/InboundValidationTest.java b/examples/powertools-examples-validation/src/test/java/org/demo/validation/InboundValidationTest.java index af47d3d87..d5e6de313 100644 --- a/examples/powertools-examples-validation/src/test/java/org/demo/validation/InboundValidationTest.java +++ b/examples/powertools-examples-validation/src/test/java/org/demo/validation/InboundValidationTest.java @@ -1,5 +1,22 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package org.demo.validation; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; @@ -9,9 +26,6 @@ import org.mockito.MockitoAnnotations; import software.amazon.lambda.powertools.validation.ValidationException; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; - public class InboundValidationTest { @Mock diff --git a/license-header b/license-header new file mode 100644 index 000000000..5669f143f --- /dev/null +++ b/license-header @@ -0,0 +1,13 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ diff --git a/pom.xml b/pom.xml index ccc27b64c..7744913fd 100644 --- a/pom.xml +++ b/pom.xml @@ -1,4 +1,18 @@ + + @@ -525,6 +539,45 @@ + + jdk11 + + 11 + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + 3.3.0 + + checkstyle.xml + UTF-8 + true + true + false + + + + + + com.puppycrawl.tools + checkstyle + 10.9.1 + + + + + + check + + + + + + + diff --git a/powertools-cloudformation/pom.xml b/powertools-cloudformation/pom.xml index 3a846c378..a122e7ac8 100644 --- a/powertools-cloudformation/pom.xml +++ b/powertools-cloudformation/pom.xml @@ -1,4 +1,18 @@ + + @@ -100,4 +114,12 @@ + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + + \ No newline at end of file diff --git a/powertools-cloudformation/src/main/java/software/amazon/lambda/powertools/cloudformation/AbstractCustomResourceHandler.java b/powertools-cloudformation/src/main/java/software/amazon/lambda/powertools/cloudformation/AbstractCustomResourceHandler.java index 7d3a43069..7f5b6bb24 100644 --- a/powertools-cloudformation/src/main/java/software/amazon/lambda/powertools/cloudformation/AbstractCustomResourceHandler.java +++ b/powertools-cloudformation/src/main/java/software/amazon/lambda/powertools/cloudformation/AbstractCustomResourceHandler.java @@ -1,16 +1,29 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.cloudformation; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.lambda.runtime.events.CloudFormationCustomResourceEvent; +import java.io.IOException; +import java.util.Objects; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import software.amazon.awssdk.http.SdkHttpClient; import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; -import java.io.IOException; -import java.util.Objects; - /** * Handler base class providing core functionality for sending responses to custom CloudFormation resources after * receiving some event. Depending on the type of event, this class either invokes the crete, update, or delete method diff --git a/powertools-cloudformation/src/main/java/software/amazon/lambda/powertools/cloudformation/CloudFormationResponse.java b/powertools-cloudformation/src/main/java/software/amazon/lambda/powertools/cloudformation/CloudFormationResponse.java index 39a86293b..2f020aa25 100644 --- a/powertools-cloudformation/src/main/java/software/amazon/lambda/powertools/cloudformation/CloudFormationResponse.java +++ b/powertools-cloudformation/src/main/java/software/amazon/lambda/powertools/cloudformation/CloudFormationResponse.java @@ -1,3 +1,17 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.cloudformation; import com.amazonaws.services.lambda.runtime.Context; @@ -6,6 +20,13 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.PropertyNamingStrategies; import com.fasterxml.jackson.databind.node.ObjectNode; +import java.io.IOException; +import java.net.URI; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import software.amazon.awssdk.http.Header; @@ -16,14 +37,6 @@ import software.amazon.awssdk.http.SdkHttpRequest; import software.amazon.awssdk.utils.StringInputStream; -import java.io.IOException; -import java.net.URI; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; - /** * Client for sending responses to AWS CloudFormation custom resources by way of a response URL, which is an Amazon S3 * pre-signed URL. @@ -35,102 +48,6 @@ class CloudFormationResponse { private static final Logger LOG = LoggerFactory.getLogger(CloudFormationResponse.class); - - /** - * Internal representation of the payload to be sent to the event target URL. Retains all properties of the payload - * except for "Data". This is done so that the serialization of the non-"Data" properties and the serialization of - * the value of "Data" can be handled by separate ObjectMappers, if need be. The former properties are dictated by - * the custom resource but the latter is dictated by the implementor of the custom resource handler. - */ - @SuppressWarnings("unused") - static class ResponseBody { - static final ObjectMapper MAPPER = new ObjectMapper() - .setPropertyNamingStrategy(PropertyNamingStrategies.UPPER_CAMEL_CASE); - private static final String DATA_PROPERTY_NAME = "Data"; - - private final String status; - private final String reason; - private final String physicalResourceId; - private final String stackId; - private final String requestId; - private final String logicalResourceId; - private final boolean noEcho; - - ResponseBody(CloudFormationCustomResourceEvent event, - Response.Status responseStatus, - String physicalResourceId, - boolean noEcho, - String reason) { - Objects.requireNonNull(event, "CloudFormationCustomResourceEvent cannot be null"); - - this.physicalResourceId = physicalResourceId; - this.reason = reason; - this.status = responseStatus == null ? Response.Status.SUCCESS.name() : responseStatus.name(); - this.stackId = event.getStackId(); - this.requestId = event.getRequestId(); - this.logicalResourceId = event.getLogicalResourceId(); - this.noEcho = noEcho; - } - - public String getStatus() { - return status; - } - - public String getReason() { - return reason; - } - - public String getPhysicalResourceId() { - return physicalResourceId; - } - - public String getStackId() { - return stackId; - } - - public String getRequestId() { - return requestId; - } - - public String getLogicalResourceId() { - return logicalResourceId; - } - - public boolean isNoEcho() { - return noEcho; - } - - /** - * Returns this ResponseBody as an ObjectNode with the provided JsonNode as the value of its "Data" property. - * - * @param dataNode the value of the "Data" property for the returned node; may be null - * @return an ObjectNode representation of this ResponseBody and the provided dataNode - */ - ObjectNode toObjectNode(JsonNode dataNode) { - ObjectNode node = MAPPER.valueToTree(this); - if (dataNode == null) { - node.putNull(DATA_PROPERTY_NAME); - } else { - node.set(DATA_PROPERTY_NAME, dataNode); - } - return node; - } - - @Override - public String toString() { - final StringBuffer sb = new StringBuffer("ResponseBody{"); - sb.append("status='").append(status).append('\''); - sb.append(", reason='").append(reason).append('\''); - sb.append(", physicalResourceId='").append(physicalResourceId).append('\''); - sb.append(", stackId='").append(stackId).append('\''); - sb.append(", requestId='").append(requestId).append('\''); - sb.append(", logicalResourceId='").append(logicalResourceId).append('\''); - sb.append(", noEcho=").append(noEcho); - sb.append('}'); - return sb.toString(); - } - } - private final SdkHttpClient client; /** @@ -212,7 +129,7 @@ protected Map> headers(int contentLength) { /** * Returns the response body as an input stream, for supplying with the HTTP request to the custom resource. - * + *

* If PhysicalResourceId is null at this point it will be replaced with the Lambda LogStreamName. * * @throws CustomResourceResponseException if unable to generate the response stream @@ -223,7 +140,8 @@ StringInputStream responseBodyStream(CloudFormationCustomResourceEvent event, try { String reason = "See the details in CloudWatch Log Stream: " + context.getLogStreamName(); if (resp == null) { - String physicalResourceId = event.getPhysicalResourceId() != null? event.getPhysicalResourceId() : context.getLogStreamName(); + String physicalResourceId = event.getPhysicalResourceId() != null ? event.getPhysicalResourceId() : + context.getLogStreamName(); ResponseBody body = new ResponseBody(event, Response.Status.SUCCESS, physicalResourceId, false, reason); LOG.debug("ResponseBody: {}", body); @@ -232,9 +150,11 @@ StringInputStream responseBodyStream(CloudFormationCustomResourceEvent event, } else { String physicalResourceId = resp.getPhysicalResourceId() != null ? resp.getPhysicalResourceId() : - event.getPhysicalResourceId() != null? event.getPhysicalResourceId() : context.getLogStreamName(); + event.getPhysicalResourceId() != null ? event.getPhysicalResourceId() : + context.getLogStreamName(); - ResponseBody body = new ResponseBody(event, resp.getStatus(), physicalResourceId, resp.isNoEcho(), reason); + ResponseBody body = + new ResponseBody(event, resp.getStatus(), physicalResourceId, resp.isNoEcho(), reason); LOG.debug("ResponseBody: {}", body); ObjectNode node = body.toObjectNode(resp.getJsonNode()); return new StringInputStream(node.toString()); @@ -244,4 +164,99 @@ StringInputStream responseBodyStream(CloudFormationCustomResourceEvent event, throw new CustomResourceResponseException("Unable to generate response body.", e); } } + + /** + * Internal representation of the payload to be sent to the event target URL. Retains all properties of the payload + * except for "Data". This is done so that the serialization of the non-"Data" properties and the serialization of + * the value of "Data" can be handled by separate ObjectMappers, if need be. The former properties are dictated by + * the custom resource but the latter is dictated by the implementor of the custom resource handler. + */ + @SuppressWarnings("unused") + static class ResponseBody { + static final ObjectMapper MAPPER = new ObjectMapper() + .setPropertyNamingStrategy(PropertyNamingStrategies.UPPER_CAMEL_CASE); + private static final String DATA_PROPERTY_NAME = "Data"; + + private final String status; + private final String reason; + private final String physicalResourceId; + private final String stackId; + private final String requestId; + private final String logicalResourceId; + private final boolean noEcho; + + ResponseBody(CloudFormationCustomResourceEvent event, + Response.Status responseStatus, + String physicalResourceId, + boolean noEcho, + String reason) { + Objects.requireNonNull(event, "CloudFormationCustomResourceEvent cannot be null"); + + this.physicalResourceId = physicalResourceId; + this.reason = reason; + this.status = responseStatus == null ? Response.Status.SUCCESS.name() : responseStatus.name(); + this.stackId = event.getStackId(); + this.requestId = event.getRequestId(); + this.logicalResourceId = event.getLogicalResourceId(); + this.noEcho = noEcho; + } + + public String getStatus() { + return status; + } + + public String getReason() { + return reason; + } + + public String getPhysicalResourceId() { + return physicalResourceId; + } + + public String getStackId() { + return stackId; + } + + public String getRequestId() { + return requestId; + } + + public String getLogicalResourceId() { + return logicalResourceId; + } + + public boolean isNoEcho() { + return noEcho; + } + + /** + * Returns this ResponseBody as an ObjectNode with the provided JsonNode as the value of its "Data" property. + * + * @param dataNode the value of the "Data" property for the returned node; may be null + * @return an ObjectNode representation of this ResponseBody and the provided dataNode + */ + ObjectNode toObjectNode(JsonNode dataNode) { + ObjectNode node = MAPPER.valueToTree(this); + if (dataNode == null) { + node.putNull(DATA_PROPERTY_NAME); + } else { + node.set(DATA_PROPERTY_NAME, dataNode); + } + return node; + } + + @Override + public String toString() { + final StringBuffer sb = new StringBuffer("ResponseBody{"); + sb.append("status='").append(status).append('\''); + sb.append(", reason='").append(reason).append('\''); + sb.append(", physicalResourceId='").append(physicalResourceId).append('\''); + sb.append(", stackId='").append(stackId).append('\''); + sb.append(", requestId='").append(requestId).append('\''); + sb.append(", logicalResourceId='").append(logicalResourceId).append('\''); + sb.append(", noEcho=").append(noEcho); + sb.append('}'); + return sb.toString(); + } + } } diff --git a/powertools-cloudformation/src/main/java/software/amazon/lambda/powertools/cloudformation/CustomResourceResponseException.java b/powertools-cloudformation/src/main/java/software/amazon/lambda/powertools/cloudformation/CustomResourceResponseException.java index ead912392..904ae9c3f 100644 --- a/powertools-cloudformation/src/main/java/software/amazon/lambda/powertools/cloudformation/CustomResourceResponseException.java +++ b/powertools-cloudformation/src/main/java/software/amazon/lambda/powertools/cloudformation/CustomResourceResponseException.java @@ -1,3 +1,17 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.cloudformation; /** diff --git a/powertools-cloudformation/src/main/java/software/amazon/lambda/powertools/cloudformation/Response.java b/powertools-cloudformation/src/main/java/software/amazon/lambda/powertools/cloudformation/Response.java index f388f6384..fe18000d4 100644 --- a/powertools-cloudformation/src/main/java/software/amazon/lambda/powertools/cloudformation/Response.java +++ b/powertools-cloudformation/src/main/java/software/amazon/lambda/powertools/cloudformation/Response.java @@ -1,8 +1,21 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.cloudformation; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; - import java.util.HashMap; import java.util.Map; import java.util.stream.Collectors; @@ -13,119 +26,16 @@ */ public class Response { - /** - * Indicates whether a response is a success or failure. - */ - public enum Status { - SUCCESS, FAILED - } - - /** - * For building Response instances. - */ - public static class Builder { - private Object value; - private ObjectMapper objectMapper; - private Status status; - private String physicalResourceId; - private boolean noEcho; - - private Builder() { - } - - /** - * Configures the value of this Response, typically a Map of name/value pairs. - * - * @param value if null, the Response will be empty - * @return a reference to this builder - */ - public Builder value(Object value) { - this.value = value; - return this; - } - - /** - * Configures a custom ObjectMapper for serializing the value object. Creates a copy of the mapper provided; - * future mutations of the ObjectMapper made using the provided reference will not affect Response - * serialization. - * - * @param objectMapper if null, a default mapper will be used - * @return a reference to this builder - */ - public Builder objectMapper(ObjectMapper objectMapper) { - this.objectMapper = objectMapper == null ? null : objectMapper.copy(); - return this; - } - - /** - * Configures the status of this response. - * - * @param status if null, SUCCESS will be assumed - * @return a reference to this builder - */ - public Builder status(Status status) { - this.status = status; - return this; - } - - /** - * A unique identifier for the custom resource being responded to. By default, the identifier is the name of the - * Amazon CloudWatch Logs log stream associated with the Lambda function. - * - * @param physicalResourceId if null, the default resource ID will be used - * @return a reference to this builder - */ - public Builder physicalResourceId(String physicalResourceId) { - this.physicalResourceId = physicalResourceId; - return this; - } - - /** - * Indicates whether to mask the output of the custom resource when it's retrieved by using the Fn::GetAtt - * function. If set to true, values will be masked with asterisks (*****), except for information stored in the - * these locations: - *

    - *
  • The Metadata template section. CloudFormation does not transform, modify, or redact any information - * included in the Metadata section. See - * https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/metadata-section-structure.html
  • - *
  • The Outputs template section. See - * https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/outputs-section-structure.html
  • - *
  • The Metadata attribute of a resource definition. See - * https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-attribute-metadata.html
  • - *
- *

- * We strongly recommend not using these mechanisms to include sensitive information, such as passwords or - * secrets. - *

- * For more information about using noEcho to mask sensitive information, see - * https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/best-practices.html#creds - *

- * By default, this value is false. - * - * @param noEcho when true, masks certain output - * @return a reference to this builder - */ - public Builder noEcho(boolean noEcho) { - this.noEcho = noEcho; - return this; - } + private final JsonNode jsonNode; + private final Status status; + private final String physicalResourceId; + private final boolean noEcho; - /** - * Builds a Response object for the value. - * - * @return a Response object wrapping the initially provided value. - */ - public Response build() { - JsonNode node; - if (value == null) { - node = null; - } else { - ObjectMapper mapper = objectMapper != null ? objectMapper : CloudFormationResponse.ResponseBody.MAPPER; - node = mapper.valueToTree(value); - } - Status responseStatus = this.status != null ? this.status : Status.SUCCESS; - return new Response(node, responseStatus, physicalResourceId, noEcho); - } + private Response(JsonNode jsonNode, Status status, String physicalResourceId, boolean noEcho) { + this.jsonNode = jsonNode; + this.status = status; + this.physicalResourceId = physicalResourceId; + this.noEcho = noEcho; } /** @@ -140,14 +50,14 @@ public static Builder builder() { /** * Creates a failed Response with no physicalResourceId set. Powertools for AWS Lambda (Java) will set the physicalResourceId to the * Lambda LogStreamName - * + *

* The value returned for a PhysicalResourceId can change custom resource update operations. If the value returned * is the same, it is considered a normal update. If the value returned is different, AWS CloudFormation recognizes * the update as a replacement and sends a delete request to the old resource. For more information, * see AWS::CloudFormation::CustomResource. * - * @deprecated this method is not safe. Provide a physicalResourceId. * @return a failed Response with no value. + * @deprecated this method is not safe. Provide a physicalResourceId. */ @Deprecated public static Response failed() { @@ -173,14 +83,14 @@ public static Response failed(String physicalResourceId) { /** * Creates a successful Response with no physicalResourceId set. Powertools for AWS Lambda (Java) will set the physicalResourceId to the * Lambda LogStreamName - * + *

* The value returned for a PhysicalResourceId can change custom resource update operations. If the value returned * is the same, it is considered a normal update. If the value returned is different, AWS CloudFormation recognizes * the update as a replacement and sends a delete request to the old resource. For more information, * see AWS::CloudFormation::CustomResource. * - * @deprecated this method is not safe. Provide a physicalResourceId. * @return a success Response with no physicalResourceId value. + * @deprecated this method is not safe. Provide a physicalResourceId. */ @Deprecated public static Response success() { @@ -203,18 +113,6 @@ public static Response success(String physicalResourceId) { return new Response(null, Status.SUCCESS, physicalResourceId, false); } - private final JsonNode jsonNode; - private final Status status; - private final String physicalResourceId; - private final boolean noEcho; - - private Response(JsonNode jsonNode, Status status, String physicalResourceId, boolean noEcho) { - this.jsonNode = jsonNode; - this.status = status; - this.physicalResourceId = physicalResourceId; - this.noEcho = noEcho; - } - /** * Returns a JsonNode representation of the Response. * @@ -267,4 +165,119 @@ public String toString() { .map(entry -> entry.getKey() + " = " + entry.getValue()) .collect(Collectors.joining(",", "[", "]")); } + + /** + * Indicates whether a response is a success or failure. + */ + public enum Status { + SUCCESS, FAILED + } + + /** + * For building Response instances. + */ + public static class Builder { + private Object value; + private ObjectMapper objectMapper; + private Status status; + private String physicalResourceId; + private boolean noEcho; + + private Builder() { + } + + /** + * Configures the value of this Response, typically a Map of name/value pairs. + * + * @param value if null, the Response will be empty + * @return a reference to this builder + */ + public Builder value(Object value) { + this.value = value; + return this; + } + + /** + * Configures a custom ObjectMapper for serializing the value object. Creates a copy of the mapper provided; + * future mutations of the ObjectMapper made using the provided reference will not affect Response + * serialization. + * + * @param objectMapper if null, a default mapper will be used + * @return a reference to this builder + */ + public Builder objectMapper(ObjectMapper objectMapper) { + this.objectMapper = objectMapper == null ? null : objectMapper.copy(); + return this; + } + + /** + * Configures the status of this response. + * + * @param status if null, SUCCESS will be assumed + * @return a reference to this builder + */ + public Builder status(Status status) { + this.status = status; + return this; + } + + /** + * A unique identifier for the custom resource being responded to. By default, the identifier is the name of the + * Amazon CloudWatch Logs log stream associated with the Lambda function. + * + * @param physicalResourceId if null, the default resource ID will be used + * @return a reference to this builder + */ + public Builder physicalResourceId(String physicalResourceId) { + this.physicalResourceId = physicalResourceId; + return this; + } + + /** + * Indicates whether to mask the output of the custom resource when it's retrieved by using the Fn::GetAtt + * function. If set to true, values will be masked with asterisks (*****), except for information stored in the + * these locations: + *

    + *
  • The Metadata template section. CloudFormation does not transform, modify, or redact any information + * included in the Metadata section. See + * https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/metadata-section-structure.html
  • + *
  • The Outputs template section. See + * https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/outputs-section-structure.html
  • + *
  • The Metadata attribute of a resource definition. See + * https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-attribute-metadata.html
  • + *
+ *

+ * We strongly recommend not using these mechanisms to include sensitive information, such as passwords or + * secrets. + *

+ * For more information about using noEcho to mask sensitive information, see + * https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/best-practices.html#creds + *

+ * By default, this value is false. + * + * @param noEcho when true, masks certain output + * @return a reference to this builder + */ + public Builder noEcho(boolean noEcho) { + this.noEcho = noEcho; + return this; + } + + /** + * Builds a Response object for the value. + * + * @return a Response object wrapping the initially provided value. + */ + public Response build() { + JsonNode node; + if (value == null) { + node = null; + } else { + ObjectMapper mapper = objectMapper != null ? objectMapper : CloudFormationResponse.ResponseBody.MAPPER; + node = mapper.valueToTree(value); + } + Status responseStatus = this.status != null ? this.status : Status.SUCCESS; + return new Response(node, responseStatus, physicalResourceId, noEcho); + } + } } diff --git a/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/AbstractCustomResourceHandlerTest.java b/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/AbstractCustomResourceHandlerTest.java index d68b434d6..1e399ef6f 100644 --- a/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/AbstractCustomResourceHandlerTest.java +++ b/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/AbstractCustomResourceHandlerTest.java @@ -1,14 +1,18 @@ -package software.amazon.lambda.powertools.cloudformation; +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.events.CloudFormationCustomResourceEvent; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.CsvSource; -import software.amazon.awssdk.http.SdkHttpClient; -import software.amazon.lambda.powertools.cloudformation.Response.Status; - -import java.io.IOException; +package software.amazon.lambda.powertools.cloudformation; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; @@ -21,95 +25,16 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -public class AbstractCustomResourceHandlerTest { - - /** - * Bare-bones implementation that returns null for abstract methods. - */ - static class NullCustomResourceHandler extends AbstractCustomResourceHandler { - NullCustomResourceHandler() { - } - - NullCustomResourceHandler(SdkHttpClient client) { - super(client); - } - - @Override - protected Response create(CloudFormationCustomResourceEvent event, Context context) { - return null; - } - - @Override - protected Response update(CloudFormationCustomResourceEvent event, Context context) { - return null; - } - - @Override - protected Response delete(CloudFormationCustomResourceEvent event, Context context) { - return null; - } - } - - /** - * Uses a mocked CloudFormationResponse to avoid sending actual HTTP requests. - */ - static class NoOpCustomResourceHandler extends NullCustomResourceHandler { - - NoOpCustomResourceHandler() { - super(mock(SdkHttpClient.class)); - } - - @Override - protected CloudFormationResponse buildResponseClient() { - return mock(CloudFormationResponse.class); - } - } - - /** - * Creates a handler that will expect the Response to be sent with an expected status. Will throw an AssertionError - * if the method is sent with an unexpected status. - */ - static class ExpectedStatusResourceHandler extends NoOpCustomResourceHandler { - private final Status expectedStatus; - - ExpectedStatusResourceHandler(Status expectedStatus) { - this.expectedStatus = expectedStatus; - } - - @Override - protected CloudFormationResponse buildResponseClient() { - // create a CloudFormationResponse that fails if invoked with unexpected status - CloudFormationResponse cfnResponse = mock(CloudFormationResponse.class); - try { - when(cfnResponse.send(any(), any(), argThat(resp -> resp.getStatus() != expectedStatus))) - .thenThrow(new AssertionError("Expected response's status to be " + expectedStatus)); - } catch (IOException | CustomResourceResponseException e) { - // this should never happen - throw new RuntimeException("Unexpected mocking exception", e); - } - return cfnResponse; - } - } +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.CloudFormationCustomResourceEvent; +import java.io.IOException; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import software.amazon.awssdk.http.SdkHttpClient; +import software.amazon.lambda.powertools.cloudformation.Response.Status; - /** - * Always fails to send the response - */ - static class FailToSendResponseHandler extends NoOpCustomResourceHandler { - @Override - protected CloudFormationResponse buildResponseClient() { - CloudFormationResponse cfnResponse = mock(CloudFormationResponse.class); - try { - when(cfnResponse.send(any(), any())) - .thenThrow(new IOException("Intentional send failure")); - when(cfnResponse.send(any(), any(), any())) - .thenThrow(new IOException("Intentional send failure")); - } catch (IOException | CustomResourceResponseException e) { - // this should never happen - throw new RuntimeException("Unexpected mocking exception", e); - } - return cfnResponse; - } - } +public class AbstractCustomResourceHandlerTest { /** * Builds a valid Event with the provide request type. @@ -288,4 +213,92 @@ protected Response create(CloudFormationCustomResourceEvent event, Context conte verify(handler, times(1)) .onSendFailure(eq(event), eq(context), isNull(), any(IOException.class)); } + + /** + * Bare-bones implementation that returns null for abstract methods. + */ + static class NullCustomResourceHandler extends AbstractCustomResourceHandler { + NullCustomResourceHandler() { + } + + NullCustomResourceHandler(SdkHttpClient client) { + super(client); + } + + @Override + protected Response create(CloudFormationCustomResourceEvent event, Context context) { + return null; + } + + @Override + protected Response update(CloudFormationCustomResourceEvent event, Context context) { + return null; + } + + @Override + protected Response delete(CloudFormationCustomResourceEvent event, Context context) { + return null; + } + } + + /** + * Uses a mocked CloudFormationResponse to avoid sending actual HTTP requests. + */ + static class NoOpCustomResourceHandler extends NullCustomResourceHandler { + + NoOpCustomResourceHandler() { + super(mock(SdkHttpClient.class)); + } + + @Override + protected CloudFormationResponse buildResponseClient() { + return mock(CloudFormationResponse.class); + } + } + + /** + * Creates a handler that will expect the Response to be sent with an expected status. Will throw an AssertionError + * if the method is sent with an unexpected status. + */ + static class ExpectedStatusResourceHandler extends NoOpCustomResourceHandler { + private final Status expectedStatus; + + ExpectedStatusResourceHandler(Status expectedStatus) { + this.expectedStatus = expectedStatus; + } + + @Override + protected CloudFormationResponse buildResponseClient() { + // create a CloudFormationResponse that fails if invoked with unexpected status + CloudFormationResponse cfnResponse = mock(CloudFormationResponse.class); + try { + when(cfnResponse.send(any(), any(), argThat(resp -> resp.getStatus() != expectedStatus))) + .thenThrow(new AssertionError("Expected response's status to be " + expectedStatus)); + } catch (IOException | CustomResourceResponseException e) { + // this should never happen + throw new RuntimeException("Unexpected mocking exception", e); + } + return cfnResponse; + } + } + + /** + * Always fails to send the response + */ + static class FailToSendResponseHandler extends NoOpCustomResourceHandler { + @Override + protected CloudFormationResponse buildResponseClient() { + CloudFormationResponse cfnResponse = mock(CloudFormationResponse.class); + try { + when(cfnResponse.send(any(), any())) + .thenThrow(new IOException("Intentional send failure")); + when(cfnResponse.send(any(), any(), any())) + .thenThrow(new IOException("Intentional send failure")); + } catch (IOException | CustomResourceResponseException e) { + // this should never happen + throw new RuntimeException("Unexpected mocking exception", e); + } + return cfnResponse; + } + } } diff --git a/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/CloudFormationIntegrationTest.java b/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/CloudFormationIntegrationTest.java index 06463308c..ce45d3afc 100644 --- a/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/CloudFormationIntegrationTest.java +++ b/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/CloudFormationIntegrationTest.java @@ -1,5 +1,28 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.cloudformation; +import static com.github.tomakehurst.wiremock.client.WireMock.matchingJsonPath; +import static com.github.tomakehurst.wiremock.client.WireMock.ok; +import static com.github.tomakehurst.wiremock.client.WireMock.put; +import static com.github.tomakehurst.wiremock.client.WireMock.putRequestedFor; +import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; +import static com.github.tomakehurst.wiremock.client.WireMock.urlPathMatching; +import static com.github.tomakehurst.wiremock.client.WireMock.verify; +import static org.assertj.core.api.Assertions.assertThat; + import com.amazonaws.services.lambda.runtime.ClientContext; import com.amazonaws.services.lambda.runtime.CognitoIdentity; import com.amazonaws.services.lambda.runtime.Context; @@ -7,6 +30,7 @@ import com.amazonaws.services.lambda.runtime.events.CloudFormationCustomResourceEvent; import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; import com.github.tomakehurst.wiremock.junit5.WireMockTest; +import java.util.UUID; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; @@ -14,20 +38,47 @@ import software.amazon.lambda.powertools.cloudformation.handlers.PhysicalResourceIdSetHandler; import software.amazon.lambda.powertools.cloudformation.handlers.RuntimeExceptionThrownHandler; -import java.util.UUID; - -import static com.github.tomakehurst.wiremock.client.WireMock.*; -import static org.assertj.core.api.Assertions.assertThat; - @WireMockTest public class CloudFormationIntegrationTest { public static final String PHYSICAL_RESOURCE_ID = UUID.randomUUID().toString(); public static final String LOG_STREAM_NAME = "FakeLogStreamName"; + private static CloudFormationCustomResourceEvent updateEventWithPhysicalResourceId(int httpPort, + String physicalResourceId) { + CloudFormationCustomResourceEvent.CloudFormationCustomResourceEventBuilder builder = baseEvent(httpPort); + + builder.withPhysicalResourceId(physicalResourceId); + builder.withRequestType("Update"); + + return builder.build(); + } + + private static CloudFormationCustomResourceEvent deleteEventWithPhysicalResourceId(int httpPort, + String physicalResourceId) { + CloudFormationCustomResourceEvent.CloudFormationCustomResourceEventBuilder builder = baseEvent(httpPort); + + builder.withPhysicalResourceId(physicalResourceId); + builder.withRequestType("Delete"); + + return builder.build(); + } + + private static CloudFormationCustomResourceEvent.CloudFormationCustomResourceEventBuilder baseEvent(int httpPort) { + CloudFormationCustomResourceEvent.CloudFormationCustomResourceEventBuilder builder = + CloudFormationCustomResourceEvent.builder() + .withResponseUrl("http://localhost:" + httpPort + "/") + .withStackId("123") + .withRequestId("234") + .withLogicalResourceId("345"); + + return builder; + } + @ParameterizedTest @ValueSource(strings = {"Update", "Delete"}) - void physicalResourceIdTakenFromRequestForUpdateOrDeleteWhenUserSpecifiesNull(String requestType, WireMockRuntimeInfo wmRuntimeInfo) { + void physicalResourceIdTakenFromRequestForUpdateOrDeleteWhenUserSpecifiesNull(String requestType, + WireMockRuntimeInfo wmRuntimeInfo) { stubFor(put("/").willReturn(ok())); NoPhysicalResourceIdSetHandler handler = new NoPhysicalResourceIdSetHandler(); @@ -48,7 +99,8 @@ void physicalResourceIdTakenFromRequestForUpdateOrDeleteWhenUserSpecifiesNull(St @ParameterizedTest @ValueSource(strings = {"Update", "Delete"}) - void physicalResourceIdDoesNotChangeWhenRuntimeExceptionThrownWhenUpdatingOrDeleting(String requestType, WireMockRuntimeInfo wmRuntimeInfo) { + void physicalResourceIdDoesNotChangeWhenRuntimeExceptionThrownWhenUpdatingOrDeleting(String requestType, + WireMockRuntimeInfo wmRuntimeInfo) { stubFor(put("/").willReturn(ok())); RuntimeExceptionThrownHandler handler = new RuntimeExceptionThrownHandler(); @@ -68,7 +120,7 @@ void physicalResourceIdDoesNotChangeWhenRuntimeExceptionThrownWhenUpdatingOrDele } @Test - void runtimeExceptionThrownOnCreateSendsLogStreamNameAsPhysicalResourceId(WireMockRuntimeInfo wmRuntimeInfo) { + void runtimeExceptionThrownOnCreateSendsLogStreamNameAsPhysicalResourceId(WireMockRuntimeInfo wmRuntimeInfo) { stubFor(put("/").willReturn(ok())); RuntimeExceptionThrownHandler handler = new RuntimeExceptionThrownHandler(); @@ -85,7 +137,8 @@ void runtimeExceptionThrownOnCreateSendsLogStreamNameAsPhysicalResourceId(WireMo @ParameterizedTest @ValueSource(strings = {"Update", "Delete"}) - void physicalResourceIdSetFromRequestOnUpdateOrDeleteWhenCustomerDoesntProvideAPhysicalResourceId(String requestType, WireMockRuntimeInfo wmRuntimeInfo) { + void physicalResourceIdSetFromRequestOnUpdateOrDeleteWhenCustomerDoesntProvideAPhysicalResourceId( + String requestType, WireMockRuntimeInfo wmRuntimeInfo) { stubFor(put("/").willReturn(ok())); NoPhysicalResourceIdSetHandler handler = new NoPhysicalResourceIdSetHandler(); @@ -160,34 +213,6 @@ void physicalResourceIdReturnedFromFailedToCloudformation(String requestType, Wi ); } - private static CloudFormationCustomResourceEvent updateEventWithPhysicalResourceId(int httpPort, String physicalResourceId) { - CloudFormationCustomResourceEvent.CloudFormationCustomResourceEventBuilder builder = baseEvent(httpPort); - - builder.withPhysicalResourceId(physicalResourceId); - builder.withRequestType("Update"); - - return builder.build(); - } - - private static CloudFormationCustomResourceEvent deleteEventWithPhysicalResourceId(int httpPort, String physicalResourceId) { - CloudFormationCustomResourceEvent.CloudFormationCustomResourceEventBuilder builder = baseEvent(httpPort); - - builder.withPhysicalResourceId(physicalResourceId); - builder.withRequestType("Delete"); - - return builder.build(); - } - - private static CloudFormationCustomResourceEvent.CloudFormationCustomResourceEventBuilder baseEvent(int httpPort) { - CloudFormationCustomResourceEvent.CloudFormationCustomResourceEventBuilder builder = CloudFormationCustomResourceEvent.builder() - .withResponseUrl("http://localhost:" + httpPort + "/") - .withStackId("123") - .withRequestId("234") - .withLogicalResourceId("345"); - - return builder; - } - private static class FakeContext implements Context { @Override public String getAwsRequestId() { diff --git a/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/CloudFormationResponseTest.java b/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/CloudFormationResponseTest.java index 64c313695..2e7fbcc0c 100644 --- a/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/CloudFormationResponseTest.java +++ b/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/CloudFormationResponseTest.java @@ -1,8 +1,33 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.cloudformation; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.events.CloudFormationCustomResourceEvent; import com.fasterxml.jackson.databind.node.ObjectNode; +import java.io.IOException; +import java.io.InputStream; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Optional; import org.junit.jupiter.api.Test; import software.amazon.awssdk.http.AbortableInputStream; import software.amazon.awssdk.http.ExecutableHttpRequest; @@ -13,18 +38,6 @@ import software.amazon.awssdk.utils.StringInputStream; import software.amazon.lambda.powertools.cloudformation.CloudFormationResponse.ResponseBody; -import java.io.IOException; -import java.io.InputStream; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Optional; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - public class CloudFormationResponseTest { /** @@ -44,16 +57,17 @@ static CloudFormationResponse testableCloudFormationResponse() { SdkHttpClient client = mock(SdkHttpClient.class); ExecutableHttpRequest executableRequest = mock(ExecutableHttpRequest.class); - when(client.prepareRequest(any(HttpExecuteRequest.class))).thenAnswer(args -> { - HttpExecuteRequest request = args.getArgument(0, HttpExecuteRequest.class); - assertThat(request.contentStreamProvider()).isPresent(); + when(client.prepareRequest(any(HttpExecuteRequest.class))).thenAnswer(args -> + { + HttpExecuteRequest request = args.getArgument(0, HttpExecuteRequest.class); + assertThat(request.contentStreamProvider()).isPresent(); - InputStream inputStream = request.contentStreamProvider().get().newStream(); - HttpExecuteResponse response = mock(HttpExecuteResponse.class); - when(response.responseBody()).thenReturn(Optional.of(AbortableInputStream.create(inputStream))); - when(executableRequest.call()).thenReturn(response); - return executableRequest; - }); + InputStream inputStream = request.contentStreamProvider().get().newStream(); + HttpExecuteResponse response = mock(HttpExecuteResponse.class); + when(response.responseBody()).thenReturn(Optional.of(AbortableInputStream.create(inputStream))); + when(executableRequest.call()).thenReturn(response); + return executableRequest; + }); return new CloudFormationResponse(client); } @@ -113,7 +127,8 @@ void customPhysicalResponseId() { String customPhysicalResourceId = "Custom-Physical-Resource-ID"; ResponseBody body = new ResponseBody( - event, Response.Status.SUCCESS, customPhysicalResourceId, false, "See the details in CloudWatch Log Stream: " + context.getLogStreamName()); + event, Response.Status.SUCCESS, customPhysicalResourceId, false, + "See the details in CloudWatch Log Stream: " + context.getLogStreamName()); assertThat(body.getPhysicalResourceId()).isEqualTo(customPhysicalResourceId); } @@ -122,7 +137,8 @@ void responseBodyWithNullDataNode() { CloudFormationCustomResourceEvent event = mockCloudFormationCustomResourceEvent(); Context context = mock(Context.class); - ResponseBody responseBody = new ResponseBody(event, Response.Status.FAILED, null, true, "See the details in CloudWatch Log Stream: " + context.getLogStreamName()); + ResponseBody responseBody = new ResponseBody(event, Response.Status.FAILED, null, true, + "See the details in CloudWatch Log Stream: " + context.getLogStreamName()); String actualJson = responseBody.toObjectNode(null).toString(); String expectedJson = "{" + @@ -146,7 +162,8 @@ void responseBodyWithNonNullDataNode() { dataNode.put("foo", "bar"); dataNode.put("baz", 10); - ResponseBody responseBody = new ResponseBody(event, Response.Status.FAILED, null, true, "See the details in CloudWatch Log Stream: " + context.getLogStreamName()); + ResponseBody responseBody = new ResponseBody(event, Response.Status.FAILED, null, true, + "See the details in CloudWatch Log Stream: " + context.getLogStreamName()); String actualJson = responseBody.toObjectNode(dataNode).toString(); String expectedJson = "{" + @@ -178,7 +195,8 @@ void customStatus() { Context context = mock(Context.class); ResponseBody body = new ResponseBody( - event, Response.Status.FAILED, null, false, "See the details in CloudWatch Log Stream: " + context.getLogStreamName()); + event, Response.Status.FAILED, null, false, + "See the details in CloudWatch Log Stream: " + context.getLogStreamName()); assertThat(body.getStatus()).isEqualTo("FAILED"); } @@ -191,7 +209,8 @@ void reasonIncludesLogStreamName() { when(context.getLogStreamName()).thenReturn(logStreamName); ResponseBody body = new ResponseBody( - event, Response.Status.SUCCESS, null, false, "See the details in CloudWatch Log Stream: " + context.getLogStreamName()); + event, Response.Status.SUCCESS, null, false, + "See the details in CloudWatch Log Stream: " + context.getLogStreamName()); assertThat(body.getReason()).contains(logStreamName); } diff --git a/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/ResponseTest.java b/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/ResponseTest.java index e97a1a5ba..37fe73d0f 100644 --- a/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/ResponseTest.java +++ b/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/ResponseTest.java @@ -1,29 +1,29 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.cloudformation; +import static org.assertj.core.api.Assertions.assertThat; + import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.PropertyNamingStrategies; -import org.junit.jupiter.api.Test; - import java.util.HashMap; import java.util.Map; - -import static org.assertj.core.api.Assertions.assertThat; +import org.junit.jupiter.api.Test; public class ResponseTest { - static class DummyBean { - private final Object propertyWithLongName; - - DummyBean(Object propertyWithLongName) { - this.propertyWithLongName = propertyWithLongName; - } - - @SuppressWarnings("unused") - public Object getPropertyWithLongName() { - return propertyWithLongName; - } - } - @Test void defaultValues() { Response response = Response.builder().build(); @@ -173,4 +173,17 @@ void failedFactoryMethod() { assertThat(response).isNotNull(); assertThat(response.getStatus()).isEqualTo(Response.Status.FAILED); } + + static class DummyBean { + private final Object propertyWithLongName; + + DummyBean(Object propertyWithLongName) { + this.propertyWithLongName = propertyWithLongName; + } + + @SuppressWarnings("unused") + public Object getPropertyWithLongName() { + return propertyWithLongName; + } + } } diff --git a/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/handlers/NoPhysicalResourceIdSetHandler.java b/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/handlers/NoPhysicalResourceIdSetHandler.java index 68d057b54..2bbda309f 100644 --- a/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/handlers/NoPhysicalResourceIdSetHandler.java +++ b/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/handlers/NoPhysicalResourceIdSetHandler.java @@ -1,3 +1,17 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.cloudformation.handlers; import com.amazonaws.services.lambda.runtime.Context; diff --git a/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/handlers/PhysicalResourceIdSetHandler.java b/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/handlers/PhysicalResourceIdSetHandler.java index 51f520a3d..c6bd56b76 100644 --- a/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/handlers/PhysicalResourceIdSetHandler.java +++ b/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/handlers/PhysicalResourceIdSetHandler.java @@ -1,3 +1,17 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.cloudformation.handlers; import com.amazonaws.services.lambda.runtime.Context; @@ -17,16 +31,16 @@ public PhysicalResourceIdSetHandler(String physicalResourceId, boolean callsSucc @Override protected Response create(CloudFormationCustomResourceEvent event, Context context) { - return callsSucceed? Response.success(physicalResourceId) : Response.failed(physicalResourceId); + return callsSucceed ? Response.success(physicalResourceId) : Response.failed(physicalResourceId); } @Override protected Response update(CloudFormationCustomResourceEvent event, Context context) { - return callsSucceed? Response.success(physicalResourceId) : Response.failed(physicalResourceId); + return callsSucceed ? Response.success(physicalResourceId) : Response.failed(physicalResourceId); } @Override protected Response delete(CloudFormationCustomResourceEvent event, Context context) { - return callsSucceed? Response.success(physicalResourceId) : Response.failed(physicalResourceId); + return callsSucceed ? Response.success(physicalResourceId) : Response.failed(physicalResourceId); } } diff --git a/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/handlers/RuntimeExceptionThrownHandler.java b/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/handlers/RuntimeExceptionThrownHandler.java index ee5be77b8..d5a11e895 100644 --- a/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/handlers/RuntimeExceptionThrownHandler.java +++ b/powertools-cloudformation/src/test/java/software/amazon/lambda/powertools/cloudformation/handlers/RuntimeExceptionThrownHandler.java @@ -1,3 +1,17 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.cloudformation.handlers; import com.amazonaws.services.lambda.runtime.Context; diff --git a/powertools-core/pom.xml b/powertools-core/pom.xml index cf9ad45d1..1adb64af8 100644 --- a/powertools-core/pom.xml +++ b/powertools-core/pom.xml @@ -1,4 +1,18 @@ + + @@ -89,4 +103,13 @@ + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + + + \ No newline at end of file diff --git a/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaConstants.java b/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaConstants.java index ea6a6ff44..d0f94260b 100644 --- a/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaConstants.java +++ b/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaConstants.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.core.internal; public class LambdaConstants { diff --git a/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaHandlerProcessor.java b/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaHandlerProcessor.java index c7f8b119f..e9e220e41 100644 --- a/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaHandlerProcessor.java +++ b/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/LambdaHandlerProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,20 +11,20 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.core.internal; +import static java.util.Optional.empty; +import static java.util.Optional.of; +import static software.amazon.lambda.powertools.core.internal.SystemWrapper.getenv; + import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.lambda.runtime.RequestStreamHandler; -import org.aspectj.lang.ProceedingJoinPoint; - import java.io.InputStream; import java.io.OutputStream; import java.util.Optional; - -import static java.util.Optional.empty; -import static java.util.Optional.of; -import static software.amazon.lambda.powertools.core.internal.SystemWrapper.getenv; +import org.aspectj.lang.ProceedingJoinPoint; public final class LambdaHandlerProcessor { diff --git a/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/SystemWrapper.java b/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/SystemWrapper.java index aef64378f..30f72232f 100644 --- a/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/SystemWrapper.java +++ b/powertools-core/src/main/java/software/amazon/lambda/powertools/core/internal/SystemWrapper.java @@ -1,3 +1,17 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.core.internal; public class SystemWrapper { diff --git a/powertools-core/src/test/java/software/amazon/lambda/powertools/core/internal/LambdaHandlerProcessorTest.java b/powertools-core/src/test/java/software/amazon/lambda/powertools/core/internal/LambdaHandlerProcessorTest.java index 94ad97506..dc8f49580 100644 --- a/powertools-core/src/test/java/software/amazon/lambda/powertools/core/internal/LambdaHandlerProcessorTest.java +++ b/powertools-core/src/test/java/software/amazon/lambda/powertools/core/internal/LambdaHandlerProcessorTest.java @@ -1,24 +1,37 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.core.internal; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.when; +import static software.amazon.lambda.powertools.core.internal.SystemWrapper.getenv; + import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.lambda.runtime.RequestStreamHandler; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Optional; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.Signature; import org.junit.jupiter.api.Test; import org.mockito.MockedStatic; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.Optional; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.mockStatic; -import static org.mockito.Mockito.when; -import static software.amazon.lambda.powertools.core.internal.SystemWrapper.getenv; - class LambdaHandlerProcessorTest { private Signature signature = mock(Signature.class); @@ -42,7 +55,7 @@ void isHandlerMethod_shouldRecognizeRequestStreamHandler() { @Test void isHandlerMethod_shouldReturnFalse() { - ProceedingJoinPoint pjpMock = mockRequestHandlerPjp(Object.class, new Object[]{}); + ProceedingJoinPoint pjpMock = mockRequestHandlerPjp(Object.class, new Object[] {}); boolean isHandlerMethod = LambdaHandlerProcessor.isHandlerMethod(pjpMock); @@ -210,7 +223,8 @@ void isSamLocal() { void serviceName() { try (MockedStatic mockedSystemWrapper = mockStatic(SystemWrapper.class)) { String expectedServiceName = "MyService"; - mockedSystemWrapper.when(() -> getenv(LambdaConstants.POWERTOOLS_SERVICE_NAME)).thenReturn(expectedServiceName); + mockedSystemWrapper.when(() -> getenv(LambdaConstants.POWERTOOLS_SERVICE_NAME)) + .thenReturn(expectedServiceName); String actualServiceName = LambdaHandlerProcessor.serviceName(); diff --git a/powertools-e2e-tests/handlers/idempotency/src/main/java/software/amazon/lambda/powertools/e2e/Function.java b/powertools-e2e-tests/handlers/idempotency/src/main/java/software/amazon/lambda/powertools/e2e/Function.java index cc6eec4fa..8c2b5fc58 100644 --- a/powertools-e2e-tests/handlers/idempotency/src/main/java/software/amazon/lambda/powertools/e2e/Function.java +++ b/powertools-e2e-tests/handlers/idempotency/src/main/java/software/amazon/lambda/powertools/e2e/Function.java @@ -1,7 +1,26 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.e2e; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; +import java.time.Duration; +import java.time.Instant; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; +import java.util.TimeZone; import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.dynamodb.DynamoDbClient; @@ -10,12 +29,6 @@ import software.amazon.lambda.powertools.idempotency.Idempotent; import software.amazon.lambda.powertools.idempotency.persistence.DynamoDBPersistenceStore; -import java.time.Duration; -import java.time.Instant; -import java.time.format.DateTimeFormatter; -import java.time.temporal.ChronoUnit; -import java.util.TimeZone; - public class Function implements RequestHandler { @@ -42,7 +55,7 @@ public Function(DynamoDbClient client) { @Idempotent public String handleRequest(Input input, Context context) { - DateTimeFormatter dtf = DateTimeFormatter.ISO_DATE_TIME.withZone(TimeZone.getTimeZone("UTC").toZoneId()); + DateTimeFormatter dtf = DateTimeFormatter.ISO_DATE_TIME.withZone(TimeZone.getTimeZone("UTC").toZoneId()); return dtf.format(Instant.now()); } } \ No newline at end of file diff --git a/powertools-e2e-tests/handlers/idempotency/src/main/java/software/amazon/lambda/powertools/e2e/Input.java b/powertools-e2e-tests/handlers/idempotency/src/main/java/software/amazon/lambda/powertools/e2e/Input.java index c5c2a121e..e0e4c27c9 100644 --- a/powertools-e2e-tests/handlers/idempotency/src/main/java/software/amazon/lambda/powertools/e2e/Input.java +++ b/powertools-e2e-tests/handlers/idempotency/src/main/java/software/amazon/lambda/powertools/e2e/Input.java @@ -1,3 +1,17 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.e2e; public class Input { diff --git a/powertools-e2e-tests/handlers/logging/src/main/java/software/amazon/lambda/powertools/e2e/Function.java b/powertools-e2e-tests/handlers/logging/src/main/java/software/amazon/lambda/powertools/e2e/Function.java index 5a9a87109..62ebabc6e 100644 --- a/powertools-e2e-tests/handlers/logging/src/main/java/software/amazon/lambda/powertools/e2e/Function.java +++ b/powertools-e2e-tests/handlers/logging/src/main/java/software/amazon/lambda/powertools/e2e/Function.java @@ -1,3 +1,17 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.e2e; import com.amazonaws.services.lambda.runtime.Context; diff --git a/powertools-e2e-tests/handlers/logging/src/main/java/software/amazon/lambda/powertools/e2e/Input.java b/powertools-e2e-tests/handlers/logging/src/main/java/software/amazon/lambda/powertools/e2e/Input.java index 83afbbd5a..cc449922e 100644 --- a/powertools-e2e-tests/handlers/logging/src/main/java/software/amazon/lambda/powertools/e2e/Input.java +++ b/powertools-e2e-tests/handlers/logging/src/main/java/software/amazon/lambda/powertools/e2e/Input.java @@ -1,3 +1,17 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.e2e; import java.util.Map; diff --git a/powertools-e2e-tests/handlers/metrics/src/main/java/software/amazon/lambda/powertools/e2e/Function.java b/powertools-e2e-tests/handlers/metrics/src/main/java/software/amazon/lambda/powertools/e2e/Function.java index e643de9d5..d9cf575c3 100644 --- a/powertools-e2e-tests/handlers/metrics/src/main/java/software/amazon/lambda/powertools/e2e/Function.java +++ b/powertools-e2e-tests/handlers/metrics/src/main/java/software/amazon/lambda/powertools/e2e/Function.java @@ -1,3 +1,17 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.e2e; import com.amazonaws.services.lambda.runtime.Context; diff --git a/powertools-e2e-tests/handlers/metrics/src/main/java/software/amazon/lambda/powertools/e2e/Input.java b/powertools-e2e-tests/handlers/metrics/src/main/java/software/amazon/lambda/powertools/e2e/Input.java index 5ff8a7125..18c4eb747 100644 --- a/powertools-e2e-tests/handlers/metrics/src/main/java/software/amazon/lambda/powertools/e2e/Input.java +++ b/powertools-e2e-tests/handlers/metrics/src/main/java/software/amazon/lambda/powertools/e2e/Input.java @@ -1,3 +1,17 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.e2e; import java.util.Map; @@ -7,6 +21,9 @@ public class Input { private Map dimensions; + public Input() { + } + public Map getMetrics() { return metrics; } @@ -15,10 +32,6 @@ public void setMetrics(Map metrics) { this.metrics = metrics; } - public Input() { - } - - public Map getDimensions() { return dimensions; } diff --git a/powertools-e2e-tests/handlers/parameters/src/main/java/software/amazon/lambda/powertools/e2e/Function.java b/powertools-e2e-tests/handlers/parameters/src/main/java/software/amazon/lambda/powertools/e2e/Function.java index a2d6a1ba6..3a83a1b05 100644 --- a/powertools-e2e-tests/handlers/parameters/src/main/java/software/amazon/lambda/powertools/e2e/Function.java +++ b/powertools-e2e-tests/handlers/parameters/src/main/java/software/amazon/lambda/powertools/e2e/Function.java @@ -1,3 +1,17 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.e2e; import com.amazonaws.services.lambda.runtime.Context; diff --git a/powertools-e2e-tests/handlers/parameters/src/main/java/software/amazon/lambda/powertools/e2e/Input.java b/powertools-e2e-tests/handlers/parameters/src/main/java/software/amazon/lambda/powertools/e2e/Input.java index 7ea22143f..b481d25e1 100644 --- a/powertools-e2e-tests/handlers/parameters/src/main/java/software/amazon/lambda/powertools/e2e/Input.java +++ b/powertools-e2e-tests/handlers/parameters/src/main/java/software/amazon/lambda/powertools/e2e/Input.java @@ -1,34 +1,47 @@ -package software.amazon.lambda.powertools.e2e; +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ -import java.util.Map; +package software.amazon.lambda.powertools.e2e; public class Input { private String app; private String environment; private String key; - public void setApp(String app) { - this.app = app; - } - - public void setEnvironment(String environment) { - this.environment = environment; - } - - public void setKey(String key) { - this.key = key; - } public String getApp() { return app; } + public void setApp(String app) { + this.app = app; + } + public String getEnvironment() { return environment; } + public void setEnvironment(String environment) { + this.environment = environment; + } + public String getKey() { return key; } + public void setKey(String key) { + this.key = key; + } + } diff --git a/powertools-e2e-tests/handlers/tracing/src/main/java/software/amazon/lambda/powertools/e2e/Function.java b/powertools-e2e-tests/handlers/tracing/src/main/java/software/amazon/lambda/powertools/e2e/Function.java index f7b2c5e5d..462b7c71d 100644 --- a/powertools-e2e-tests/handlers/tracing/src/main/java/software/amazon/lambda/powertools/e2e/Function.java +++ b/powertools-e2e-tests/handlers/tracing/src/main/java/software/amazon/lambda/powertools/e2e/Function.java @@ -1,3 +1,17 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.e2e; import com.amazonaws.services.lambda.runtime.Context; @@ -16,13 +30,14 @@ public String handleRequest(Input input, Context context) { } String message = buildMessage(input.getMessage(), context.getFunctionName()); - TracingUtils.withSubsegment("internal_stuff", subsegment -> { - try { - Thread.sleep(100); // simulate stuff - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - }); + TracingUtils.withSubsegment("internal_stuff", subsegment -> + { + try { + Thread.sleep(100); // simulate stuff + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + }); return message; } diff --git a/powertools-e2e-tests/handlers/tracing/src/main/java/software/amazon/lambda/powertools/e2e/Input.java b/powertools-e2e-tests/handlers/tracing/src/main/java/software/amazon/lambda/powertools/e2e/Input.java index 29cf618ba..92078d0b3 100644 --- a/powertools-e2e-tests/handlers/tracing/src/main/java/software/amazon/lambda/powertools/e2e/Input.java +++ b/powertools-e2e-tests/handlers/tracing/src/main/java/software/amazon/lambda/powertools/e2e/Input.java @@ -1,3 +1,17 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.e2e; public class Input { diff --git a/powertools-e2e-tests/pom.xml b/powertools-e2e-tests/pom.xml index 5b6b50511..3e7f0f460 100644 --- a/powertools-e2e-tests/pom.xml +++ b/powertools-e2e-tests/pom.xml @@ -1,4 +1,18 @@ + + @@ -133,6 +147,10 @@ + + org.apache.maven.plugins + maven-checkstyle-plugin + org.apache.maven.plugins maven-compiler-plugin diff --git a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/IdempotencyE2ET.java b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/IdempotencyE2ET.java index 6923c3caa..fed823299 100644 --- a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/IdempotencyE2ET.java +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/IdempotencyE2ET.java @@ -1,5 +1,25 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools; +import static software.amazon.lambda.powertools.testutils.lambda.LambdaInvoker.invokeFunction; + +import java.time.Year; +import java.util.Collections; +import java.util.UUID; +import java.util.concurrent.TimeUnit; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; @@ -8,13 +28,6 @@ import software.amazon.lambda.powertools.testutils.Infrastructure; import software.amazon.lambda.powertools.testutils.lambda.InvocationResult; -import java.time.Year; -import java.util.Collections; -import java.util.UUID; -import java.util.concurrent.TimeUnit; - -import static software.amazon.lambda.powertools.testutils.lambda.LambdaInvoker.invokeFunction; - public class IdempotencyE2ET { private static Infrastructure infrastructure; private static String functionName; @@ -34,8 +47,9 @@ public static void setup() { @AfterAll public static void tearDown() { - if (infrastructure != null) + if (infrastructure != null) { infrastructure.destroy(); + } } @Test diff --git a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/LoggingE2ET.java b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/LoggingE2ET.java index 15c5eb935..e4b8dccd2 100644 --- a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/LoggingE2ET.java +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/LoggingE2ET.java @@ -1,8 +1,30 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools; +import static org.assertj.core.api.Assertions.assertThat; +import static software.amazon.lambda.powertools.testutils.lambda.LambdaInvoker.invokeFunction; +import static software.amazon.lambda.powertools.testutils.logging.InvocationLogs.Level.INFO; + import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.UUID; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -10,15 +32,6 @@ import software.amazon.lambda.powertools.testutils.Infrastructure; import software.amazon.lambda.powertools.testutils.lambda.InvocationResult; -import java.util.UUID; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import static org.assertj.core.api.Assertions.assertThat; -import static software.amazon.lambda.powertools.testutils.lambda.LambdaInvoker.invokeFunction; -import static software.amazon.lambda.powertools.testutils.logging.InvocationLogs.Level.INFO; - public class LoggingE2ET { private static final ObjectMapper objectMapper = new ObjectMapper(); @@ -33,7 +46,7 @@ public static void setup() { .testName(LoggingE2ET.class.getSimpleName()) .pathToFunction("logging") .environmentVariables( - Stream.of(new String[][]{ + Stream.of(new String[][] { {"POWERTOOLS_LOG_LEVEL", "INFO"}, {"POWERTOOLS_SERVICE_NAME", LoggingE2ET.class.getSimpleName()} }) @@ -44,15 +57,16 @@ public static void setup() { @AfterAll public static void tearDown() { - if (infrastructure != null) + if (infrastructure != null) { infrastructure.destroy(); + } } @Test public void test_logInfoWithAdditionalKeys() throws JsonProcessingException { // GIVEN String orderId = UUID.randomUUID().toString(); - String event = "{\"message\":\"New Order\", \"keys\":{\"orderId\":\"" + orderId +"\"}}"; + String event = "{\"message\":\"New Order\", \"keys\":{\"orderId\":\"" + orderId + "\"}}"; // WHEN InvocationResult invocationResult1 = invokeFunction(functionName, event); diff --git a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/MetricsE2ET.java b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/MetricsE2ET.java index 4b8d7ea5a..32965b3be 100644 --- a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/MetricsE2ET.java +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/MetricsE2ET.java @@ -1,12 +1,21 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Timeout; -import software.amazon.lambda.powertools.testutils.Infrastructure; -import software.amazon.lambda.powertools.testutils.lambda.InvocationResult; -import software.amazon.lambda.powertools.testutils.metrics.MetricsFetcher; +import static org.assertj.core.api.Assertions.assertThat; +import static software.amazon.lambda.powertools.testutils.lambda.LambdaInvoker.invokeFunction; import java.util.Collections; import java.util.List; @@ -14,13 +23,17 @@ import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import java.util.stream.Stream; - -import static org.assertj.core.api.Assertions.assertThat; -import static software.amazon.lambda.powertools.testutils.lambda.LambdaInvoker.invokeFunction; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import software.amazon.lambda.powertools.testutils.Infrastructure; +import software.amazon.lambda.powertools.testutils.lambda.InvocationResult; +import software.amazon.lambda.powertools.testutils.metrics.MetricsFetcher; public class MetricsE2ET { - private static final String namespace = "MetricsE2ENamespace_"+UUID.randomUUID(); - private static final String service = "MetricsE2EService_"+UUID.randomUUID(); + private static final String namespace = "MetricsE2ENamespace_" + UUID.randomUUID(); + private static final String service = "MetricsE2EService_" + UUID.randomUUID(); private static Infrastructure infrastructure; private static String functionName; @@ -31,7 +44,7 @@ public static void setup() { .testName(MetricsE2ET.class.getSimpleName()) .pathToFunction("metrics") .environmentVariables( - Stream.of(new String[][]{ + Stream.of(new String[][] { {"POWERTOOLS_METRICS_NAMESPACE", namespace}, {"POWERTOOLS_SERVICE_NAME", service} }) @@ -42,32 +55,44 @@ public static void setup() { @AfterAll public static void tearDown() { - if (infrastructure != null) + if (infrastructure != null) { infrastructure.destroy(); + } } @Test - public void test_recordMetrics() { + public void test_recordMetrics() { // GIVEN - String event1 = "{ \"metrics\": {\"orders\": 1, \"products\": 4}, \"dimensions\": { \"Environment\": \"test\"} }"; + String event1 = + "{ \"metrics\": {\"orders\": 1, \"products\": 4}, \"dimensions\": { \"Environment\": \"test\"} }"; // WHEN InvocationResult invocationResult = invokeFunction(functionName, event1); // THEN MetricsFetcher metricsFetcher = new MetricsFetcher(); - List coldStart = metricsFetcher.fetchMetrics(invocationResult.getStart(), invocationResult.getEnd(), 60, namespace, "ColdStart", Stream.of(new String[][]{ - {"FunctionName", functionName}, - {"Service", service}} - ).collect(Collectors.toMap(data -> data[0], data -> data[1]))); + List coldStart = + metricsFetcher.fetchMetrics(invocationResult.getStart(), invocationResult.getEnd(), 60, namespace, + "ColdStart", Stream.of(new String[][] { + {"FunctionName", functionName}, + {"Service", service}} + ).collect(Collectors.toMap(data -> data[0], data -> data[1]))); assertThat(coldStart.get(0)).isEqualTo(1); - List orderMetrics = metricsFetcher.fetchMetrics(invocationResult.getStart(), invocationResult.getEnd(), 60, namespace, "orders", Collections.singletonMap("Environment", "test")); + List orderMetrics = + metricsFetcher.fetchMetrics(invocationResult.getStart(), invocationResult.getEnd(), 60, namespace, + "orders", Collections.singletonMap("Environment", "test")); assertThat(orderMetrics.get(0)).isEqualTo(1); - List productMetrics = metricsFetcher.fetchMetrics(invocationResult.getStart(), invocationResult.getEnd(), 60, namespace, "products", Collections.singletonMap("Environment", "test")); + List productMetrics = + metricsFetcher.fetchMetrics(invocationResult.getStart(), invocationResult.getEnd(), 60, namespace, + "products", Collections.singletonMap("Environment", "test")); assertThat(productMetrics.get(0)).isEqualTo(4); - orderMetrics = metricsFetcher.fetchMetrics(invocationResult.getStart(), invocationResult.getEnd(), 60, namespace, "orders", Collections.singletonMap("Service", service)); + orderMetrics = + metricsFetcher.fetchMetrics(invocationResult.getStart(), invocationResult.getEnd(), 60, namespace, + "orders", Collections.singletonMap("Service", service)); assertThat(orderMetrics.get(0)).isEqualTo(1); - productMetrics = metricsFetcher.fetchMetrics(invocationResult.getStart(), invocationResult.getEnd(), 60, namespace, "products", Collections.singletonMap("Service", service)); + productMetrics = + metricsFetcher.fetchMetrics(invocationResult.getStart(), invocationResult.getEnd(), 60, namespace, + "products", Collections.singletonMap("Service", service)); assertThat(productMetrics.get(0)).isEqualTo(4); } } diff --git a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/ParametersE2ET.java b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/ParametersE2ET.java index dbebe721f..678321a99 100644 --- a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/ParametersE2ET.java +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/ParametersE2ET.java @@ -1,9 +1,21 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools; -import org.junit.jupiter.api.*; -import software.amazon.lambda.powertools.testutils.AppConfig; -import software.amazon.lambda.powertools.testutils.Infrastructure; -import software.amazon.lambda.powertools.testutils.lambda.InvocationResult; +import static org.assertj.core.api.Assertions.assertThat; +import static software.amazon.lambda.powertools.testutils.lambda.LambdaInvoker.invokeFunction; import java.util.HashMap; import java.util.Map; @@ -11,23 +23,29 @@ import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import java.util.stream.Stream; - -import static org.assertj.core.api.Assertions.assertThat; -import static software.amazon.lambda.powertools.testutils.lambda.LambdaInvoker.invokeFunction; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.Timeout; +import software.amazon.lambda.powertools.testutils.AppConfig; +import software.amazon.lambda.powertools.testutils.Infrastructure; +import software.amazon.lambda.powertools.testutils.lambda.InvocationResult; @TestInstance(TestInstance.Lifecycle.PER_CLASS) public class ParametersE2ET { + private final AppConfig appConfig; private Infrastructure infrastructure; private String functionName; - private final AppConfig appConfig; public ParametersE2ET() { String appName = UUID.randomUUID().toString(); - Map params = new HashMap<>(); + Map params = new HashMap<>(); params.put("key1", "value1"); params.put("key2", "value2"); appConfig = new AppConfig(appName, "e2etest", params); } + @BeforeAll @Timeout(value = 5, unit = TimeUnit.MINUTES) public void setup() { @@ -36,7 +54,7 @@ public void setup() { .pathToFunction("parameters") .appConfig(appConfig) .environmentVariables( - Stream.of(new String[][]{ + Stream.of(new String[][] { {"POWERTOOLS_LOG_LEVEL", "INFO"}, {"POWERTOOLS_SERVICE_NAME", ParametersE2ET.class.getSimpleName()} }) @@ -47,13 +65,14 @@ public void setup() { @AfterAll public void tearDown() { - if (infrastructure != null) + if (infrastructure != null) { infrastructure.destroy(); + } } @Test public void test_getAppConfigValue() { - for (Map.EntryconfigKey: appConfig.getConfigurationValues().entrySet()) { + for (Map.Entry configKey : appConfig.getConfigurationValues().entrySet()) { // Arrange String event1 = "{" + diff --git a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/TracingE2ET.java b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/TracingE2ET.java index 1f002fe60..cd4a21df4 100644 --- a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/TracingE2ET.java +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/TracingE2ET.java @@ -1,5 +1,26 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools; +import static org.assertj.core.api.Assertions.assertThat; +import static software.amazon.lambda.powertools.testutils.lambda.LambdaInvoker.invokeFunction; + +import java.util.Collections; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -10,16 +31,9 @@ import software.amazon.lambda.powertools.testutils.tracing.Trace; import software.amazon.lambda.powertools.testutils.tracing.TraceFetcher; -import java.util.Collections; -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.TimeUnit; - -import static org.assertj.core.api.Assertions.assertThat; -import static software.amazon.lambda.powertools.testutils.lambda.LambdaInvoker.invokeFunction; - public class TracingE2ET { - private static final String service = "TracingE2EService_" + UUID.randomUUID(); // "TracingE2EService_e479fb27-422b-4107-9f8c-086c62e1cd12"; + private static final String service = "TracingE2EService_" + UUID.randomUUID(); + // "TracingE2EService_e479fb27-422b-4107-9f8c-086c62e1cd12"; private static Infrastructure infrastructure; private static String functionName; @@ -38,8 +52,9 @@ public static void setup() { @AfterAll public static void tearDown() { - if (infrastructure != null) + if (infrastructure != null) { infrastructure.destroy(); + } } @Test @@ -77,7 +92,8 @@ public void test_tracing() { sub = handleRequest.getSubsegments().get(1); assertThat(sub.getName()).isIn("## internal_stuff", "## buildMessage"); - SubSegment buildMessage = handleRequest.getSubsegments().stream().filter(subSegment -> subSegment.getName().equals("## buildMessage")).findFirst().orElse(null); + SubSegment buildMessage = handleRequest.getSubsegments().stream() + .filter(subSegment -> subSegment.getName().equals("## buildMessage")).findFirst().orElse(null); assertThat(buildMessage).isNotNull(); assertThat(buildMessage.getAnnotations()).hasSize(1); assertThat(buildMessage.getAnnotations().get("message")).isEqualTo(message); diff --git a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/AppConfig.java b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/AppConfig.java index c87f4ac48..4229af040 100644 --- a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/AppConfig.java +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/AppConfig.java @@ -1,12 +1,25 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.testutils; -import java.util.HashMap; import java.util.Map; /** * Defines configuration used to setup an AppConfig * deployment when the infrastructure is rolled out. - * + *

* All fields are non-nullable. */ public class AppConfig { diff --git a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/Infrastructure.java b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/Infrastructure.java index 59035af7c..62bb018f4 100644 --- a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/Infrastructure.java +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/Infrastructure.java @@ -1,23 +1,56 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.testutils; +import static java.util.Collections.singletonList; + import com.fasterxml.jackson.databind.JsonNode; +import java.io.File; +import java.io.IOException; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.UUID; +import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.yaml.snakeyaml.Yaml; +import software.amazon.awscdk.App; +import software.amazon.awscdk.BundlingOptions; +import software.amazon.awscdk.BundlingOutput; +import software.amazon.awscdk.DockerVolume; +import software.amazon.awscdk.Duration; +import software.amazon.awscdk.RemovalPolicy; import software.amazon.awscdk.Stack; -import software.amazon.awscdk.*; import software.amazon.awscdk.cxapi.CloudAssembly; -import software.amazon.awscdk.services.appconfig.*; +import software.amazon.awscdk.services.appconfig.CfnApplication; +import software.amazon.awscdk.services.appconfig.CfnConfigurationProfile; +import software.amazon.awscdk.services.appconfig.CfnDeployment; +import software.amazon.awscdk.services.appconfig.CfnDeploymentStrategy; +import software.amazon.awscdk.services.appconfig.CfnEnvironment; +import software.amazon.awscdk.services.appconfig.CfnHostedConfigurationVersion; import software.amazon.awscdk.services.dynamodb.Attribute; import software.amazon.awscdk.services.dynamodb.AttributeType; import software.amazon.awscdk.services.dynamodb.BillingMode; import software.amazon.awscdk.services.dynamodb.Table; -import software.amazon.awscdk.services.groundstation.CfnConfig; import software.amazon.awscdk.services.iam.PolicyStatement; -import software.amazon.awscdk.services.iam.ServicePrincipal; import software.amazon.awscdk.services.lambda.Code; import software.amazon.awscdk.services.lambda.Function; -import software.amazon.awscdk.services.lambda.Permission; import software.amazon.awscdk.services.lambda.Tracing; import software.amazon.awscdk.services.logs.LogGroup; import software.amazon.awscdk.services.logs.RetentionDays; @@ -27,7 +60,12 @@ import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.cloudformation.CloudFormationClient; -import software.amazon.awssdk.services.cloudformation.model.*; +import software.amazon.awssdk.services.cloudformation.model.Capability; +import software.amazon.awssdk.services.cloudformation.model.CreateStackRequest; +import software.amazon.awssdk.services.cloudformation.model.DeleteStackRequest; +import software.amazon.awssdk.services.cloudformation.model.DescribeStacksRequest; +import software.amazon.awssdk.services.cloudformation.model.DescribeStacksResponse; +import software.amazon.awssdk.services.cloudformation.model.OnFailure; import software.amazon.awssdk.services.s3.S3Client; import software.amazon.awssdk.services.s3.model.ListObjectsV2Request; import software.amazon.awssdk.services.s3.model.ListObjectsV2Response; @@ -36,14 +74,6 @@ import software.amazon.awssdk.utils.StringUtils; import software.amazon.lambda.powertools.utilities.JsonConfig; -import java.io.File; -import java.io.IOException; -import java.nio.file.Paths; -import java.util.*; -import java.util.stream.Collectors; - -import static java.util.Collections.singletonList; - /** * This class is in charge of bootstrapping the infrastructure for the tests. *
@@ -71,12 +101,10 @@ public class Infrastructure { private final String account; private final String idempotencyTable; private final AppConfig appConfig; - - + private final SdkHttpClient httpClient; private String functionName; private Object cfnTemplate; private String cfnAssetDirectory; - private final SdkHttpClient httpClient; private Infrastructure(Builder builder) { this.stackName = builder.stackName; @@ -110,8 +138,13 @@ private Infrastructure(Builder builder) { .build(); } + public static Builder builder() { + return new Builder(); + } + /** * Use the CloudFormation SDK to create the stack + * * @return the name of the function deployed part of the stack */ public String deploy() { @@ -124,9 +157,11 @@ public String deploy() { .onFailure(OnFailure.ROLLBACK) .capabilities(Capability.CAPABILITY_IAM) .build()); - WaiterResponse waiterResponse = cfn.waiter().waitUntilStackCreateComplete(DescribeStacksRequest.builder().stackName(stackName).build()); + WaiterResponse waiterResponse = + cfn.waiter().waitUntilStackCreateComplete(DescribeStacksRequest.builder().stackName(stackName).build()); if (waiterResponse.matched().response().isPresent()) { - LOG.info("Stack " + waiterResponse.matched().response().get().stacks().get(0).stackName() + " successfully deployed"); + LOG.info("Stack " + waiterResponse.matched().response().get().stacks().get(0).stackName() + + " successfully deployed"); } else { throw new RuntimeException("Failed to create stack"); } @@ -141,93 +176,9 @@ public void destroy() { cfn.deleteStack(DeleteStackRequest.builder().stackName(stackName).build()); } - public static Builder builder() { - return new Builder(); - } - - public static class Builder { - public long timeoutInSeconds = 30; - public String pathToFunction; - public String testName; - public AppConfig appConfig; - private String stackName; - private boolean tracing = false; - private JavaRuntime runtime; - private Map environmentVariables = new HashMap<>(); - private String idemPotencyTable; - - private Builder() { - getJavaRuntime(); - } - - /** - * Retrieve the java runtime to use for the lambda function. - */ - private void getJavaRuntime() { - String javaVersion = System.getenv("JAVA_VERSION"); // must be set in GitHub actions - if (javaVersion == null) { - throw new IllegalArgumentException("JAVA_VERSION is not set"); - } - if (javaVersion.startsWith("8")) { - runtime = JavaRuntime.JAVA8AL2; - } else if (javaVersion.startsWith("11")) { - runtime = JavaRuntime.JAVA11; - } else if (javaVersion.startsWith("17")) { - runtime = JavaRuntime.JAVA17; - } else { - throw new IllegalArgumentException("Unsupported Java version " + javaVersion); - } - LOG.debug("Java Version set to {}, using runtime {}", javaVersion, runtime.getRuntime()); - } - - public Infrastructure build() { - Objects.requireNonNull(testName, "testName must not be null"); - - String uuid = UUID.randomUUID().toString().replace("-", "").substring(0, 12); - stackName = testName + "-" + uuid; - - Objects.requireNonNull(pathToFunction, "pathToFunction must not be null"); - return new Infrastructure(this); - } - - public Builder testName(String testName) { - this.testName = testName; - return this; - } - - public Builder pathToFunction(String pathToFunction) { - this.pathToFunction = pathToFunction; - return this; - } - - public Builder tracing(boolean tracing) { - this.tracing = tracing; - return this; - } - - public Builder idempotencyTable(String tableName) { - this.idemPotencyTable = tableName; - return this; - } - - public Builder appConfig(AppConfig app) { - this.appConfig = app; - return this; - } - - public Builder environmentVariables(Map environmentVariables) { - this.environmentVariables = environmentVariables; - return this; - } - - public Builder timeoutInSeconds(long timeoutInSeconds) { - this.timeoutInSeconds = timeoutInSeconds; - return this; - } - } - /** * Build the CDK Stack containing the required resources (Lambda function, LogGroup, DDB Table) + * * @return the CDK stack */ private Stack createStackWithLambda() { @@ -259,7 +210,8 @@ private Stack createStackWithLambda() { functionName = stackName + "-function"; - LOG.debug("Building Lambda function with command "+ packagingInstruction.stream().collect(Collectors.joining(" ", "[", "]"))); + LOG.debug("Building Lambda function with command " + + packagingInstruction.stream().collect(Collectors.joining(" ", "[", "]"))); Function function = Function.Builder .create(stack, functionName) .code(Code.fromAsset("handlers/", AssetOptions.builder() @@ -297,10 +249,10 @@ private Stack createStackWithLambda() { } if (appConfig != null) { - CfnApplication app = CfnApplication.Builder - .create(stack, "AppConfigApp") - .name(appConfig.getApplication()) - .build(); + CfnApplication app = CfnApplication.Builder + .create(stack, "AppConfigApp") + .name(appConfig.getApplication()) + .build(); CfnEnvironment environment = CfnEnvironment.Builder .create(stack, "AppConfigEnvironment") @@ -327,7 +279,7 @@ private Stack createStackWithLambda() { ); CfnDeployment previousDeployment = null; - for (Map.Entry entry : appConfig.getConfigurationValues().entrySet()) { + for (Map.Entry entry : appConfig.getConfigurationValues().entrySet()) { CfnConfigurationProfile configProfile = CfnConfigurationProfile.Builder .create(stack, "AppConfigProfileFor" + entry.getKey()) .applicationId(app.getRef()) @@ -376,43 +328,133 @@ private void synthesize() { */ private void uploadAssets() { Map assets = findAssets(); - assets.forEach((objectKey, asset) -> { - if (!asset.assetPath.endsWith(".jar")) { - return; - } - ListObjectsV2Response objects = s3.listObjectsV2(ListObjectsV2Request.builder().bucket(asset.bucketName).build()); - if (objects.contents().stream().anyMatch(o -> o.key().equals(objectKey))) { - LOG.debug("Asset already exists, skipping"); - return; - } - LOG.info("Uploading asset " + objectKey + " to " + asset.bucketName); - s3.putObject(PutObjectRequest.builder().bucket(asset.bucketName).key(objectKey).build(), Paths.get(cfnAssetDirectory, asset.assetPath)); - }); + assets.forEach((objectKey, asset) -> + { + if (!asset.assetPath.endsWith(".jar")) { + return; + } + ListObjectsV2Response objects = + s3.listObjectsV2(ListObjectsV2Request.builder().bucket(asset.bucketName).build()); + if (objects.contents().stream().anyMatch(o -> o.key().equals(objectKey))) { + LOG.debug("Asset already exists, skipping"); + return; + } + LOG.info("Uploading asset " + objectKey + " to " + asset.bucketName); + s3.putObject(PutObjectRequest.builder().bucket(asset.bucketName).key(objectKey).build(), + Paths.get(cfnAssetDirectory, asset.assetPath)); + }); } /** * Reading the cdk assets.json file to retrieve the list of assets to push to S3 + * * @return a map of assets */ private Map findAssets() { Map assets = new HashMap<>(); try { - JsonNode jsonNode = JsonConfig.get().getObjectMapper().readTree(new File(cfnAssetDirectory, stackName + ".assets.json")); + JsonNode jsonNode = JsonConfig.get().getObjectMapper() + .readTree(new File(cfnAssetDirectory, stackName + ".assets.json")); JsonNode files = jsonNode.get("files"); - files.iterator().forEachRemaining(file -> { - String assetPath = file.get("source").get("path").asText(); - String assetPackaging = file.get("source").get("packaging").asText(); - String bucketName = file.get("destinations").get("current_account-current_region").get("bucketName").asText(); - String objectKey = file.get("destinations").get("current_account-current_region").get("objectKey").asText(); - Asset asset = new Asset(assetPath, assetPackaging, bucketName.replace("${AWS::AccountId}", account).replace("${AWS::Region}", region.toString())); - assets.put(objectKey, asset); - }); + files.iterator().forEachRemaining(file -> + { + String assetPath = file.get("source").get("path").asText(); + String assetPackaging = file.get("source").get("packaging").asText(); + String bucketName = + file.get("destinations").get("current_account-current_region").get("bucketName").asText(); + String objectKey = + file.get("destinations").get("current_account-current_region").get("objectKey").asText(); + Asset asset = new Asset(assetPath, assetPackaging, bucketName.replace("${AWS::AccountId}", account) + .replace("${AWS::Region}", region.toString())); + assets.put(objectKey, asset); + }); } catch (IOException e) { throw new RuntimeException(e); } return assets; } + public static class Builder { + public long timeoutInSeconds = 30; + public String pathToFunction; + public String testName; + public AppConfig appConfig; + private String stackName; + private boolean tracing = false; + private JavaRuntime runtime; + private Map environmentVariables = new HashMap<>(); + private String idemPotencyTable; + + private Builder() { + getJavaRuntime(); + } + + /** + * Retrieve the java runtime to use for the lambda function. + */ + private void getJavaRuntime() { + String javaVersion = System.getenv("JAVA_VERSION"); // must be set in GitHub actions + if (javaVersion == null) { + throw new IllegalArgumentException("JAVA_VERSION is not set"); + } + if (javaVersion.startsWith("8")) { + runtime = JavaRuntime.JAVA8AL2; + } else if (javaVersion.startsWith("11")) { + runtime = JavaRuntime.JAVA11; + } else if (javaVersion.startsWith("17")) { + runtime = JavaRuntime.JAVA17; + } else { + throw new IllegalArgumentException("Unsupported Java version " + javaVersion); + } + LOG.debug("Java Version set to {}, using runtime {}", javaVersion, runtime.getRuntime()); + } + + public Infrastructure build() { + Objects.requireNonNull(testName, "testName must not be null"); + + String uuid = UUID.randomUUID().toString().replace("-", "").substring(0, 12); + stackName = testName + "-" + uuid; + + Objects.requireNonNull(pathToFunction, "pathToFunction must not be null"); + return new Infrastructure(this); + } + + public Builder testName(String testName) { + this.testName = testName; + return this; + } + + public Builder pathToFunction(String pathToFunction) { + this.pathToFunction = pathToFunction; + return this; + } + + public Builder tracing(boolean tracing) { + this.tracing = tracing; + return this; + } + + public Builder idempotencyTable(String tableName) { + this.idemPotencyTable = tableName; + return this; + } + + public Builder appConfig(AppConfig app) { + this.appConfig = app; + return this; + } + + public Builder environmentVariables(Map environmentVariables) { + this.environmentVariables = environmentVariables; + return this; + } + + public Builder timeoutInSeconds(long timeoutInSeconds) { + this.timeoutInSeconds = timeoutInSeconds; + return this; + } + } + private static class Asset { private final String assetPath; private final String assetPackaging; diff --git a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/JavaRuntime.java b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/JavaRuntime.java index dce97538f..c50fcab84 100644 --- a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/JavaRuntime.java +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/JavaRuntime.java @@ -1,3 +1,17 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.testutils; import software.amazon.awscdk.services.lambda.Runtime; diff --git a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/lambda/InvocationResult.java b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/lambda/InvocationResult.java index 168fec71b..b91840b8e 100644 --- a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/lambda/InvocationResult.java +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/lambda/InvocationResult.java @@ -1,10 +1,23 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.testutils.lambda; +import java.time.Instant; import software.amazon.awssdk.services.lambda.model.InvokeResponse; import software.amazon.lambda.powertools.testutils.logging.InvocationLogs; -import java.time.Instant; - public class InvocationResult { private final InvocationLogs logs; @@ -21,6 +34,7 @@ public InvocationResult(InvokeResponse response, Instant start, Instant end) { this.start = start; this.end = end; } + public InvocationLogs getLogs() { return logs; } diff --git a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/lambda/LambdaInvoker.java b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/lambda/LambdaInvoker.java index ecde1042e..cf45076bf 100644 --- a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/lambda/LambdaInvoker.java +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/lambda/LambdaInvoker.java @@ -1,5 +1,23 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.testutils.lambda; +import static java.time.temporal.ChronoUnit.MINUTES; + +import java.time.Clock; +import java.time.Instant; import software.amazon.awssdk.core.SdkBytes; import software.amazon.awssdk.http.SdkHttpClient; import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; @@ -9,18 +27,13 @@ import software.amazon.awssdk.services.lambda.model.InvokeResponse; import software.amazon.awssdk.services.lambda.model.LogType; -import java.time.Clock; -import java.time.Instant; - -import static java.time.temporal.ChronoUnit.MINUTES; - public class LambdaInvoker { private static final SdkHttpClient httpClient = UrlConnectionHttpClient.builder().build(); private static final Region region = Region.of(System.getProperty("AWS_DEFAULT_REGION", "eu-west-1")); private static final LambdaClient lambda = LambdaClient.builder() .httpClient(httpClient) - .region(region) - .build(); + .region(region) + .build(); public static InvocationResult invokeFunction(String functionName, String input) { SdkBytes payload = SdkBytes.fromUtf8String(input); diff --git a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/logging/InvocationLogs.java b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/logging/InvocationLogs.java index 1ae1cfad7..cd63d308a 100644 --- a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/logging/InvocationLogs.java +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/logging/InvocationLogs.java @@ -1,3 +1,17 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.testutils.logging; import java.nio.charset.StandardCharsets; @@ -35,6 +49,7 @@ public String[] getAllLogs() { /** * Return only logs from function, exclude START, END, and REPORT and other elements generated by Lambda service + * * @return only logs generated by the function */ public String[] getFunctionLogs() { @@ -44,7 +59,8 @@ public String[] getFunctionLogs() { public String[] getFunctionLogs(Level level) { String[] filtered = getFunctionLogs(); - return Arrays.stream(filtered).filter(log -> log.contains("\"level\":\""+level.getLevel()+"\"")).toArray(String[]::new); + return Arrays.stream(filtered).filter(log -> log.contains("\"level\":\"" + level.getLevel() + "\"")) + .toArray(String[]::new); } public enum Level { diff --git a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/metrics/MetricsFetcher.java b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/metrics/MetricsFetcher.java index eb3cd63a4..349a9acc1 100644 --- a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/metrics/MetricsFetcher.java +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/metrics/MetricsFetcher.java @@ -1,25 +1,44 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.testutils.metrics; +import static java.time.Duration.ofSeconds; + import com.evanlennick.retry4j.CallExecutor; import com.evanlennick.retry4j.CallExecutorBuilder; import com.evanlennick.retry4j.Status; import com.evanlennick.retry4j.config.RetryConfig; import com.evanlennick.retry4j.config.RetryConfigBuilder; +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Callable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import software.amazon.awssdk.http.SdkHttpClient; import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.cloudwatch.CloudWatchClient; -import software.amazon.awssdk.services.cloudwatch.model.*; - -import java.time.Instant; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.concurrent.Callable; - -import static java.time.Duration.ofSeconds; +import software.amazon.awssdk.services.cloudwatch.model.Dimension; +import software.amazon.awssdk.services.cloudwatch.model.GetMetricDataRequest; +import software.amazon.awssdk.services.cloudwatch.model.GetMetricDataResponse; +import software.amazon.awssdk.services.cloudwatch.model.Metric; +import software.amazon.awssdk.services.cloudwatch.model.MetricDataQuery; +import software.amazon.awssdk.services.cloudwatch.model.MetricStat; +import software.amazon.awssdk.services.cloudwatch.model.StandardUnit; /** * Class in charge of retrieving the actual metrics of a Lambda execution on CloudWatch @@ -37,6 +56,7 @@ public class MetricsFetcher { /** * Retrieve the metric values from start to end. Different parameters are required (see {@link CloudWatchClient#getMetricData} for more info). * Use a retry mechanism as metrics may not be available instantaneously after a function runs. + * * @param start * @param end * @param period @@ -45,37 +65,41 @@ public class MetricsFetcher { * @param dimensions * @return */ - public List fetchMetrics(Instant start, Instant end, int period, String namespace, String metricName, Map dimensions) { + public List fetchMetrics(Instant start, Instant end, int period, String namespace, String metricName, + Map dimensions) { List dimensionsList = new ArrayList<>(); - if (dimensions != null) + if (dimensions != null) { dimensions.forEach((key, value) -> dimensionsList.add(Dimension.builder().name(key).value(value).build())); + } - Callable> callable = () -> { - LOG.debug("Get Metrics for namespace {}, start {}, end {}, metric {}, dimensions {}", namespace, start, end, metricName, dimensionsList); - GetMetricDataResponse metricData = cloudwatch.getMetricData(GetMetricDataRequest.builder() - .startTime(start) - .endTime(end) - .metricDataQueries(MetricDataQuery.builder() - .id(metricName.toLowerCase()) - .metricStat(MetricStat.builder() - .unit(StandardUnit.COUNT) - .metric(Metric.builder() - .namespace(namespace) - .metricName(metricName) - .dimensions(dimensionsList) - .build()) - .period(period) - .stat("Sum") - .build()) - .returnData(true) - .build()) - .build()); - List values = metricData.metricDataResults().get(0).values(); - if (values == null || values.isEmpty()) { - throw new Exception("No data found for metric " + metricName); - } - return values; - }; + Callable> callable = () -> + { + LOG.debug("Get Metrics for namespace {}, start {}, end {}, metric {}, dimensions {}", namespace, start, + end, metricName, dimensionsList); + GetMetricDataResponse metricData = cloudwatch.getMetricData(GetMetricDataRequest.builder() + .startTime(start) + .endTime(end) + .metricDataQueries(MetricDataQuery.builder() + .id(metricName.toLowerCase()) + .metricStat(MetricStat.builder() + .unit(StandardUnit.COUNT) + .metric(Metric.builder() + .namespace(namespace) + .metricName(metricName) + .dimensions(dimensionsList) + .build()) + .period(period) + .stat("Sum") + .build()) + .returnData(true) + .build()) + .build()); + List values = metricData.metricDataResults().get(0).values(); + if (values == null || values.isEmpty()) { + throw new Exception("No data found for metric " + metricName); + } + return values; + }; RetryConfig retryConfig = new RetryConfigBuilder() .withMaxNumberOfTries(10) @@ -85,9 +109,10 @@ public List fetchMetrics(Instant start, Instant end, int period, String .build(); CallExecutor> callExecutor = new CallExecutorBuilder>() .config(retryConfig) - .afterFailedTryListener(s -> { - LOG.warn(s.getLastExceptionThatCausedRetry().getMessage() + ", attempts: " + s.getTotalTries()); - }) + .afterFailedTryListener(s -> + { + LOG.warn(s.getLastExceptionThatCausedRetry().getMessage() + ", attempts: " + s.getTotalTries()); + }) .build(); Status> status = callExecutor.execute(callable); return status.getResult(); diff --git a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/tracing/SegmentDocument.java b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/tracing/SegmentDocument.java index 08f4bf7d8..5654b9876 100644 --- a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/tracing/SegmentDocument.java +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/tracing/SegmentDocument.java @@ -1,7 +1,20 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.testutils.tracing; import com.fasterxml.jackson.annotation.JsonSetter; - import java.time.Duration; import java.util.ArrayList; import java.util.List; @@ -92,7 +105,7 @@ public boolean hasSubsegments() { return !subsegments.isEmpty(); } - public static class SubSegment{ + public static class SubSegment { private String id; private String name; diff --git a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/tracing/Trace.java b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/tracing/Trace.java index 15026a9d1..7298957aa 100644 --- a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/tracing/Trace.java +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/tracing/Trace.java @@ -1,9 +1,22 @@ -package software.amazon.lambda.powertools.testutils.tracing; +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ -import software.amazon.lambda.powertools.testutils.tracing.SegmentDocument.SubSegment; +package software.amazon.lambda.powertools.testutils.tracing; import java.util.ArrayList; import java.util.List; +import software.amazon.lambda.powertools.testutils.tracing.SegmentDocument.SubSegment; public class Trace { private final List subsegments = new ArrayList<>(); diff --git a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/tracing/TraceFetcher.java b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/tracing/TraceFetcher.java index e7cd13640..dc63987fd 100644 --- a/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/tracing/TraceFetcher.java +++ b/powertools-e2e-tests/src/test/java/software/amazon/lambda/powertools/testutils/tracing/TraceFetcher.java @@ -1,5 +1,21 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.testutils.tracing; +import static java.time.Duration.ofSeconds; + import com.evanlennick.retry4j.CallExecutor; import com.evanlennick.retry4j.CallExecutorBuilder; import com.evanlennick.retry4j.Status; @@ -8,15 +24,6 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import software.amazon.awssdk.http.SdkHttpClient; -import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; -import software.amazon.awssdk.regions.Region; -import software.amazon.awssdk.services.xray.XRayClient; -import software.amazon.awssdk.services.xray.model.*; -import software.amazon.lambda.powertools.testutils.tracing.SegmentDocument.SubSegment; - import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.Arrays; @@ -24,25 +31,42 @@ import java.util.List; import java.util.concurrent.Callable; import java.util.stream.Collectors; - -import static java.time.Duration.ofSeconds; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import software.amazon.awssdk.http.SdkHttpClient; +import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.xray.XRayClient; +import software.amazon.awssdk.services.xray.model.BatchGetTracesRequest; +import software.amazon.awssdk.services.xray.model.BatchGetTracesResponse; +import software.amazon.awssdk.services.xray.model.GetTraceSummariesRequest; +import software.amazon.awssdk.services.xray.model.GetTraceSummariesResponse; +import software.amazon.awssdk.services.xray.model.TimeRangeType; +import software.amazon.awssdk.services.xray.model.TraceSummary; +import software.amazon.lambda.powertools.testutils.tracing.SegmentDocument.SubSegment; /** * Class in charge of retrieving the actual traces of a Lambda execution on X-Ray */ public class TraceFetcher { - private static final ObjectMapper MAPPER = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + private static final ObjectMapper MAPPER = + new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); private static final Logger LOG = LoggerFactory.getLogger(TraceFetcher.class); - + private static final SdkHttpClient httpClient = UrlConnectionHttpClient.builder().build(); + private static final Region region = Region.of(System.getProperty("AWS_DEFAULT_REGION", "eu-west-1")); + private static final XRayClient xray = XRayClient.builder() + .httpClient(httpClient) + .region(region) + .build(); private final Instant start; private final Instant end; private final String filterExpression; private final List excludedSegments; /** - * @param start beginning of the time slot to search in - * @param end end of the time slot to search in + * @param start beginning of the time slot to search in + * @param end end of the time slot to search in * @param filterExpression eventual filter for the search * @param excludedSegments list of segment to exclude from the search */ @@ -64,10 +88,11 @@ public static Builder builder() { * @return traces */ public Trace fetchTrace() { - Callable callable = () -> { - List traceIds = getTraceIds(); - return getTrace(traceIds); - }; + Callable callable = () -> + { + List traceIds = getTraceIds(); + return getTrace(traceIds); + }; RetryConfig retryConfig = new RetryConfigBuilder() .withMaxNumberOfTries(10) @@ -77,7 +102,10 @@ public Trace fetchTrace() { .build(); CallExecutor callExecutor = new CallExecutorBuilder() .config(retryConfig) - .afterFailedTryListener(s -> {LOG.warn(s.getLastExceptionThatCausedRetry().getMessage() + ", attempts: " + s.getTotalTries());}) + .afterFailedTryListener(s -> + { + LOG.warn(s.getLastExceptionThatCausedRetry().getMessage() + ", attempts: " + s.getTotalTries()); + }) .build(); Status status = callExecutor.execute(callable); return status.getResult(); @@ -85,6 +113,7 @@ public Trace fetchTrace() { /** * Retrieve traces from trace ids. + * * @param traceIds * @return */ @@ -96,43 +125,49 @@ private Trace getTrace(List traceIds) { throw new RuntimeException("No trace found"); } Trace traceRes = new Trace(); - tracesResponse.traces().forEach(trace -> { - if (trace.hasSegments()) { - trace.segments().forEach(segment -> { - try { - SegmentDocument document = MAPPER.readValue(segment.document(), SegmentDocument.class); - if (document.getOrigin().equals("AWS::Lambda::Function")) { - if (document.hasSubsegments()) { - getNestedSubSegments(document.getSubsegments(), traceRes, Collections.emptyList()); + tracesResponse.traces().forEach(trace -> + { + if (trace.hasSegments()) { + trace.segments().forEach(segment -> + { + try { + SegmentDocument document = MAPPER.readValue(segment.document(), SegmentDocument.class); + if (document.getOrigin().equals("AWS::Lambda::Function")) { + if (document.hasSubsegments()) { + getNestedSubSegments(document.getSubsegments(), traceRes, + Collections.emptyList()); + } + } + } catch (JsonProcessingException e) { + LOG.error("Failed to parse segment document: " + e.getMessage()); + throw new RuntimeException(e); } - } - } catch (JsonProcessingException e) { - LOG.error("Failed to parse segment document: " + e.getMessage()); - throw new RuntimeException(e); - } - }); - } - }); + }); + } + }); return traceRes; } private void getNestedSubSegments(List subsegments, Trace traceRes, List idsToIgnore) { - subsegments.forEach(subsegment -> { - List subSegmentIdsToIgnore = Collections.emptyList(); - if (!excludedSegments.contains(subsegment.getName()) && !idsToIgnore.contains(subsegment.getId())) { - traceRes.addSubSegment(subsegment); + subsegments.forEach(subsegment -> + { + List subSegmentIdsToIgnore = Collections.emptyList(); + if (!excludedSegments.contains(subsegment.getName()) && !idsToIgnore.contains(subsegment.getId())) { + traceRes.addSubSegment(subsegment); + if (subsegment.hasSubsegments()) { + subSegmentIdsToIgnore = subsegment.getSubsegments().stream().map(SubSegment::getId) + .collect(Collectors.toList()); + } + } if (subsegment.hasSubsegments()) { - subSegmentIdsToIgnore = subsegment.getSubsegments().stream().map(SubSegment::getId).collect(Collectors.toList()); + getNestedSubSegments(subsegment.getSubsegments(), traceRes, subSegmentIdsToIgnore); } - } - if (subsegment.hasSubsegments()) { - getNestedSubSegments(subsegment.getSubsegments(), traceRes, subSegmentIdsToIgnore); - } - }); + }); } /** * Use the X-Ray SDK to retrieve the trace ids corresponding to a specific function during a specific time slot + * * @return a list of trace ids */ private List getTraceIds() { @@ -146,20 +181,14 @@ private List getTraceIds() { if (!traceSummaries.hasTraceSummaries()) { throw new RuntimeException("No trace id found"); } - List traceIds = traceSummaries.traceSummaries().stream().map(TraceSummary::id).collect(Collectors.toList()); + List traceIds = + traceSummaries.traceSummaries().stream().map(TraceSummary::id).collect(Collectors.toList()); if (traceIds.isEmpty()) { throw new RuntimeException("No trace id found"); } return traceIds; } - private static final SdkHttpClient httpClient = UrlConnectionHttpClient.builder().build(); - private static final Region region = Region.of(System.getProperty("AWS_DEFAULT_REGION", "eu-west-1")); - private static final XRayClient xray = XRayClient.builder() - .httpClient(httpClient) - .region(region) - .build(); - public static class Builder { private Instant start; private Instant end; @@ -167,12 +196,15 @@ public static class Builder { private List excludedSegments = Arrays.asList("Initialization", "Invocation", "Overhead"); public TraceFetcher build() { - if (filterExpression == null) + if (filterExpression == null) { throw new IllegalArgumentException("filterExpression or functionName is required"); - if (start == null) + } + if (start == null) { throw new IllegalArgumentException("start is required"); - if (end == null) + } + if (end == null) { end = start.plus(1, ChronoUnit.MINUTES); + } LOG.debug("Looking for traces from {} to {} with filter {}", start, end, filterExpression); return new TraceFetcher(start, end, filterExpression, excludedSegments); } @@ -194,6 +226,7 @@ public Builder filterExpression(String filterExpression) { /** * "Initialization", "Invocation", "Overhead" are excluded by default + * * @param excludedSegments * @return */ @@ -203,7 +236,8 @@ public Builder excludeSegments(List excludedSegments) { } public Builder functionName(String functionName) { - this.filterExpression = String.format("service(id(name: \"%s\", type: \"AWS::Lambda::Function\"))", functionName); + this.filterExpression = + String.format("service(id(name: \"%s\", type: \"AWS::Lambda::Function\"))", functionName); return this; } } diff --git a/powertools-idempotency/pom.xml b/powertools-idempotency/pom.xml index cb4d9b802..ba4b147ae 100644 --- a/powertools-idempotency/pom.xml +++ b/powertools-idempotency/pom.xml @@ -1,4 +1,18 @@ + + @@ -180,6 +194,10 @@ + + org.apache.maven.plugins + maven-checkstyle-plugin + diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/Constants.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/Constants.java index 28c6f58aa..0d19fa7a9 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/Constants.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/Constants.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.idempotency; public class Constants { diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/Idempotency.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/Idempotency.java index ce652791b..6da826c45 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/Idempotency.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/Idempotency.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.idempotency; import com.amazonaws.services.lambda.runtime.Context; @@ -38,29 +39,6 @@ public class Idempotency { private Idempotency() { } - public IdempotencyConfig getConfig() { - return config; - } - - public BasePersistenceStore getPersistenceStore() { - if (persistenceStore == null) { - throw new IllegalStateException("Persistence Store is null, did you call 'configure()'?"); - } - return persistenceStore; - } - - private void setConfig(IdempotencyConfig config) { - this.config = config; - } - - private void setPersistenceStore(BasePersistenceStore persistenceStore) { - this.persistenceStore = persistenceStore; - } - - private static class Holder { - private final static Idempotency instance = new Idempotency(); - } - public static Idempotency getInstance() { return Holder.instance; } @@ -84,6 +62,29 @@ public static Config config() { return new Config(); } + public IdempotencyConfig getConfig() { + return config; + } + + private void setConfig(IdempotencyConfig config) { + this.config = config; + } + + public BasePersistenceStore getPersistenceStore() { + if (persistenceStore == null) { + throw new IllegalStateException("Persistence Store is null, did you call 'configure()'?"); + } + return persistenceStore; + } + + private void setPersistenceStore(BasePersistenceStore persistenceStore) { + this.persistenceStore = persistenceStore; + } + + private static class Holder { + private final static Idempotency instance = new Idempotency(); + } + public static class Config { private IdempotencyConfig config; @@ -94,7 +95,8 @@ public static class Config { */ public void configure() { if (store == null) { - throw new IllegalStateException("Persistence Layer is null, configure one with 'withPersistenceStore()'"); + throw new IllegalStateException( + "Persistence Layer is null, configure one with 'withPersistenceStore()'"); } if (config == null) { config = IdempotencyConfig.builder().build(); diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/IdempotencyConfig.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/IdempotencyConfig.java index 28f20ffa9..58d0a7f5b 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/IdempotencyConfig.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/IdempotencyConfig.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,12 +11,12 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.idempotency; import com.amazonaws.services.lambda.runtime.Context; -import software.amazon.lambda.powertools.idempotency.internal.cache.LRUCache; - import java.time.Duration; +import software.amazon.lambda.powertools.idempotency.internal.cache.LRUCache; /** * Configuration of the idempotency feature. Use the {@link Builder} to create an instance. @@ -31,7 +31,9 @@ public class IdempotencyConfig { private final String hashFunction; private Context lambdaContext; - private IdempotencyConfig(String eventKeyJMESPath, String payloadValidationJMESPath, boolean throwOnNoIdempotencyKey, boolean useLocalCache, int localCacheMaxItems, long expirationInSeconds, String hashFunction) { + private IdempotencyConfig(String eventKeyJMESPath, String payloadValidationJMESPath, + boolean throwOnNoIdempotencyKey, boolean useLocalCache, int localCacheMaxItems, + long expirationInSeconds, String hashFunction) { this.localCacheMaxItems = localCacheMaxItems; this.useLocalCache = useLocalCache; this.expirationInSeconds = expirationInSeconds; @@ -41,6 +43,15 @@ private IdempotencyConfig(String eventKeyJMESPath, String payloadValidationJMESP this.hashFunction = hashFunction; } + /** + * Create a builder that can be used to configure and create a {@link IdempotencyConfig}. + * + * @return a new instance of {@link Builder} + */ + public static Builder builder() { + return new Builder(); + } + public int getLocalCacheMaxItems() { return localCacheMaxItems; } @@ -69,24 +80,14 @@ public String getHashFunction() { return hashFunction; } - - /** - * Create a builder that can be used to configure and create a {@link IdempotencyConfig}. - * - * @return a new instance of {@link Builder} - */ - public static Builder builder() { - return new Builder(); + public Context getLambdaContext() { + return lambdaContext; } public void setLambdaContext(Context lambdaContext) { this.lambdaContext = lambdaContext; } - public Context getLambdaContext() { - return lambdaContext; - } - public static class Builder { private int localCacheMaxItems = 256; @@ -107,6 +108,7 @@ public static class Builder { *

          * Idempotency.config().withConfig(config).configure();
          * 
+ * * @return an instance of {@link IdempotencyConfig}. */ public IdempotencyConfig build() { @@ -124,16 +126,15 @@ public IdempotencyConfig build() { * A JMESPath expression to extract the idempotency key from the event record.
* See https://jmespath.org/ for more details.
* Common paths are:
    - *
  • powertools_json(body) for APIGatewayProxyRequestEvent and APIGatewayV2HTTPEvent
  • - *
  • Records[*].powertools_json(body) for SQSEvent
  • - *
  • Records[0].Sns.Message | powertools_json(@) for SNSEvent
  • - *
  • detail for ScheduledEvent (EventBridge / CloudWatch events)
  • - *
  • Records[*].kinesis.powertools_json(powertools_base64(data)) for KinesisEvent
  • - *
  • Records[*].powertools_json(powertools_base64(data)) for KinesisFirehoseEvent
  • - *
  • ...
  • + *
  • powertools_json(body) for APIGatewayProxyRequestEvent and APIGatewayV2HTTPEvent
  • + *
  • Records[*].powertools_json(body) for SQSEvent
  • + *
  • Records[0].Sns.Message | powertools_json(@) for SNSEvent
  • + *
  • detail for ScheduledEvent (EventBridge / CloudWatch events)
  • + *
  • Records[*].kinesis.powertools_json(powertools_base64(data)) for KinesisEvent
  • + *
  • Records[*].powertools_json(powertools_base64(data)) for KinesisFirehoseEvent
  • + *
  • ...
  • *
* - * * @param eventKeyJMESPath path of the key in the Lambda event * @return the instance of the builder (to chain operations) */ diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/IdempotencyKey.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/IdempotencyKey.java index 92a0a3d49..4f63b10f5 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/IdempotencyKey.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/IdempotencyKey.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.idempotency; import java.lang.annotation.ElementType; diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/Idempotent.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/Idempotent.java index e7cace1fb..6ca40a0e1 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/Idempotent.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/Idempotent.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,10 +11,10 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.idempotency; import com.amazonaws.services.lambda.runtime.Context; - import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyAlreadyInProgressException.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyAlreadyInProgressException.java index 3d5ee93c5..dc87f422b 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyAlreadyInProgressException.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyAlreadyInProgressException.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.idempotency.exceptions; /** diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyConfigurationException.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyConfigurationException.java index 0d3844641..9e85f4b5f 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyConfigurationException.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyConfigurationException.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.idempotency.exceptions; /** diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyInconsistentStateException.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyInconsistentStateException.java index 40c90dcab..e41e30e84 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyInconsistentStateException.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyInconsistentStateException.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.idempotency.exceptions; /** diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyItemAlreadyExistsException.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyItemAlreadyExistsException.java index 088db59c0..ba7da69bf 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyItemAlreadyExistsException.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyItemAlreadyExistsException.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.idempotency.exceptions; /** diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyItemNotFoundException.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyItemNotFoundException.java index afae2554e..420829363 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyItemNotFoundException.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyItemNotFoundException.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,15 +11,16 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.idempotency.exceptions; /** * Exception thrown when the item was not found in the persistence store. */ -public class IdempotencyItemNotFoundException extends RuntimeException{ +public class IdempotencyItemNotFoundException extends RuntimeException { private static final long serialVersionUID = 4818288566747993032L; public IdempotencyItemNotFoundException(String idempotencyKey) { - super("Item with idempotency key "+ idempotencyKey + " not found"); + super("Item with idempotency key " + idempotencyKey + " not found"); } } diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyKeyException.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyKeyException.java index 7259dff0f..29b8bd2ec 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyKeyException.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyKeyException.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.idempotency.exceptions; /** diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyPersistenceLayerException.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyPersistenceLayerException.java index fa49b746c..bfdd2d792 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyPersistenceLayerException.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyPersistenceLayerException.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.idempotency.exceptions; /** diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyValidationException.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyValidationException.java index 5aee228eb..cdb2bb6a7 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyValidationException.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyValidationException.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.idempotency.exceptions; import software.amazon.lambda.powertools.idempotency.IdempotencyConfig; diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyHandler.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyHandler.java index 5ce723f04..2875ab3d1 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyHandler.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,26 +11,32 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.idempotency.internal; +import static software.amazon.lambda.powertools.idempotency.persistence.DataRecord.Status.EXPIRED; +import static software.amazon.lambda.powertools.idempotency.persistence.DataRecord.Status.INPROGRESS; + import com.amazonaws.services.lambda.runtime.Context; import com.fasterxml.jackson.databind.JsonNode; +import java.time.Instant; +import java.util.OptionalInt; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.reflect.MethodSignature; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import software.amazon.lambda.powertools.idempotency.Idempotency; -import software.amazon.lambda.powertools.idempotency.exceptions.*; +import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyAlreadyInProgressException; +import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyInconsistentStateException; +import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyItemAlreadyExistsException; +import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyItemNotFoundException; +import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyKeyException; +import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyPersistenceLayerException; +import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyValidationException; import software.amazon.lambda.powertools.idempotency.persistence.BasePersistenceStore; import software.amazon.lambda.powertools.idempotency.persistence.DataRecord; import software.amazon.lambda.powertools.utilities.JsonConfig; -import java.time.Instant; -import java.util.OptionalInt; - -import static software.amazon.lambda.powertools.idempotency.persistence.DataRecord.Status.EXPIRED; -import static software.amazon.lambda.powertools.idempotency.persistence.DataRecord.Status.INPROGRESS; - /** * Internal class that will handle the Idempotency, and use the {@link software.amazon.lambda.powertools.idempotency.persistence.PersistenceStore} * to store the result of previous calls. @@ -90,7 +96,9 @@ private Object processIdempotency() throws Throwable { } catch (IdempotencyKeyException ike) { throw ike; } catch (Exception e) { - throw new IdempotencyPersistenceLayerException("Failed to save in progress record to idempotency store. If you believe this is a Powertools for AWS Lambda (Java) bug, please open an issue.", e); + throw new IdempotencyPersistenceLayerException( + "Failed to save in progress record to idempotency store. If you believe this is a Powertools for AWS Lambda (Java) bug, please open an issue.", + e); } return getFunctionResponse(); } @@ -121,11 +129,14 @@ private DataRecord getIdempotencyRecord() { } catch (IdempotencyItemNotFoundException e) { // This code path will only be triggered if the record is removed between saveInProgress and getRecord LOG.debug("An existing idempotency record was deleted before we could fetch it"); - throw new IdempotencyInconsistentStateException("saveInProgress and getRecord return inconsistent results", e); + throw new IdempotencyInconsistentStateException("saveInProgress and getRecord return inconsistent results", + e); } catch (IdempotencyValidationException | IdempotencyKeyException vke) { throw vke; } catch (Exception e) { - throw new IdempotencyPersistenceLayerException("Failed to get record from idempotency store. If you believe this is a Powertools for AWS Lambda (Java) bug, please open an issue.", e); + throw new IdempotencyPersistenceLayerException( + "Failed to get record from idempotency store. If you believe this is a Powertools for AWS Lambda (Java) bug, please open an issue.", + e); } } @@ -144,19 +155,24 @@ private Object handleForStatus(DataRecord record) { if (INPROGRESS.equals(record.getStatus())) { if (record.getInProgressExpiryTimestamp().isPresent() && record.getInProgressExpiryTimestamp().getAsLong() < Instant.now().toEpochMilli()) { - throw new IdempotencyInconsistentStateException("Item should have been expired in-progress because it already time-outed."); + throw new IdempotencyInconsistentStateException( + "Item should have been expired in-progress because it already time-outed."); } - throw new IdempotencyAlreadyInProgressException("Execution already in progress with idempotency key: " + record.getIdempotencyKey()); + throw new IdempotencyAlreadyInProgressException( + "Execution already in progress with idempotency key: " + record.getIdempotencyKey()); } Class returnType = ((MethodSignature) pjp.getSignature()).getReturnType(); try { - LOG.debug("Response for key '{}' retrieved from idempotency store, skipping the function", record.getIdempotencyKey()); - if (returnType.equals(String.class)) + LOG.debug("Response for key '{}' retrieved from idempotency store, skipping the function", + record.getIdempotencyKey()); + if (returnType.equals(String.class)) { return record.getResponseData(); + } return JsonConfig.get().getObjectMapper().reader().readValue(record.getResponseData(), returnType); } catch (Exception e) { - throw new IdempotencyPersistenceLayerException("Unable to get function response as " + returnType.getSimpleName(), e); + throw new IdempotencyPersistenceLayerException( + "Unable to get function response as " + returnType.getSimpleName(), e); } } @@ -172,7 +188,9 @@ private Object getFunctionResponse() throws Throwable { } catch (IdempotencyKeyException ke) { throw ke; } catch (Exception e) { - throw new IdempotencyPersistenceLayerException("Failed to delete record from idempotency store. If you believe this is a Powertools for AWS Lambda (Java) bug, please open an issue.", e); + throw new IdempotencyPersistenceLayerException( + "Failed to delete record from idempotency store. If you believe this is a Powertools for AWS Lambda (Java) bug, please open an issue.", + e); } throw handlerException; } @@ -180,7 +198,9 @@ private Object getFunctionResponse() throws Throwable { try { persistenceStore.saveSuccess(data, response, Instant.now()); } catch (Exception e) { - throw new IdempotencyPersistenceLayerException("Failed to update record state to success in idempotency store. If you believe this is a Powertools for AWS Lambda (Java) bug, please open an issue.", e); + throw new IdempotencyPersistenceLayerException( + "Failed to update record state to success in idempotency store. If you believe this is a Powertools for AWS Lambda (Java) bug, please open an issue.", + e); } return response; } diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotentAspect.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotentAspect.java index dc2703e64..d34dd72dd 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotentAspect.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotentAspect.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,15 +11,20 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.idempotency.internal; +import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.placedOnRequestHandler; + +import com.amazonaws.services.lambda.runtime.Context; import com.fasterxml.jackson.databind.JsonNode; +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; -import com.amazonaws.services.lambda.runtime.Context; import software.amazon.lambda.powertools.idempotency.Constants; import software.amazon.lambda.powertools.idempotency.Idempotency; import software.amazon.lambda.powertools.idempotency.IdempotencyKey; @@ -27,11 +32,6 @@ import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyConfigurationException; import software.amazon.lambda.powertools.utilities.JsonConfig; -import java.lang.annotation.Annotation; -import java.lang.reflect.Method; - -import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.placedOnRequestHandler; - /** * Aspect that handles the {@link Idempotent} annotation. * It uses the {@link IdempotencyHandler} to actually do the job. @@ -48,19 +48,21 @@ public Object around(ProceedingJoinPoint pjp, Idempotent idempotent) throws Throwable { String idempotencyDisabledEnv = System.getenv().get(Constants.IDEMPOTENCY_DISABLED_ENV); - if (idempotencyDisabledEnv != null && !idempotencyDisabledEnv.equalsIgnoreCase("false")) { + if (idempotencyDisabledEnv != null && !"false".equalsIgnoreCase(idempotencyDisabledEnv)) { return pjp.proceed(pjp.getArgs()); } Method method = ((MethodSignature) pjp.getSignature()).getMethod(); if (method.getReturnType().equals(void.class)) { - throw new IdempotencyConfigurationException("The annotated method doesn't return anything. Unable to perform idempotency on void return type"); + throw new IdempotencyConfigurationException( + "The annotated method doesn't return anything. Unable to perform idempotency on void return type"); } boolean isHandler = placedOnRequestHandler(pjp); JsonNode payload = getPayload(pjp, method, isHandler); if (payload == null) { - throw new IdempotencyConfigurationException("Unable to get payload from the method. Ensure there is at least one parameter or that you use @IdempotencyKey"); + throw new IdempotencyConfigurationException( + "Unable to get payload from the method. Ensure there is at least one parameter or that you use @IdempotencyKey"); } Context lambdaContext; @@ -76,7 +78,8 @@ public Object around(ProceedingJoinPoint pjp, /** * Retrieve the payload from the annotated method parameters - * @param pjp joinPoint + * + * @param pjp joinPoint * @param method the annotated method * @return the payload used for idempotency */ diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/cache/LRUCache.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/cache/LRUCache.java index a017c211a..b57ad2977 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/cache/LRUCache.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/internal/cache/LRUCache.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.idempotency.internal.cache; import java.util.LinkedHashMap; @@ -19,6 +20,7 @@ /** * Implementation of a simple LRU Cache based on a {@link LinkedHashMap} * See here. + * * @param Type of the keys * @param Types of the values */ diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStore.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStore.java index c79068d1a..f58b276fd 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStore.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStore.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,23 +11,15 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.idempotency.persistence; +import static software.amazon.lambda.powertools.core.internal.LambdaConstants.LAMBDA_FUNCTION_NAME_ENV; + import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectWriter; import io.burt.jmespath.Expression; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import software.amazon.awssdk.utils.StringUtils; -import software.amazon.lambda.powertools.idempotency.IdempotencyConfig; -import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyItemAlreadyExistsException; -import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyItemNotFoundException; -import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyKeyException; -import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyValidationException; -import software.amazon.lambda.powertools.idempotency.internal.cache.LRUCache; -import software.amazon.lambda.powertools.utilities.JsonConfig; - import java.math.BigInteger; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; @@ -42,8 +34,16 @@ import java.util.Spliterators; import java.util.stream.Stream; import java.util.stream.StreamSupport; - -import static software.amazon.lambda.powertools.core.internal.LambdaConstants.LAMBDA_FUNCTION_NAME_ENV; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import software.amazon.awssdk.utils.StringUtils; +import software.amazon.lambda.powertools.idempotency.IdempotencyConfig; +import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyItemAlreadyExistsException; +import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyItemNotFoundException; +import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyKeyException; +import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyValidationException; +import software.amazon.lambda.powertools.idempotency.internal.cache.LRUCache; +import software.amazon.lambda.powertools.utilities.JsonConfig; /** * Persistence layer that will store the idempotency result. @@ -53,7 +53,7 @@ public abstract class BasePersistenceStore implements PersistenceStore { private static final Logger LOG = LoggerFactory.getLogger(BasePersistenceStore.class); - + protected boolean payloadValidationEnabled = false; private String functionName = ""; private boolean configured = false; private long expirationInSeconds = 60 * 60; // 1 hour default @@ -61,7 +61,6 @@ public abstract class BasePersistenceStore implements PersistenceStore { private LRUCache cache; private String eventKeyJMESPath; private Expression eventKeyCompiledJMESPath; - protected boolean payloadValidationEnabled = false; private Expression validationKeyJMESPath; private boolean throwOnNoIdempotencyKey = false; private String hashFunctionName; @@ -130,7 +129,8 @@ public void saveSuccess(JsonNode data, Object result, Instant now) { responseJson, getHashedPayload(data) ); - LOG.debug("Function successfully executed. Saving record to persistence store with idempotency key: {}", record.getIdempotencyKey()); + LOG.debug("Function successfully executed. Saving record to persistence store with idempotency key: {}", + record.getIdempotencyKey()); updateRecord(record); saveToCache(record); } catch (JsonProcessingException e) { @@ -145,7 +145,8 @@ public void saveSuccess(JsonNode data, Object result, Instant now) { * @param data Payload * @param now */ - public void saveInProgress(JsonNode data, Instant now, OptionalInt remainingTimeInMs) throws IdempotencyItemAlreadyExistsException { + public void saveInProgress(JsonNode data, Instant now, OptionalInt remainingTimeInMs) + throws IdempotencyItemAlreadyExistsException { Optional hashedIdempotencyKey = getHashedIdempotencyKey(data); if (!hashedIdempotencyKey.isPresent()) { // missing idempotency key => non-idempotent transaction, we do not store the data, simply return @@ -159,7 +160,8 @@ public void saveInProgress(JsonNode data, Instant now, OptionalInt remainingTime OptionalLong inProgressExpirationMsTimestamp = OptionalLong.empty(); if (remainingTimeInMs.isPresent()) { - inProgressExpirationMsTimestamp = OptionalLong.of(now.plus(remainingTimeInMs.getAsInt(), ChronoUnit.MILLIS).toEpochMilli()); + inProgressExpirationMsTimestamp = + OptionalLong.of(now.plus(remainingTimeInMs.getAsInt(), ChronoUnit.MILLIS).toEpochMilli()); } DataRecord record = new DataRecord( @@ -205,7 +207,8 @@ public void deleteRecord(JsonNode data, Throwable throwable) { * @throws IdempotencyValidationException Payload doesn't match the stored record for the given idempotency key * @throws IdempotencyItemNotFoundException Exception thrown if no record exists in persistence store with the idempotency key */ - public DataRecord getRecord(JsonNode data, Instant now) throws IdempotencyValidationException, IdempotencyItemNotFoundException { + public DataRecord getRecord(JsonNode data, Instant now) + throws IdempotencyValidationException, IdempotencyItemNotFoundException { Optional hashedIdempotencyKey = getHashedIdempotencyKey(data); if (!hashedIdempotencyKey.isPresent()) { // missing idempotency key => non-idempotent transaction, we do not get the data, simply return nothing @@ -255,7 +258,9 @@ private Optional getHashedIdempotencyKey(JsonNode data) { private boolean isMissingIdemPotencyKey(JsonNode data) { if (data.isContainerNode()) { - Stream> stream = StreamSupport.stream(Spliterators.spliteratorUnknownSize(data.fields(), Spliterator.ORDERED), false); + Stream> stream = + StreamSupport.stream(Spliterators.spliteratorUnknownSize(data.fields(), Spliterator.ORDERED), + false); return stream.allMatch(e -> e.getValue().isNull()); } return data.isNull(); @@ -302,7 +307,9 @@ String generateHash(JsonNode data) { node = data.decimalValue(); } else if (data.isBoolean()) { node = data.asBoolean(); - } else node = data; // anything else + } else { + node = data; // anything else + } MessageDigest hashAlgorithm = getHashAlgorithm(); byte[] digest = hashAlgorithm.digest(node.toString().getBytes(StandardCharsets.UTF_8)); @@ -356,17 +363,20 @@ private long getExpiryEpochSecond(Instant now) { * @param dataRecord DataRecord to save in cache */ private void saveToCache(DataRecord dataRecord) { - if (!useLocalCache) + if (!useLocalCache) { return; - if (dataRecord.getStatus().equals(DataRecord.Status.INPROGRESS)) + } + if (dataRecord.getStatus().equals(DataRecord.Status.INPROGRESS)) { return; + } cache.put(dataRecord.getIdempotencyKey(), dataRecord); } private DataRecord retrieveFromCache(String idempotencyKey, Instant now) { - if (!useLocalCache) + if (!useLocalCache) { return null; + } DataRecord record = cache.get(idempotencyKey); if (record != null) { @@ -380,8 +390,9 @@ private DataRecord retrieveFromCache(String idempotencyKey, Instant now) { } private void deleteFromCache(String idempotencyKey) { - if (!useLocalCache) + if (!useLocalCache) { return; + } cache.remove(idempotencyKey); } diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DataRecord.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DataRecord.java index 54001c449..9af7c6a53 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DataRecord.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DataRecord.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,14 +11,13 @@ * limitations under the License. * */ -package software.amazon.lambda.powertools.idempotency.persistence; -import software.amazon.lambda.powertools.idempotency.IdempotencyConfig; +package software.amazon.lambda.powertools.idempotency.persistence; import java.time.Instant; import java.util.Objects; -import java.util.OptionalInt; import java.util.OptionalLong; +import software.amazon.lambda.powertools.idempotency.IdempotencyConfig; /** * Data Class for idempotency records. This is actually the item that will be stored in the persistence layer. @@ -30,7 +29,7 @@ public class DataRecord { /** * This field is controlling how long the result of the idempotent * event is cached. It is stored in _seconds since epoch_. - * + *

* DynamoDB's TTL mechanism is used to remove the record once the * expiry has been reached, and subsequent execution of the request * will be permitted. The user must configure this on their table. @@ -43,16 +42,17 @@ public class DataRecord { * The in-progress field is set to the remaining lambda execution time * when the record is created. * This field is stored in _milliseconds since epoch_. - * + *

* This ensures that: - * + *

* 1/ other concurrently executing requests are blocked from starting * 2/ if a lambda times out, subsequent requests will be allowed again, despite - * the fact that the idempotency record is already in the table + * the fact that the idempotency record is already in the table */ private final OptionalLong inProgressExpiryTimestamp; - public DataRecord(String idempotencyKey, Status status, long expiryTimestamp, String responseData, String payloadHash) { + public DataRecord(String idempotencyKey, Status status, long expiryTimestamp, String responseData, + String payloadHash) { this.idempotencyKey = idempotencyKey; this.status = status.toString(); this.expiryTimestamp = expiryTimestamp; @@ -61,7 +61,8 @@ public DataRecord(String idempotencyKey, Status status, long expiryTimestamp, St this.inProgressExpiryTimestamp = OptionalLong.empty(); } - public DataRecord(String idempotencyKey, Status status, long expiryTimestamp, String responseData, String payloadHash, OptionalLong inProgressExpiryTimestamp) { + public DataRecord(String idempotencyKey, Status status, long expiryTimestamp, String responseData, + String payloadHash, OptionalLong inProgressExpiryTimestamp) { this.idempotencyKey = idempotencyKey; this.status = status.toString(); this.expiryTimestamp = expiryTimestamp; @@ -110,8 +111,12 @@ public String getPayloadHash() { @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } DataRecord record = (DataRecord) o; return expiryTimestamp == record.expiryTimestamp && idempotencyKey.equals(record.idempotencyKey) diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStore.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStore.java index 783b029bb..7a023b4de 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStore.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStore.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,31 +11,37 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.idempotency.persistence; +import static software.amazon.lambda.powertools.core.internal.LambdaConstants.AWS_REGION_ENV; +import static software.amazon.lambda.powertools.core.internal.LambdaConstants.LAMBDA_FUNCTION_NAME_ENV; +import static software.amazon.lambda.powertools.idempotency.persistence.DataRecord.Status.INPROGRESS; + +import java.time.Instant; +import java.util.AbstractMap; +import java.util.HashMap; +import java.util.Map; +import java.util.OptionalLong; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.model.*; +import software.amazon.awssdk.services.dynamodb.model.AttributeValue; +import software.amazon.awssdk.services.dynamodb.model.ConditionalCheckFailedException; +import software.amazon.awssdk.services.dynamodb.model.DeleteItemRequest; +import software.amazon.awssdk.services.dynamodb.model.GetItemRequest; +import software.amazon.awssdk.services.dynamodb.model.GetItemResponse; +import software.amazon.awssdk.services.dynamodb.model.PutItemRequest; +import software.amazon.awssdk.services.dynamodb.model.UpdateItemRequest; import software.amazon.awssdk.utils.StringUtils; import software.amazon.lambda.powertools.idempotency.Constants; import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyItemAlreadyExistsException; import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyItemNotFoundException; -import java.time.Instant; -import java.util.AbstractMap; -import java.util.HashMap; -import java.util.Map; -import java.util.OptionalLong; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import static software.amazon.lambda.powertools.core.internal.LambdaConstants.AWS_REGION_ENV; -import static software.amazon.lambda.powertools.core.internal.LambdaConstants.LAMBDA_FUNCTION_NAME_ENV; -import static software.amazon.lambda.powertools.idempotency.persistence.DataRecord.Status.INPROGRESS; - /** * DynamoDB version of the {@link PersistenceStore}. Will store idempotency data in DynamoDB.
* Use the {@link Builder} to create a new instance. @@ -83,7 +89,7 @@ private DynamoDBPersistenceStore(String tableName, this.dynamoDbClient = client; } else { String idempotencyDisabledEnv = System.getenv().get(Constants.IDEMPOTENCY_DISABLED_ENV); - if (idempotencyDisabledEnv == null || idempotencyDisabledEnv.equalsIgnoreCase("false")) { + if (idempotencyDisabledEnv == null || "false".equalsIgnoreCase(idempotencyDisabledEnv)) { this.dynamoDbClient = DynamoDbClient.builder() .httpClient(UrlConnectionHttpClient.builder().build()) .region(Region.of(System.getenv(AWS_REGION_ENV))) @@ -96,6 +102,10 @@ private DynamoDBPersistenceStore(String tableName, } } + public static Builder builder() { + return new Builder(); + } + @Override public DataRecord getRecord(String idempotencyKey) throws IdempotencyItemNotFoundException { GetItemResponse response = dynamoDbClient.getItem( @@ -133,7 +143,9 @@ public void putRecord(DataRecord record, Instant now) throws IdempotencyItemAlre item.put(this.statusAttr, AttributeValue.builder().s(record.getStatus().toString()).build()); if (record.getInProgressExpiryTimestamp().isPresent()) { - item.put(this.inProgressExpiryAttr, AttributeValue.builder().n(String.valueOf(record.getInProgressExpiryTimestamp().getAsLong())).build()); + item.put(this.inProgressExpiryAttr, + AttributeValue.builder().n(String.valueOf(record.getInProgressExpiryTimestamp().getAsLong())) + .build()); } if (this.payloadValidationEnabled) { @@ -151,9 +163,12 @@ public void putRecord(DataRecord record, Instant now) throws IdempotencyItemAlre .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); Map expressionAttributeValues = Stream.of( - new AbstractMap.SimpleEntry<>(":now", AttributeValue.builder().n(String.valueOf(now.getEpochSecond())).build()), - new AbstractMap.SimpleEntry<>(":now_milliseconds", AttributeValue.builder().n(String.valueOf(now.toEpochMilli())).build()), - new AbstractMap.SimpleEntry<>(":inprogress", AttributeValue.builder().s(INPROGRESS.toString()).build()) + new AbstractMap.SimpleEntry<>(":now", + AttributeValue.builder().n(String.valueOf(now.getEpochSecond())).build()), + new AbstractMap.SimpleEntry<>(":now_milliseconds", + AttributeValue.builder().n(String.valueOf(now.toEpochMilli())).build()), + new AbstractMap.SimpleEntry<>(":inprogress", + AttributeValue.builder().s(INPROGRESS.toString()).build()) ).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); @@ -161,14 +176,16 @@ public void putRecord(DataRecord record, Instant now) throws IdempotencyItemAlre PutItemRequest.builder() .tableName(tableName) .item(item) - .conditionExpression("attribute_not_exists(#id) OR #expiry < :now OR (attribute_exists(#in_progress_expiry) AND #in_progress_expiry < :now_milliseconds AND #status = :inprogress)") + .conditionExpression( + "attribute_not_exists(#id) OR #expiry < :now OR (attribute_exists(#in_progress_expiry) AND #in_progress_expiry < :now_milliseconds AND #status = :inprogress)") .expressionAttributeNames(expressionAttributeNames) .expressionAttributeValues(expressionAttributeValues) .build() ); } catch (ConditionalCheckFailedException e) { LOG.debug("Failed to put record for already existing idempotency key: {}", record.getIdempotencyKey()); - throw new IdempotencyItemAlreadyExistsException("Failed to put record for already existing idempotency key: " + record.getIdempotencyKey(), e); + throw new IdempotencyItemAlreadyExistsException( + "Failed to put record for already existing idempotency key: " + record.getIdempotencyKey(), e); } } @@ -184,15 +201,19 @@ public void updateRecord(DataRecord record) { .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); Map expressionAttributeValues = Stream.of( - new AbstractMap.SimpleEntry<>(":response_data", AttributeValue.builder().s(record.getResponseData()).build()), - new AbstractMap.SimpleEntry<>(":expiry", AttributeValue.builder().n(String.valueOf(record.getExpiryTimestamp())).build()), - new AbstractMap.SimpleEntry<>(":status", AttributeValue.builder().s(record.getStatus().toString()).build())) + new AbstractMap.SimpleEntry<>(":response_data", + AttributeValue.builder().s(record.getResponseData()).build()), + new AbstractMap.SimpleEntry<>(":expiry", + AttributeValue.builder().n(String.valueOf(record.getExpiryTimestamp())).build()), + new AbstractMap.SimpleEntry<>(":status", + AttributeValue.builder().s(record.getStatus().toString()).build())) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); if (payloadValidationEnabled) { updateExpression += ", #validation_key = :validation_key"; expressionAttributeNames.put("#validation_key", this.validationAttr); - expressionAttributeValues.put(":validation_key", AttributeValue.builder().s(record.getPayloadHash()).build()); + expressionAttributeValues.put(":validation_key", + AttributeValue.builder().s(record.getPayloadHash()).build()); } dynamoDbClient.updateItem(UpdateItemRequest.builder() @@ -242,16 +263,14 @@ private DataRecord itemToRecord(Map item) { // data and validation payload may be null AttributeValue data = item.get(this.dataAttr); AttributeValue validation = item.get(this.validationAttr); - return new DataRecord(item.get(sortKeyAttr != null ? sortKeyAttr: keyAttr).s(), + return new DataRecord(item.get(sortKeyAttr != null ? sortKeyAttr : keyAttr).s(), DataRecord.Status.valueOf(item.get(this.statusAttr).s()), Long.parseLong(item.get(this.expiryAttr).n()), data != null ? data.s() : null, validation != null ? validation.s() : null, - item.get(this.inProgressExpiryAttr) != null ? OptionalLong.of(Long.parseLong(item.get(this.inProgressExpiryAttr).n())) : OptionalLong.empty()); - } - - public static Builder builder() { - return new Builder(); + item.get(this.inProgressExpiryAttr) != null ? + OptionalLong.of(Long.parseLong(item.get(this.inProgressExpiryAttr).n())) : + OptionalLong.empty()); } /** @@ -288,7 +307,8 @@ public DynamoDBPersistenceStore build() { if (StringUtils.isEmpty(tableName)) { throw new IllegalArgumentException("Table name is not specified"); } - return new DynamoDBPersistenceStore(tableName, keyAttr, staticPkValue, sortKeyAttr, expiryAttr, inProgressExpiryAttr, statusAttr, dataAttr, validationAttr, dynamoDbClient); + return new DynamoDBPersistenceStore(tableName, keyAttr, staticPkValue, sortKeyAttr, expiryAttr, + inProgressExpiryAttr, statusAttr, dataAttr, validationAttr, dynamoDbClient); } /** diff --git a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/PersistenceStore.java b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/PersistenceStore.java index d199c99b5..c058b592e 100644 --- a/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/PersistenceStore.java +++ b/powertools-idempotency/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/PersistenceStore.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,13 +11,13 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.idempotency.persistence; +import java.time.Instant; import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyItemAlreadyExistsException; import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyItemNotFoundException; -import java.time.Instant; - /** * Persistence layer that will store the idempotency result. * In order to provide another implementation, extends {@link BasePersistenceStore}. @@ -26,6 +26,7 @@ public interface PersistenceStore { /** * Retrieve item from persistence store using idempotency key and return it as a DataRecord instance. + * * @param idempotencyKey the key of the record * @return DataRecord representation of existing record found in persistence store * @throws IdempotencyItemNotFoundException Exception thrown if no record exists in persistence store with the idempotency key @@ -34,6 +35,7 @@ public interface PersistenceStore { /** * Add a DataRecord to persistence store if it does not already exist with that key + * * @param record DataRecord instance * @param now * @throws IdempotencyItemAlreadyExistsException if a non-expired entry already exists. @@ -42,12 +44,14 @@ public interface PersistenceStore { /** * Update item in persistence store + * * @param record DataRecord instance */ void updateRecord(DataRecord record); /** * Remove item from persistence store + * * @param idempotencyKey the key of the record */ void deleteRecord(String idempotencyKey); diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/DynamoDBConfig.java b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/DynamoDBConfig.java index 38678322c..66ddb53ac 100644 --- a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/DynamoDBConfig.java +++ b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/DynamoDBConfig.java @@ -1,7 +1,24 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.idempotency; import com.amazonaws.services.dynamodbv2.local.main.ServerRunner; import com.amazonaws.services.dynamodbv2.local.server.DynamoDBProxyServer; +import java.io.IOException; +import java.net.ServerSocket; +import java.net.URI; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; @@ -9,11 +26,14 @@ import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.model.*; - -import java.io.IOException; -import java.net.ServerSocket; -import java.net.URI; +import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition; +import software.amazon.awssdk.services.dynamodb.model.BillingMode; +import software.amazon.awssdk.services.dynamodb.model.CreateTableRequest; +import software.amazon.awssdk.services.dynamodb.model.DescribeTableRequest; +import software.amazon.awssdk.services.dynamodb.model.DescribeTableResponse; +import software.amazon.awssdk.services.dynamodb.model.KeySchemaElement; +import software.amazon.awssdk.services.dynamodb.model.KeyType; +import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType; public class DynamoDBConfig { protected static final String TABLE_NAME = "idempotency_table"; @@ -24,7 +44,7 @@ public class DynamoDBConfig { public static void setupDynamo() { int port = getFreePort(); try { - dynamoProxy = ServerRunner.createServerFromCommandLineArgs(new String[]{ + dynamoProxy = ServerRunner.createServerFromCommandLineArgs(new String[] { "-inMemory", "-port", Integer.toString(port) @@ -51,7 +71,8 @@ public static void setupDynamo() { .billingMode(BillingMode.PAY_PER_REQUEST) .build()); - DescribeTableResponse response = client.describeTable(DescribeTableRequest.builder().tableName(TABLE_NAME).build()); + DescribeTableResponse response = + client.describeTable(DescribeTableRequest.builder().tableName(TABLE_NAME).build()); if (response == null) { throw new RuntimeException("Table was not created within expected time"); } diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/IdempotencyTest.java b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/IdempotencyTest.java index a782d9613..c94fec3db 100644 --- a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/IdempotencyTest.java +++ b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/IdempotencyTest.java @@ -1,6 +1,22 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.idempotency; +import static org.assertj.core.api.Assertions.assertThat; + import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; import com.amazonaws.services.lambda.runtime.tests.EventLoader; @@ -11,8 +27,6 @@ import software.amazon.awssdk.services.dynamodb.model.ScanRequest; import software.amazon.lambda.powertools.idempotency.handlers.IdempotencyFunction; -import static org.assertj.core.api.Assertions.assertThat; - public class IdempotencyTest extends DynamoDBConfig { @Mock @@ -27,12 +41,14 @@ void setUp() { public void endToEndTest() { IdempotencyFunction function = new IdempotencyFunction(client); - APIGatewayProxyResponseEvent response = function.handleRequest(EventLoader.loadApiGatewayRestEvent("apigw_event2.json"), context); + APIGatewayProxyResponseEvent response = + function.handleRequest(EventLoader.loadApiGatewayRestEvent("apigw_event2.json"), context); assertThat(function.handlerExecuted).isTrue(); function.handlerExecuted = false; - APIGatewayProxyResponseEvent response2 = function.handleRequest(EventLoader.loadApiGatewayRestEvent("apigw_event2.json"), context); + APIGatewayProxyResponseEvent response2 = + function.handleRequest(EventLoader.loadApiGatewayRestEvent("apigw_event2.json"), context); assertThat(function.handlerExecuted).isFalse(); assertThat(response).isEqualTo(response2); diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyEnabledFunction.java b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyEnabledFunction.java index 6c39dc6de..f12edc87e 100644 --- a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyEnabledFunction.java +++ b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyEnabledFunction.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.idempotency.handlers; import com.amazonaws.services.lambda.runtime.Context; diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyFunction.java b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyFunction.java index 0423bd90a..76c36ae9f 100644 --- a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyFunction.java +++ b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyFunction.java @@ -1,9 +1,30 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.idempotency.handlers; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URL; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Collectors; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import software.amazon.awssdk.services.dynamodb.DynamoDbClient; @@ -13,14 +34,6 @@ import software.amazon.lambda.powertools.idempotency.persistence.DynamoDBPersistenceStore; import software.amazon.lambda.powertools.utilities.JsonConfig; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.net.URL; -import java.util.HashMap; -import java.util.Map; -import java.util.stream.Collectors; - public class IdempotencyFunction implements RequestHandler { private final static Logger LOG = LogManager.getLogger(IdempotencyFunction.class); diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunction.java b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunction.java index f3c1bdbc9..34e3eb319 100644 --- a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunction.java +++ b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunction.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.idempotency.handlers; import com.amazonaws.services.lambda.runtime.Context; @@ -38,14 +39,14 @@ public Basket handleRequest(Product input, Context context) { if (registerContext) { Idempotency.registerLambdaContext(context); } - + return createBasket("fake", input); } @Idempotent private Basket createBasket(@IdempotencyKey String magicProduct, Product p) { called = true; - Basket b = new Basket(p); + Basket b = new Basket(p); b.add(new Product(0, magicProduct, 0)); return b; } diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunctionInternalKey.java b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunctionInternalKey.java index 566db6727..3ae500341 100644 --- a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunctionInternalKey.java +++ b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunctionInternalKey.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.idempotency.handlers; import com.amazonaws.services.lambda.runtime.Context; diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunctionInvalid.java b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunctionInvalid.java index 4c82bff15..42e438798 100644 --- a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunctionInvalid.java +++ b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunctionInvalid.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.idempotency.handlers; import com.amazonaws.services.lambda.runtime.Context; @@ -33,7 +34,7 @@ public Basket handleRequest(Product input, Context context) { @Idempotent private Basket createBasket(String magicProduct, Product p) { - Basket b = new Basket(p); + Basket b = new Basket(p); b.add(new Product(0, magicProduct, 0)); return b; } diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunctionVoid.java b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunctionVoid.java index a6b89fc8d..384ed5e86 100644 --- a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunctionVoid.java +++ b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/handlers/IdempotencyInternalFunctionVoid.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.idempotency.handlers; import com.amazonaws.services.lambda.runtime.Context; @@ -28,7 +29,7 @@ public class IdempotencyInternalFunctionVoid implements RequestHandler function.handleRequest(p, context)).isInstanceOf(IdempotencyAlreadyInProgressException.class); + assertThatThrownBy(() -> function.handleRequest(p, context)).isInstanceOf( + IdempotencyAlreadyInProgressException.class); } @Test - public void secondCall_inProgress_lambdaTimeout_timeoutExpired_shouldThrowInconsistentState() throws JsonProcessingException { + public void secondCall_inProgress_lambdaTimeout_timeoutExpired_shouldThrowInconsistentState() + throws JsonProcessingException { // GIVEN Idempotency.config() .withPersistenceStore(store) @@ -195,7 +210,8 @@ public void secondCall_inProgress_lambdaTimeout_timeoutExpired_shouldThrowIncons Product p = new Product(42, "fake product", 12); Basket b = new Basket(p); - OptionalLong timestampInThePast = OptionalLong.of(Instant.now().toEpochMilli() - 100); // timeout expired 100ms ago + OptionalLong timestampInThePast = + OptionalLong.of(Instant.now().toEpochMilli() - 100); // timeout expired 100ms ago DataRecord record = new DataRecord( "42", DataRecord.Status.INPROGRESS, @@ -207,7 +223,8 @@ public void secondCall_inProgress_lambdaTimeout_timeoutExpired_shouldThrowIncons // THEN IdempotencyEnabledFunction function = new IdempotencyEnabledFunction(); - assertThatThrownBy(() -> function.handleRequest(p, context)).isInstanceOf(IdempotencyInconsistentStateException.class); + assertThatThrownBy(() -> function.handleRequest(p, context)).isInstanceOf( + IdempotencyInconsistentStateException.class); } @Test @@ -278,7 +295,8 @@ public void idempotencyOnSubMethodAnnotated_firstCall_shouldPutInStore() { ArgumentCaptor resultCaptor = ArgumentCaptor.forClass(Basket.class); verify(store).saveSuccess(any(), resultCaptor.capture(), any()); - assertThat(resultCaptor.getValue().getProducts()).contains(basket.getProducts().get(0), new Product(0, "fake", 0)); + assertThat(resultCaptor.getValue().getProducts()).contains(basket.getProducts().get(0), + new Product(0, "fake", 0)); } @Test @@ -305,11 +323,13 @@ public void idempotencyOnSubMethodAnnotated_firstCall_contextNotRegistered_shoul ArgumentCaptor resultCaptor = ArgumentCaptor.forClass(Basket.class); verify(store).saveSuccess(any(), resultCaptor.capture(), any()); - assertThat(resultCaptor.getValue().getProducts()).contains(basket.getProducts().get(0), new Product(0, "fake", 0)); + assertThat(resultCaptor.getValue().getProducts()).contains(basket.getProducts().get(0), + new Product(0, "fake", 0)); } @Test - public void idempotencyOnSubMethodAnnotated_secondCall_notExpired_shouldGetFromStore() throws JsonProcessingException { + public void idempotencyOnSubMethodAnnotated_secondCall_notExpired_shouldGetFromStore() + throws JsonProcessingException { // GIVEN Idempotency.config() .withPersistenceStore(store) @@ -354,7 +374,8 @@ public void idempotencyOnSubMethodAnnotated_keyJMESPath_shouldPutInStoreWithKey( ArgumentCaptor recordCaptor = ArgumentCaptor.forClass(DataRecord.class); verify(persistenceStore).putRecord(recordCaptor.capture(), any()); // a1d0c6e83f027327d8461063f4ac58a6 = MD5(42) - assertThat(recordCaptor.getValue().getIdempotencyKey()).isEqualTo("testFunction.createBasket#a1d0c6e83f027327d8461063f4ac58a6"); + assertThat(recordCaptor.getValue().getIdempotencyKey()).isEqualTo( + "testFunction.createBasket#a1d0c6e83f027327d8461063f4ac58a6"); } @Test @@ -369,7 +390,8 @@ public void idempotencyOnSubMethodNotAnnotated_shouldThrowException() { Product p = new Product(42, "fake product", 12); // THEN - assertThatThrownBy(() -> function.handleRequest(p, context)).isInstanceOf(IdempotencyConfigurationException.class); + assertThatThrownBy(() -> function.handleRequest(p, context)).isInstanceOf( + IdempotencyConfigurationException.class); } @Test @@ -384,7 +406,8 @@ public void idempotencyOnSubMethodVoid_shouldThrowException() { Product p = new Product(42, "fake product", 12); // THEN - assertThatThrownBy(() -> function.handleRequest(p, context)).isInstanceOf(IdempotencyConfigurationException.class); + assertThatThrownBy(() -> function.handleRequest(p, context)).isInstanceOf( + IdempotencyConfigurationException.class); } } diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/internal/cache/LRUCacheTest.java b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/internal/cache/LRUCacheTest.java index 3d2f7c7e3..8854be1f2 100644 --- a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/internal/cache/LRUCacheTest.java +++ b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/internal/cache/LRUCacheTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,12 +11,13 @@ * limitations under the License. * */ -package software.amazon.lambda.powertools.idempotency.internal.cache; -import org.junit.jupiter.api.Test; +package software.amazon.lambda.powertools.idempotency.internal.cache; import static org.assertj.core.api.Assertions.assertThat; +import org.junit.jupiter.api.Test; + public class LRUCacheTest { @Test diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/model/Basket.java b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/model/Basket.java index 304fd3810..a17bb8abf 100644 --- a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/model/Basket.java +++ b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/model/Basket.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.idempotency.model; import java.util.ArrayList; @@ -21,19 +22,19 @@ public class Basket { private List products = new ArrayList<>(); - public List getProducts() { - return products; + public Basket() { } - public void setProducts(List products) { - this.products = products; + public Basket(Product... p) { + products.addAll(Arrays.asList(p)); } - public Basket() { + public List getProducts() { + return products; } - public Basket( Product ...p){ - products.addAll(Arrays.asList(p)); + public void setProducts(List products) { + this.products = products; } public void add(Product product) { @@ -42,8 +43,12 @@ public void add(Product product) { @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } Basket basket = (Basket) o; return products.equals(basket.products); } diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/model/Product.java b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/model/Product.java index 1c66c584d..7fa191d82 100644 --- a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/model/Product.java +++ b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/model/Product.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.idempotency.model; import java.util.Objects; @@ -57,8 +58,12 @@ public void setPrice(double price) { @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } Product product = (Product) o; return id == product.id && Double.compare(product.price, price) == 0 && Objects.equals(name, product.name); } diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStoreTest.java b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStoreTest.java index 6b58fa8a5..67bc5aa22 100644 --- a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStoreTest.java +++ b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/BasePersistenceStoreTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,13 +11,21 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.idempotency.persistence; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; import com.amazonaws.services.lambda.runtime.tests.EventLoader; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.node.DoubleNode; import com.fasterxml.jackson.databind.node.TextNode; +import java.time.Duration; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.OptionalInt; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import software.amazon.lambda.powertools.idempotency.IdempotencyConfig; @@ -29,14 +37,6 @@ import software.amazon.lambda.powertools.idempotency.model.Product; import software.amazon.lambda.powertools.utilities.JsonConfig; -import java.time.Duration; -import java.time.Instant; -import java.time.temporal.ChronoUnit; -import java.util.OptionalInt; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - public class BasePersistenceStoreTest { private DataRecord dr; @@ -53,7 +53,8 @@ public void setup() { @Override public DataRecord getRecord(String idempotencyKey) throws IdempotencyItemNotFoundException { status = 0; - return new DataRecord(idempotencyKey, DataRecord.Status.INPROGRESS, Instant.now().plus(3600, ChronoUnit.SECONDS).getEpochSecond(), "Response", validationHash); + return new DataRecord(idempotencyKey, DataRecord.Status.INPROGRESS, + Instant.now().plus(3600, ChronoUnit.SECONDS).getEpochSecond(), "Response", validationHash); } @Override @@ -84,7 +85,8 @@ public void saveInProgress_defaultConfig() { persistenceStore.configure(IdempotencyConfig.builder().build(), null); Instant now = Instant.now(); - persistenceStore.saveInProgress(JsonConfig.get().getObjectMapper().valueToTree(event), now, OptionalInt.empty()); + persistenceStore.saveInProgress(JsonConfig.get().getObjectMapper().valueToTree(event), now, + OptionalInt.empty()); assertThat(dr.getStatus()).isEqualTo(DataRecord.Status.INPROGRESS); assertThat(dr.getExpiryTimestamp()).isEqualTo(now.plus(3600, ChronoUnit.SECONDS).getEpochSecond()); assertThat(dr.getResponseData()).isNull(); @@ -101,13 +103,15 @@ public void saveInProgress_withRemainingTime() { int lambdaTimeoutMs = 30000; Instant now = Instant.now(); - persistenceStore.saveInProgress(JsonConfig.get().getObjectMapper().valueToTree(event), now, OptionalInt.of(lambdaTimeoutMs)); + persistenceStore.saveInProgress(JsonConfig.get().getObjectMapper().valueToTree(event), now, + OptionalInt.of(lambdaTimeoutMs)); assertThat(dr.getStatus()).isEqualTo(DataRecord.Status.INPROGRESS); assertThat(dr.getExpiryTimestamp()).isEqualTo(now.plus(3600, ChronoUnit.SECONDS).getEpochSecond()); assertThat(dr.getResponseData()).isNull(); assertThat(dr.getIdempotencyKey()).isEqualTo("testFunction#7b40f56c086de5aa91dc467456329ed2"); assertThat(dr.getPayloadHash()).isEqualTo(""); - assertThat(dr.getInProgressExpiryTimestamp().orElse(-1)).isEqualTo(now.plus(lambdaTimeoutMs, ChronoUnit.MILLIS).toEpochMilli()); + assertThat(dr.getInProgressExpiryTimestamp().orElse(-1)).isEqualTo( + now.plus(lambdaTimeoutMs, ChronoUnit.MILLIS).toEpochMilli()); assertThat(status).isEqualTo(1); } @@ -119,7 +123,8 @@ public void saveInProgress_jmespath() { .build(), "myfunc"); Instant now = Instant.now(); - persistenceStore.saveInProgress(JsonConfig.get().getObjectMapper().valueToTree(event), now, OptionalInt.empty()); + persistenceStore.saveInProgress(JsonConfig.get().getObjectMapper().valueToTree(event), now, + OptionalInt.empty()); assertThat(dr.getStatus()).isEqualTo(DataRecord.Status.INPROGRESS); assertThat(dr.getExpiryTimestamp()).isEqualTo(now.plus(3600, ChronoUnit.SECONDS).getEpochSecond()); assertThat(dr.getResponseData()).isNull(); @@ -136,7 +141,9 @@ public void saveInProgress_jmespath_NotFound_shouldThrowException() { .withThrowOnNoIdempotencyKey(true) // should throw .build(), ""); Instant now = Instant.now(); - assertThatThrownBy(() -> persistenceStore.saveInProgress(JsonConfig.get().getObjectMapper().valueToTree(event), now, OptionalInt.empty())) + assertThatThrownBy( + () -> persistenceStore.saveInProgress(JsonConfig.get().getObjectMapper().valueToTree(event), now, + OptionalInt.empty())) .isInstanceOf(IdempotencyKeyException.class) .hasMessageContaining("No data found to create a hashed idempotency key"); assertThat(status).isEqualTo(-1); @@ -149,7 +156,8 @@ public void saveInProgress_jmespath_NotFound_shouldNotPersist() { .withEventKeyJMESPath("unavailable") .build(), ""); Instant now = Instant.now(); - persistenceStore.saveInProgress(JsonConfig.get().getObjectMapper().valueToTree(event), now, OptionalInt.empty()); + persistenceStore.saveInProgress(JsonConfig.get().getObjectMapper().valueToTree(event), now, + OptionalInt.empty()); assertThat(dr).isNull(); assertThat(status).isEqualTo(-1); } @@ -170,7 +178,9 @@ public void saveInProgress_withLocalCache_NotExpired_ShouldThrowException() { now.plus(3600, ChronoUnit.SECONDS).getEpochSecond(), null, null) ); - assertThatThrownBy(() -> persistenceStore.saveInProgress(JsonConfig.get().getObjectMapper().valueToTree(event), now, OptionalInt.empty())) + assertThatThrownBy( + () -> persistenceStore.saveInProgress(JsonConfig.get().getObjectMapper().valueToTree(event), now, + OptionalInt.empty())) .isInstanceOf(IdempotencyItemAlreadyExistsException.class); assertThat(status).isEqualTo(-1); } @@ -192,7 +202,8 @@ public void saveInProgress_withLocalCache_Expired_ShouldRemoveFromCache() { now.minus(3, ChronoUnit.SECONDS).getEpochSecond(), null, null) ); - persistenceStore.saveInProgress(JsonConfig.get().getObjectMapper().valueToTree(event), now, OptionalInt.empty()); + persistenceStore.saveInProgress(JsonConfig.get().getObjectMapper().valueToTree(event), now, + OptionalInt.empty()); assertThat(dr.getStatus()).isEqualTo(DataRecord.Status.INPROGRESS); assertThat(cache).isEmpty(); assertThat(status).isEqualTo(1); @@ -250,7 +261,8 @@ public void saveSuccess_withCacheEnabled_shouldSaveInCache() throws JsonProcessi // @Test - public void getRecord_shouldReturnRecordFromPersistence() throws IdempotencyItemNotFoundException, IdempotencyValidationException { + public void getRecord_shouldReturnRecordFromPersistence() + throws IdempotencyItemNotFoundException, IdempotencyValidationException { APIGatewayProxyRequestEvent event = EventLoader.loadApiGatewayRestEvent("apigw_event.json"); LRUCache cache = new LRUCache<>(2); persistenceStore.configure(IdempotencyConfig.builder().build(), "myfunc", cache); @@ -264,7 +276,8 @@ public void getRecord_shouldReturnRecordFromPersistence() throws IdempotencyItem } @Test - public void getRecord_cacheEnabledNotExpired_shouldReturnRecordFromCache() throws IdempotencyItemNotFoundException, IdempotencyValidationException { + public void getRecord_cacheEnabledNotExpired_shouldReturnRecordFromCache() + throws IdempotencyItemNotFoundException, IdempotencyValidationException { APIGatewayProxyRequestEvent event = EventLoader.loadApiGatewayRestEvent("apigw_event.json"); LRUCache cache = new LRUCache<>(2); persistenceStore.configure(IdempotencyConfig.builder() @@ -287,7 +300,8 @@ public void getRecord_cacheEnabledNotExpired_shouldReturnRecordFromCache() throw } @Test - public void getRecord_cacheEnabledExpired_shouldReturnRecordFromPersistence() throws IdempotencyItemNotFoundException, IdempotencyValidationException { + public void getRecord_cacheEnabledExpired_shouldReturnRecordFromPersistence() + throws IdempotencyItemNotFoundException, IdempotencyValidationException { APIGatewayProxyRequestEvent event = EventLoader.loadApiGatewayRestEvent("apigw_event.json"); LRUCache cache = new LRUCache<>(2); persistenceStore.configure(IdempotencyConfig.builder() @@ -314,14 +328,15 @@ public void getRecord_cacheEnabledExpired_shouldReturnRecordFromPersistence() th public void getRecord_invalidPayload_shouldThrowValidationException() { APIGatewayProxyRequestEvent event = EventLoader.loadApiGatewayRestEvent("apigw_event.json"); persistenceStore.configure(IdempotencyConfig.builder() - .withEventKeyJMESPath("powertools_json(body).id") - .withPayloadValidationJMESPath("powertools_json(body).message") - .build(), + .withEventKeyJMESPath("powertools_json(body).id") + .withPayloadValidationJMESPath("powertools_json(body).message") + .build(), "myfunc"); this.validationHash = "different hash"; // "Lambda rocks" ==> 70c24d88041893f7fbab4105b76fd9e1 - assertThatThrownBy(() -> persistenceStore.getRecord(JsonConfig.get().getObjectMapper().valueToTree(event), Instant.now())) + assertThatThrownBy( + () -> persistenceStore.getRecord(JsonConfig.get().getObjectMapper().valueToTree(event), Instant.now())) .isInstanceOf(IdempotencyValidationException.class); } @@ -348,7 +363,8 @@ public void deleteRecord_cacheEnabled_shouldDeleteRecordFromCache() { .withUseLocalCache(true).build(), null, cache); cache.put("testFunction#7b40f56c086de5aa91dc467456329ed2", - new DataRecord("testFunction#7b40f56c086de5aa91dc467456329ed2", DataRecord.Status.COMPLETED, 123, null, null)); + new DataRecord("testFunction#7b40f56c086de5aa91dc467456329ed2", DataRecord.Status.COMPLETED, 123, null, + null)); persistenceStore.deleteRecord(JsonConfig.get().getObjectMapper().valueToTree(event), new ArithmeticException()); assertThat(status).isEqualTo(3); assertThat(cache).isEmpty(); @@ -385,7 +401,8 @@ public void generateHashDouble_shouldGenerateMd5ofDouble() { @Test public void generateHashString_withSha256Algorithm_shouldGenerateSha256ofString() { persistenceStore.configure(IdempotencyConfig.builder().withHashFunction("SHA-256").build(), null); - String expectedHash = "e6139efa88ef3337e901e826e6f327337f414860fb499d9f26eefcff21d719af"; // SHA-256(Lambda rocks) + String expectedHash = + "e6139efa88ef3337e901e826e6f327337f414860fb499d9f26eefcff21d719af"; // SHA-256(Lambda rocks) String generatedHash = persistenceStore.generateHash(new TextNode("Lambda rocks")); assertThat(generatedHash).isEqualTo(expectedHash); } diff --git a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStoreTest.java b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStoreTest.java index 768da2eaa..b19cebfe1 100644 --- a/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStoreTest.java +++ b/powertools-idempotency/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/DynamoDBPersistenceStoreTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,28 +11,39 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.idempotency.persistence; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junitpioneer.jupiter.SetEnvironmentVariable; -import software.amazon.awssdk.services.dynamodb.model.*; +import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition; +import software.amazon.awssdk.services.dynamodb.model.AttributeValue; +import software.amazon.awssdk.services.dynamodb.model.BillingMode; +import software.amazon.awssdk.services.dynamodb.model.CreateTableRequest; +import software.amazon.awssdk.services.dynamodb.model.DeleteItemRequest; +import software.amazon.awssdk.services.dynamodb.model.DeleteTableRequest; +import software.amazon.awssdk.services.dynamodb.model.GetItemRequest; +import software.amazon.awssdk.services.dynamodb.model.KeySchemaElement; +import software.amazon.awssdk.services.dynamodb.model.KeyType; +import software.amazon.awssdk.services.dynamodb.model.PutItemRequest; +import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType; +import software.amazon.awssdk.services.dynamodb.model.ScanRequest; import software.amazon.lambda.powertools.idempotency.Constants; import software.amazon.lambda.powertools.idempotency.DynamoDBConfig; import software.amazon.lambda.powertools.idempotency.IdempotencyConfig; import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyItemAlreadyExistsException; import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyItemNotFoundException; -import java.time.Instant; -import java.time.temporal.ChronoUnit; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - /** * These test are using DynamoDBLocal and sqlite, see https://nickolasfisher.com/blog/Configuring-an-In-Memory-DynamoDB-instance-with-Java-for-Integration-Testing * NOTE: on a Mac with Apple Chipset, you need to use the Oracle JDK x86 64-bit @@ -51,7 +62,8 @@ public void putRecord_shouldCreateRecordInDynamoDB() throws IdempotencyItemAlrea dynamoDBPersistenceStore.putRecord(new DataRecord("key", DataRecord.Status.COMPLETED, expiry, null, null), now); key = Collections.singletonMap("id", AttributeValue.builder().s("key").build()); - Map item = client.getItem(GetItemRequest.builder().tableName(TABLE_NAME).key(key).build()).item(); + Map item = + client.getItem(GetItemRequest.builder().tableName(TABLE_NAME).key(key).build()).item(); assertThat(item).isNotNull(); assertThat(item.get("status").s()).isEqualTo("COMPLETED"); assertThat(item.get("expiration").n()).isEqualTo(String.valueOf(expiry)); @@ -81,7 +93,8 @@ public void putRecord_shouldCreateRecordInDynamoDB_IfPreviousExpired() { ), now); // THEN: an item is inserted - Map itemInDb = client.getItem(GetItemRequest.builder().tableName(TABLE_NAME).key(key).build()).item(); + Map itemInDb = + client.getItem(GetItemRequest.builder().tableName(TABLE_NAME).key(key).build()).item(); assertThat(itemInDb).isNotNull(); assertThat(itemInDb.get("status").s()).isEqualTo("INPROGRESS"); assertThat(itemInDb.get("expiration").n()).isEqualTo(String.valueOf(expiry2)); @@ -113,7 +126,8 @@ public void putRecord_shouldCreateRecordInDynamoDB_IfLambdaWasInProgressAndTimed ), now); // THEN: an item is inserted - Map itemInDb = client.getItem(GetItemRequest.builder().tableName(TABLE_NAME).key(key).build()).item(); + Map itemInDb = + client.getItem(GetItemRequest.builder().tableName(TABLE_NAME).key(key).build()).item(); assertThat(itemInDb).isNotNull(); assertThat(itemInDb.get("status").s()).isEqualTo("INPROGRESS"); assertThat(itemInDb.get("expiration").n()).isEqualTo(String.valueOf(expiry2)); @@ -144,7 +158,8 @@ public void putRecord_shouldThrowIdempotencyItemAlreadyExistsException_IfRecordA ).isInstanceOf(IdempotencyItemAlreadyExistsException.class); // THEN: item was not updated, retrieve the initial one - Map itemInDb = client.getItem(GetItemRequest.builder().tableName(TABLE_NAME).key(key).build()).item(); + Map itemInDb = + client.getItem(GetItemRequest.builder().tableName(TABLE_NAME).key(key).build()).item(); assertThat(itemInDb).isNotNull(); assertThat(itemInDb.get("status").s()).isEqualTo("COMPLETED"); assertThat(itemInDb.get("expiration").n()).isEqualTo(String.valueOf(expiry)); @@ -178,7 +193,8 @@ public void putRecord_shouldBlockUpdate_IfRecordAlreadyExistAndProgressNotExpire .isInstanceOf(IdempotencyItemAlreadyExistsException.class); // THEN: item was not updated, retrieve the initial one - Map itemInDb = client.getItem(GetItemRequest.builder().tableName(TABLE_NAME).key(key).build()).item(); + Map itemInDb = + client.getItem(GetItemRequest.builder().tableName(TABLE_NAME).key(key).build()).item(); assertThat(itemInDb).isNotNull(); assertThat(itemInDb.get("status").s()).isEqualTo("INPROGRESS"); assertThat(itemInDb.get("expiration").n()).isEqualTo(String.valueOf(expiry)); @@ -217,7 +233,8 @@ public void getRecord_shouldReturnExistingRecord() throws IdempotencyItemNotFoun @Test public void getRecord_shouldThrowException_whenRecordIsAbsent() { - assertThatThrownBy(() -> dynamoDBPersistenceStore.getRecord("key")).isInstanceOf(IdempotencyItemNotFoundException.class); + assertThatThrownBy(() -> dynamoDBPersistenceStore.getRecord("key")).isInstanceOf( + IdempotencyItemNotFoundException.class); } // @@ -237,7 +254,8 @@ public void updateRecord_shouldUpdateRecord() { item.put("status", AttributeValue.builder().s(DataRecord.Status.INPROGRESS.toString()).build()); client.putItem(PutItemRequest.builder().tableName(TABLE_NAME).item(item).build()); // enable payload validation - dynamoDBPersistenceStore.configure(IdempotencyConfig.builder().withPayloadValidationJMESPath("path").build(), null); + dynamoDBPersistenceStore.configure(IdempotencyConfig.builder().withPayloadValidationJMESPath("path").build(), + null); // WHEN expiry = now.plus(3600, ChronoUnit.SECONDS).getEpochSecond(); @@ -245,7 +263,8 @@ public void updateRecord_shouldUpdateRecord() { dynamoDBPersistenceStore.updateRecord(record); // THEN - Map itemInDb = client.getItem(GetItemRequest.builder().tableName(TABLE_NAME).key(key).build()).item(); + Map itemInDb = + client.getItem(GetItemRequest.builder().tableName(TABLE_NAME).key(key).build()).item(); assertThat(itemInDb.get("status").s()).isEqualTo("COMPLETED"); assertThat(itemInDb.get("expiration").n()).isEqualTo(String.valueOf(expiry)); assertThat(itemInDb.get("data").s()).isEqualTo("Fake result"); @@ -290,8 +309,10 @@ public void endToEndWithCustomAttrNamesAndSortKey() throws IdempotencyItemNotFou KeySchemaElement.builder().keyType(KeyType.RANGE).attributeName("sortkey").build() ) .attributeDefinitions( - AttributeDefinition.builder().attributeName("key").attributeType(ScalarAttributeType.S).build(), - AttributeDefinition.builder().attributeName("sortkey").attributeType(ScalarAttributeType.S).build() + AttributeDefinition.builder().attributeName("key").attributeType(ScalarAttributeType.S) + .build(), + AttributeDefinition.builder().attributeName("sortkey").attributeType(ScalarAttributeType.S) + .build() ) .billingMode(BillingMode.PAY_PER_REQUEST) .build()); @@ -323,7 +344,8 @@ public void endToEndWithCustomAttrNamesAndSortKey() throws IdempotencyItemNotFou customKey.put("key", AttributeValue.builder().s("pk").build()); customKey.put("sortkey", AttributeValue.builder().s("mykey").build()); - Map itemInDb = client.getItem(GetItemRequest.builder().tableName(TABLE_NAME_CUSTOM).key(customKey).build()).item(); + Map itemInDb = + client.getItem(GetItemRequest.builder().tableName(TABLE_NAME_CUSTOM).key(customKey).build()).item(); // GET DataRecord recordInDb = persistenceStore.getRecord("mykey"); diff --git a/powertools-logging/pom.xml b/powertools-logging/pom.xml index 767fbd3ee..83650fcde 100644 --- a/powertools-logging/pom.xml +++ b/powertools-logging/pom.xml @@ -1,4 +1,18 @@ + + @@ -127,4 +141,12 @@ + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + + \ No newline at end of file diff --git a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/CorrelationIdPathConstants.java b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/CorrelationIdPathConstants.java index e0d24b8a5..ce43c9aa0 100644 --- a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/CorrelationIdPathConstants.java +++ b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/CorrelationIdPathConstants.java @@ -1,3 +1,17 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.logging; /** @@ -11,7 +25,7 @@ public class CorrelationIdPathConstants { /** * To use when function is expecting API Gateway HTTP API Request event */ - public static final String API_GATEWAY_HTTP = "/requestContext/requestId"; + public static final String API_GATEWAY_HTTP = "/requestContext/requestId"; /** * To use when function is expecting Application Load balancer Request event */ @@ -19,5 +33,5 @@ public class CorrelationIdPathConstants { /** * To use when function is expecting Event Bridge Request event */ - public static final String EVENT_BRIDGE = "/id"; + public static final String EVENT_BRIDGE = "/id"; } diff --git a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/Logging.java b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/Logging.java index b86b800b7..9932eb700 100644 --- a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/Logging.java +++ b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/Logging.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.logging; import java.lang.annotation.ElementType; @@ -70,7 +71,8 @@ /** * Json Pointer path to extract correlation id from. - * @see + * + * @see */ String correlationIdPath() default ""; diff --git a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/LoggingUtils.java b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/LoggingUtils.java index 9a1567d57..6e11573cc 100644 --- a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/LoggingUtils.java +++ b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/LoggingUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,18 +11,18 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.logging; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.apache.logging.log4j.ThreadContext; +import static java.util.Arrays.asList; +import com.fasterxml.jackson.databind.ObjectMapper; import java.util.Map; - -import static java.util.Arrays.asList; +import org.apache.logging.log4j.ThreadContext; /** * A class of helper functions to add additional functionality to Logging. - * + *

* {@see Logging} */ public final class LoggingUtils { @@ -35,7 +35,7 @@ private LoggingUtils() { * Appends an additional key and value to each log entry made. Duplicate values * for the same key will be replaced with the latest. * - * @param key The name of the key to be logged + * @param key The name of the key to be logged * @param value The value to be logged */ public static void appendKey(String key, String value) { @@ -43,7 +43,6 @@ public static void appendKey(String key, String value) { } - /** * Appends additional key and value to each log entry made. Duplicate values * for the same key will be replaced with the latest. @@ -93,8 +92,8 @@ public static void defaultObjectMapper(ObjectMapper objectMapper) { } public static ObjectMapper objectMapper() { - if(null == objectMapper) { - objectMapper = new ObjectMapper(); + if (null == objectMapper) { + objectMapper = new ObjectMapper(); } return objectMapper; diff --git a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/AbstractJacksonLayoutCopy.java b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/AbstractJacksonLayoutCopy.java index c96d1383e..f98e2ee46 100644 --- a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/AbstractJacksonLayoutCopy.java +++ b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/AbstractJacksonLayoutCopy.java @@ -1,3 +1,17 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.logging.internal; import com.fasterxml.jackson.annotation.JsonAnyGetter; @@ -7,6 +21,11 @@ import com.fasterxml.jackson.core.JsonGenerationException; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.ObjectWriter; +import java.io.IOException; +import java.io.Writer; +import java.nio.charset.Charset; +import java.util.LinkedHashMap; +import java.util.Map; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.Marker; import org.apache.logging.log4j.ThreadContext; @@ -26,17 +45,145 @@ import org.apache.logging.log4j.util.ReadOnlyStringMap; import org.apache.logging.log4j.util.Strings; -import java.io.IOException; -import java.io.Writer; -import java.nio.charset.Charset; -import java.util.LinkedHashMap; -import java.util.Map; - @Deprecated abstract class AbstractJacksonLayoutCopy extends AbstractStringLayout { protected static final String DEFAULT_EOL = "\r\n"; protected static final String COMPACT_EOL = Strings.EMPTY; + protected final String eol; + protected final ObjectWriter objectWriter; + protected final boolean compact; + protected final boolean complete; + protected final boolean includeNullDelimiter; + protected final ResolvableKeyValuePair[] additionalFields; + @Deprecated + protected AbstractJacksonLayoutCopy(final Configuration config, final ObjectWriter objectWriter, + final Charset charset, + final boolean compact, final boolean complete, final boolean eventEol, + final Serializer headerSerializer, + final Serializer footerSerializer) { + this(config, objectWriter, charset, compact, complete, eventEol, headerSerializer, footerSerializer, false); + } + + @Deprecated + protected AbstractJacksonLayoutCopy(final Configuration config, final ObjectWriter objectWriter, + final Charset charset, + final boolean compact, final boolean complete, final boolean eventEol, + final Serializer headerSerializer, + final Serializer footerSerializer, final boolean includeNullDelimiter) { + this(config, objectWriter, charset, compact, complete, eventEol, null, headerSerializer, footerSerializer, + includeNullDelimiter, null); + } + + protected AbstractJacksonLayoutCopy(final Configuration config, final ObjectWriter objectWriter, + final Charset charset, + final boolean compact, final boolean complete, final boolean eventEol, + final String endOfLine, final Serializer headerSerializer, + final Serializer footerSerializer, final boolean includeNullDelimiter, + final KeyValuePair[] additionalFields) { + super(config, charset, headerSerializer, footerSerializer); + this.objectWriter = objectWriter; + this.compact = compact; + this.complete = complete; + this.eol = endOfLine != null ? endOfLine : compact && !eventEol ? COMPACT_EOL : DEFAULT_EOL; + this.includeNullDelimiter = includeNullDelimiter; + this.additionalFields = prepareAdditionalFields(config, additionalFields); + } + + protected static boolean valueNeedsLookup(final String value) { + return value != null && value.contains("${"); + } + + private static ResolvableKeyValuePair[] prepareAdditionalFields(final Configuration config, + final KeyValuePair[] additionalFields) { + if (additionalFields == null || additionalFields.length == 0) { + // No fields set + return ResolvableKeyValuePair.EMPTY_ARRAY; + } + + // Convert to specific class which already determines whether values needs lookup during serialization + final ResolvableKeyValuePair[] resolvableFields = new ResolvableKeyValuePair[additionalFields.length]; + + for (int i = 0; i < additionalFields.length; i++) { + final ResolvableKeyValuePair resolvable = + resolvableFields[i] = new ResolvableKeyValuePair(additionalFields[i]); + + // Validate + if (config == null && resolvable.valueNeedsLookup) { + throw new IllegalArgumentException( + "configuration needs to be set when there are additional fields with variables"); + } + } + + return resolvableFields; + } + + private static LogEvent convertMutableToLog4jEvent(final LogEvent event) { + return event instanceof Log4jLogEvent ? event : Log4jLogEvent.createMemento(event); + } + + /** + * Formats a {@link org.apache.logging.log4j.core.LogEvent}. + * + * @param event The LogEvent. + * @return The XML representation of the LogEvent. + */ + @Override + public String toSerializable(final LogEvent event) { + final StringBuilderWriter writer = new StringBuilderWriter(); + try { + toSerializable(event, writer); + return writer.toString(); + } catch (final IOException e) { + // Should this be an ISE or IAE? + LOGGER.error(e); + return Strings.EMPTY; + } + } + + protected Object wrapLogEvent(final LogEvent event) { + if (additionalFields.length > 0) { + // Construct map for serialization - note that we are intentionally using original LogEvent + final Map additionalFieldsMap = resolveAdditionalFields(event); + // This class combines LogEvent with AdditionalFields during serialization + return new LogEventWithAdditionalFields(event, additionalFieldsMap); + } else if (event instanceof Message) { + // If the LogEvent implements the Messagee interface Jackson will not treat is as a LogEvent. + return new ReadOnlyLogEventWrapper(event); + } else { + // No additional fields, return original object + return event; + } + } + + private Map resolveAdditionalFields(final LogEvent logEvent) { + // Note: LinkedHashMap retains order + final Map additionalFieldsMap = new LinkedHashMap<>(additionalFields.length); + final StrSubstitutor strSubstitutor = configuration.getStrSubstitutor(); + + // Go over each field + for (final ResolvableKeyValuePair pair : additionalFields) { + if (pair.valueNeedsLookup) { + // Resolve value + additionalFieldsMap.put(pair.key, strSubstitutor.replace(logEvent, pair.value)); + } else { + // Plain text value + additionalFieldsMap.put(pair.key, pair.value); + } + } + + return additionalFieldsMap; + } + + public void toSerializable(final LogEvent event, final Writer writer) + throws JsonGenerationException, JsonMappingException, IOException { + objectWriter.writeValue(writer, wrapLogEvent(convertMutableToLog4jEvent(event))); + writer.write(eol); + if (includeNullDelimiter) { + writer.write('\0'); + } + markEvent(); + } public static abstract class Builder> extends AbstractStringLayout.Builder { @@ -81,80 +228,68 @@ public boolean getEventEol() { return eventEol; } - public String getEndOfLine() { - return endOfLine; - } - - public boolean isCompact() { - return compact; - } - - public boolean isComplete() { - return complete; - } - - public boolean isLocationInfo() { - return locationInfo; - } - - public boolean isProperties() { - return properties; - } - - /** - * If "true", includes the stacktrace of any Throwable in the generated data, defaults to "true". - * @return If "true", includes the stacktrace of any Throwable in the generated data, defaults to "true". - */ - public boolean isIncludeStacktrace() { - return includeStacktrace; - } - - public boolean isStacktraceAsString() { - return stacktraceAsString; - } - - public boolean isIncludeNullDelimiter() { return includeNullDelimiter; } - - public boolean isIncludeTimeMillis() { - return includeTimeMillis; - } - - public KeyValuePair[] getAdditionalFields() { - return additionalFields; - } - public B setEventEol(final boolean eventEol) { this.eventEol = eventEol; return asBuilder(); } + public String getEndOfLine() { + return endOfLine; + } + public B setEndOfLine(final String endOfLine) { this.endOfLine = endOfLine; return asBuilder(); } + public boolean isCompact() { + return compact; + } + public B setCompact(final boolean compact) { this.compact = compact; return asBuilder(); } + public boolean isComplete() { + return complete; + } + public B setComplete(final boolean complete) { this.complete = complete; return asBuilder(); } + public boolean isLocationInfo() { + return locationInfo; + } + public B setLocationInfo(final boolean locationInfo) { this.locationInfo = locationInfo; return asBuilder(); } + public boolean isProperties() { + return properties; + } + public B setProperties(final boolean properties) { this.properties = properties; return asBuilder(); } + /** + * If "true", includes the stacktrace of any Throwable in the generated data, defaults to "true". + * + * @return If "true", includes the stacktrace of any Throwable in the generated data, defaults to "true". + */ + public boolean isIncludeStacktrace() { + return includeStacktrace; + } + /** * If "true", includes the stacktrace of any Throwable in the generated JSON, defaults to "true". + * * @param includeStacktrace If "true", includes the stacktrace of any Throwable in the generated JSON, defaults to "true". * @return this builder */ @@ -163,6 +298,10 @@ public B setIncludeStacktrace(final boolean includeStacktrace) { return asBuilder(); } + public boolean isStacktraceAsString() { + return stacktraceAsString; + } + /** * Whether to format the stacktrace as a string, and not a nested object (optional, defaults to false). * @@ -173,6 +312,10 @@ public B setStacktraceAsString(final boolean stacktraceAsString) { return asBuilder(); } + public boolean isIncludeNullDelimiter() { + return includeNullDelimiter; + } + /** * Whether to include NULL byte as delimiter after each event (optional, default to false). * @@ -183,6 +326,10 @@ public B setIncludeNullDelimiter(final boolean includeNullDelimiter) { return asBuilder(); } + public boolean isIncludeTimeMillis() { + return includeTimeMillis; + } + /** * Whether to include the timestamp (in addition to the Instant) (optional, default to false). * @@ -193,6 +340,10 @@ public B setIncludeTimeMillis(final boolean includeTimeMillis) { return asBuilder(); } + public KeyValuePair[] getAdditionalFields() { + return additionalFields; + } + /** * Additional fields to set on each log event. * @@ -204,132 +355,6 @@ public B setAdditionalFields(final KeyValuePair[] additionalFields) { } } - protected final String eol; - protected final ObjectWriter objectWriter; - protected final boolean compact; - protected final boolean complete; - protected final boolean includeNullDelimiter; - protected final ResolvableKeyValuePair[] additionalFields; - - @Deprecated - protected AbstractJacksonLayoutCopy(final Configuration config, final ObjectWriter objectWriter, final Charset charset, - final boolean compact, final boolean complete, final boolean eventEol, final Serializer headerSerializer, - final Serializer footerSerializer) { - this(config, objectWriter, charset, compact, complete, eventEol, headerSerializer, footerSerializer, false); - } - - @Deprecated - protected AbstractJacksonLayoutCopy(final Configuration config, final ObjectWriter objectWriter, final Charset charset, - final boolean compact, final boolean complete, final boolean eventEol, final Serializer headerSerializer, - final Serializer footerSerializer, final boolean includeNullDelimiter) { - this(config, objectWriter, charset, compact, complete, eventEol, null, headerSerializer, footerSerializer, includeNullDelimiter, null); - } - - protected AbstractJacksonLayoutCopy(final Configuration config, final ObjectWriter objectWriter, final Charset charset, - final boolean compact, final boolean complete, final boolean eventEol, final String endOfLine, final Serializer headerSerializer, - final Serializer footerSerializer, final boolean includeNullDelimiter, - final KeyValuePair[] additionalFields) { - super(config, charset, headerSerializer, footerSerializer); - this.objectWriter = objectWriter; - this.compact = compact; - this.complete = complete; - this.eol = endOfLine != null ? endOfLine : compact && !eventEol ? COMPACT_EOL : DEFAULT_EOL; - this.includeNullDelimiter = includeNullDelimiter; - this.additionalFields = prepareAdditionalFields(config, additionalFields); - } - - protected static boolean valueNeedsLookup(final String value) { - return value != null && value.contains("${"); - } - - private static ResolvableKeyValuePair[] prepareAdditionalFields(final Configuration config, final KeyValuePair[] additionalFields) { - if (additionalFields == null || additionalFields.length == 0) { - // No fields set - return ResolvableKeyValuePair.EMPTY_ARRAY; - } - - // Convert to specific class which already determines whether values needs lookup during serialization - final ResolvableKeyValuePair[] resolvableFields = new ResolvableKeyValuePair[additionalFields.length]; - - for (int i = 0; i < additionalFields.length; i++) { - final ResolvableKeyValuePair resolvable = resolvableFields[i] = new ResolvableKeyValuePair(additionalFields[i]); - - // Validate - if (config == null && resolvable.valueNeedsLookup) { - throw new IllegalArgumentException("configuration needs to be set when there are additional fields with variables"); - } - } - - return resolvableFields; - } - - /** - * Formats a {@link org.apache.logging.log4j.core.LogEvent}. - * - * @param event The LogEvent. - * @return The XML representation of the LogEvent. - */ - @Override - public String toSerializable(final LogEvent event) { - final StringBuilderWriter writer = new StringBuilderWriter(); - try { - toSerializable(event, writer); - return writer.toString(); - } catch (final IOException e) { - // Should this be an ISE or IAE? - LOGGER.error(e); - return Strings.EMPTY; - } - } - - private static LogEvent convertMutableToLog4jEvent(final LogEvent event) { - return event instanceof Log4jLogEvent ? event : Log4jLogEvent.createMemento(event); - } - - protected Object wrapLogEvent(final LogEvent event) { - if (additionalFields.length > 0) { - // Construct map for serialization - note that we are intentionally using original LogEvent - final Map additionalFieldsMap = resolveAdditionalFields(event); - // This class combines LogEvent with AdditionalFields during serialization - return new LogEventWithAdditionalFields(event, additionalFieldsMap); - } else if (event instanceof Message) { - // If the LogEvent implements the Messagee interface Jackson will not treat is as a LogEvent. - return new ReadOnlyLogEventWrapper(event); - } else { - // No additional fields, return original object - return event; - } - } - - private Map resolveAdditionalFields(final LogEvent logEvent) { - // Note: LinkedHashMap retains order - final Map additionalFieldsMap = new LinkedHashMap<>(additionalFields.length); - final StrSubstitutor strSubstitutor = configuration.getStrSubstitutor(); - - // Go over each field - for (final ResolvableKeyValuePair pair : additionalFields) { - if (pair.valueNeedsLookup) { - // Resolve value - additionalFieldsMap.put(pair.key, strSubstitutor.replace(logEvent, pair.value)); - } else { - // Plain text value - additionalFieldsMap.put(pair.key, pair.value); - } - } - - return additionalFieldsMap; - } - - public void toSerializable(final LogEvent event, final Writer writer) - throws JsonGenerationException, JsonMappingException, IOException { - objectWriter.writeValue(writer, wrapLogEvent(convertMutableToLog4jEvent(event))); - writer.write(eol); - if (includeNullDelimiter) { - writer.write('\0'); - } - markEvent(); - } - @JsonRootName(XmlConstants.ELT_EVENT) public static class LogEventWithAdditionalFields { @@ -471,13 +496,13 @@ public boolean isEndOfBatch() { } @Override - public boolean isIncludeLocation() { - return event.isIncludeLocation(); + public void setEndOfBatch(boolean endOfBatch) { + } @Override - public void setEndOfBatch(boolean endOfBatch) { - + public boolean isIncludeLocation() { + return event.isIncludeLocation(); } @Override diff --git a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/DefaultLambdaFields.java b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/DefaultLambdaFields.java index a50b292b2..2461ae771 100644 --- a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/DefaultLambdaFields.java +++ b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/DefaultLambdaFields.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,10 +11,10 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.logging.internal; import com.amazonaws.services.lambda.runtime.Context; - import java.util.HashMap; import java.util.Map; @@ -31,10 +31,6 @@ enum DefaultLambdaFields { this.name = name; } - public String getName() { - return name; - } - static Map values(Context context) { Map hashMap = new HashMap<>(); @@ -46,4 +42,8 @@ static Map values(Context context) { return hashMap; } + + public String getName() { + return name; + } } diff --git a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/JacksonFactoryCopy.java b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/JacksonFactoryCopy.java index 41247cfdb..6b568be30 100644 --- a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/JacksonFactoryCopy.java +++ b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/JacksonFactoryCopy.java @@ -1,3 +1,17 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.logging.internal; import com.fasterxml.jackson.core.PrettyPrinter; @@ -7,16 +21,57 @@ import com.fasterxml.jackson.databind.ObjectWriter; import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter; import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider; +import java.util.HashSet; +import java.util.Set; import org.apache.logging.log4j.core.impl.Log4jLogEvent; import org.apache.logging.log4j.core.jackson.JsonConstants; import org.apache.logging.log4j.core.jackson.Log4jJsonObjectMapper; -import java.util.HashSet; -import java.util.Set; - @Deprecated abstract class JacksonFactoryCopy { + abstract protected String getPropertyNameForTimeMillis(); + + abstract protected String getPropertyNameForInstant(); + + abstract protected String getPropertNameForContextMap(); + + abstract protected String getPropertNameForSource(); + + abstract protected String getPropertNameForNanoTime(); + + abstract protected PrettyPrinter newCompactPrinter(); + + abstract protected ObjectMapper newObjectMapper(); + + abstract protected PrettyPrinter newPrettyPrinter(); + + ObjectWriter newWriter(final boolean locationInfo, final boolean properties, final boolean compact) { + return newWriter(locationInfo, properties, compact, false); + } + + ObjectWriter newWriter(final boolean locationInfo, final boolean properties, final boolean compact, + final boolean includeMillis) { + final SimpleFilterProvider filters = new SimpleFilterProvider(); + final Set except = new HashSet<>(3); + if (!locationInfo) { + except.add(this.getPropertNameForSource()); + } + if (!properties) { + except.add(this.getPropertNameForContextMap()); + } + if (includeMillis) { + except.add(getPropertyNameForInstant()); + } else { + except.add(getPropertyNameForTimeMillis()); + } + except.add(this.getPropertNameForNanoTime()); + filters.addFilter(Log4jLogEvent.class.getName(), SimpleBeanPropertyFilter.serializeAllExcept(except)); + final ObjectWriter writer = + this.newObjectMapper().writer(compact ? this.newCompactPrinter() : this.newPrettyPrinter()); + return writer.with(filters); + } + static class JSON extends JacksonFactoryCopy { private final boolean encodeThreadContextAsList; @@ -24,7 +79,8 @@ static class JSON extends JacksonFactoryCopy { private final boolean stacktraceAsString; private final boolean objectMessageAsJsonObject; - public JSON(final boolean encodeThreadContextAsList, final boolean includeStacktrace, final boolean stacktraceAsString, final boolean objectMessageAsJsonObject) { + public JSON(final boolean encodeThreadContextAsList, final boolean includeStacktrace, + final boolean stacktraceAsString, final boolean objectMessageAsJsonObject) { this.encodeThreadContextAsList = encodeThreadContextAsList; this.includeStacktrace = includeStacktrace; this.stacktraceAsString = stacktraceAsString; @@ -63,7 +119,8 @@ protected PrettyPrinter newCompactPrinter() { @Override protected ObjectMapper newObjectMapper() { - return new Log4jJsonObjectMapper(encodeThreadContextAsList, includeStacktrace, stacktraceAsString, objectMessageAsJsonObject); + return new Log4jJsonObjectMapper(encodeThreadContextAsList, includeStacktrace, stacktraceAsString, + objectMessageAsJsonObject); } @Override @@ -73,45 +130,4 @@ protected PrettyPrinter newPrettyPrinter() { } - abstract protected String getPropertyNameForTimeMillis(); - - abstract protected String getPropertyNameForInstant(); - - abstract protected String getPropertNameForContextMap(); - - abstract protected String getPropertNameForSource(); - - abstract protected String getPropertNameForNanoTime(); - - abstract protected PrettyPrinter newCompactPrinter(); - - abstract protected ObjectMapper newObjectMapper(); - - abstract protected PrettyPrinter newPrettyPrinter(); - - ObjectWriter newWriter(final boolean locationInfo, final boolean properties, final boolean compact) { - return newWriter(locationInfo, properties, compact, false); - } - - ObjectWriter newWriter(final boolean locationInfo, final boolean properties, final boolean compact, - final boolean includeMillis) { - final SimpleFilterProvider filters = new SimpleFilterProvider(); - final Set except = new HashSet<>(3); - if (!locationInfo) { - except.add(this.getPropertNameForSource()); - } - if (!properties) { - except.add(this.getPropertNameForContextMap()); - } - if (includeMillis) { - except.add(getPropertyNameForInstant()); - } else { - except.add(getPropertyNameForTimeMillis()); - } - except.add(this.getPropertNameForNanoTime()); - filters.addFilter(Log4jLogEvent.class.getName(), SimpleBeanPropertyFilter.serializeAllExcept(except)); - final ObjectWriter writer = this.newObjectMapper().writer(compact ? this.newCompactPrinter() : this.newPrettyPrinter()); - return writer.with(filters); - } - } \ No newline at end of file diff --git a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LambdaJsonLayout.java b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LambdaJsonLayout.java index 578937231..c2c13c86f 100644 --- a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LambdaJsonLayout.java +++ b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LambdaJsonLayout.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,12 +11,25 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.logging.internal; +import static java.time.Instant.ofEpochMilli; +import static java.time.format.DateTimeFormatter.ISO_ZONED_DATE_TIME; + import com.fasterxml.jackson.annotation.JsonAnyGetter; import com.fasterxml.jackson.annotation.JsonGetter; import com.fasterxml.jackson.annotation.JsonRootName; import com.fasterxml.jackson.annotation.JsonUnwrapped; +import java.io.IOException; +import java.io.Writer; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; import org.apache.logging.log4j.core.Layout; import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.config.Configuration; @@ -30,75 +43,16 @@ import org.apache.logging.log4j.core.util.KeyValuePair; import org.apache.logging.log4j.util.Strings; -import java.io.IOException; -import java.io.Writer; -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; -import java.time.ZoneId; -import java.time.ZonedDateTime; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.Map; - -import static java.time.Instant.ofEpochMilli; -import static java.time.format.DateTimeFormatter.ISO_ZONED_DATE_TIME; - /*** * Note: The LambdaJsonLayout should be considered to be deprecated. Please use JsonTemplateLayout instead. */ @Deprecated @Plugin(name = "LambdaJsonLayout", category = Node.CATEGORY, elementType = Layout.ELEMENT_TYPE, printObject = true) public final class LambdaJsonLayout extends AbstractJacksonLayoutCopy { + static final String CONTENT_TYPE = "application/json"; private static final String DEFAULT_FOOTER = "]"; - private static final String DEFAULT_HEADER = "["; - static final String CONTENT_TYPE = "application/json"; - - public static class Builder> extends AbstractJacksonLayoutCopy.Builder - implements org.apache.logging.log4j.core.util.Builder { - - @PluginBuilderAttribute - private boolean propertiesAsList; - - @PluginBuilderAttribute - private boolean objectMessageAsJsonObject; - - public Builder() { - super(); - setCharset(StandardCharsets.UTF_8); - } - - @Override - public LambdaJsonLayout build() { - final boolean encodeThreadContextAsList = isProperties() && propertiesAsList; - final String headerPattern = toStringOrNull(getHeader()); - final String footerPattern = toStringOrNull(getFooter()); - return new LambdaJsonLayout(getConfiguration(), isLocationInfo(), isProperties(), encodeThreadContextAsList, - isComplete(), isCompact(), getEventEol(), headerPattern, footerPattern, getCharset(), - isIncludeStacktrace(), isStacktraceAsString(), isIncludeNullDelimiter(), - getAdditionalFields(), getObjectMessageAsJsonObject()); - } - - public boolean isPropertiesAsList() { - return propertiesAsList; - } - - public B setPropertiesAsList(final boolean propertiesAsList) { - this.propertiesAsList = propertiesAsList; - return asBuilder(); - } - - public boolean getObjectMessageAsJsonObject() { - return objectMessageAsJsonObject; - } - - public B setObjectMessageAsJsonObject(final boolean objectMessageAsJsonObject) { - this.objectMessageAsJsonObject = objectMessageAsJsonObject; - return asBuilder(); - } - } - private LambdaJsonLayout(final Configuration config, final boolean locationInfo, final boolean properties, final boolean encodeThreadContextAsList, final boolean complete, final boolean compact, final boolean eventEol, @@ -106,16 +60,34 @@ private LambdaJsonLayout(final Configuration config, final boolean locationInfo, final boolean includeStacktrace, final boolean stacktraceAsString, final boolean includeNullDelimiter, final KeyValuePair[] additionalFields, final boolean objectMessageAsJsonObject) { - super(config, new JacksonFactoryCopy.JSON(encodeThreadContextAsList, includeStacktrace, stacktraceAsString, objectMessageAsJsonObject).newWriter( - locationInfo, properties, compact), + super(config, new JacksonFactoryCopy.JSON(encodeThreadContextAsList, includeStacktrace, stacktraceAsString, + objectMessageAsJsonObject).newWriter( + locationInfo, properties, compact), charset, compact, complete, eventEol, null, - PatternLayout.newSerializerBuilder().setConfiguration(config).setPattern(headerPattern).setDefaultPattern(DEFAULT_HEADER).build(), - PatternLayout.newSerializerBuilder().setConfiguration(config).setPattern(footerPattern).setDefaultPattern(DEFAULT_FOOTER).build(), + PatternLayout.newSerializerBuilder().setConfiguration(config).setPattern(headerPattern) + .setDefaultPattern(DEFAULT_HEADER).build(), + PatternLayout.newSerializerBuilder().setConfiguration(config).setPattern(footerPattern) + .setDefaultPattern(DEFAULT_FOOTER).build(), includeNullDelimiter, additionalFields); } + @PluginBuilderFactory + public static > B newBuilder() { + return new Builder().asBuilder(); + } + + /** + * Creates a JSON Layout using the default settings. Useful for testing. + * + * @return A JSON Layout. + */ + public static LambdaJsonLayout createDefaultLayout() { + return new LambdaJsonLayout(new DefaultConfiguration(), false, false, false, false, false, false, + DEFAULT_HEADER, DEFAULT_FOOTER, StandardCharsets.UTF_8, true, false, false, null, false); + } + /** * Returns appropriate JSON header. * @@ -170,21 +142,6 @@ public String getContentType() { return CONTENT_TYPE + "; charset=" + this.getCharset(); } - @PluginBuilderFactory - public static > B newBuilder() { - return new Builder().asBuilder(); - } - - /** - * Creates a JSON Layout using the default settings. Useful for testing. - * - * @return A JSON Layout. - */ - public static LambdaJsonLayout createDefaultLayout() { - return new LambdaJsonLayout(new DefaultConfiguration(), false, false, false, false, false, false, - DEFAULT_HEADER, DEFAULT_FOOTER, StandardCharsets.UTF_8, true, false, false, null, false); - } - @Override public Object wrapLogEvent(final LogEvent event) { Map additionalFieldsMap = resolveAdditionalFields(event); @@ -205,15 +162,60 @@ private Map resolveAdditionalFields(LogEvent logEvent) { final Map additionalFieldsMap = new LinkedHashMap<>(additionalFields.length); // Go over MDC - logEvent.getContextData().forEach((key, value) -> { - if (Strings.isNotBlank(key) && value != null) { - additionalFieldsMap.put(key, value); - } - }); + logEvent.getContextData().forEach((key, value) -> + { + if (Strings.isNotBlank(key) && value != null) { + additionalFieldsMap.put(key, value); + } + }); return additionalFieldsMap; } + public static class Builder> extends AbstractJacksonLayoutCopy.Builder + implements org.apache.logging.log4j.core.util.Builder { + + @PluginBuilderAttribute + private boolean propertiesAsList; + + @PluginBuilderAttribute + private boolean objectMessageAsJsonObject; + + public Builder() { + super(); + setCharset(StandardCharsets.UTF_8); + } + + @Override + public LambdaJsonLayout build() { + final boolean encodeThreadContextAsList = isProperties() && propertiesAsList; + final String headerPattern = toStringOrNull(getHeader()); + final String footerPattern = toStringOrNull(getFooter()); + return new LambdaJsonLayout(getConfiguration(), isLocationInfo(), isProperties(), encodeThreadContextAsList, + isComplete(), isCompact(), getEventEol(), headerPattern, footerPattern, getCharset(), + isIncludeStacktrace(), isStacktraceAsString(), isIncludeNullDelimiter(), + getAdditionalFields(), getObjectMessageAsJsonObject()); + } + + public boolean isPropertiesAsList() { + return propertiesAsList; + } + + public B setPropertiesAsList(final boolean propertiesAsList) { + this.propertiesAsList = propertiesAsList; + return asBuilder(); + } + + public boolean getObjectMessageAsJsonObject() { + return objectMessageAsJsonObject; + } + + public B setObjectMessageAsJsonObject(final boolean objectMessageAsJsonObject) { + this.objectMessageAsJsonObject = objectMessageAsJsonObject; + return asBuilder(); + } + } + @JsonRootName(XmlConstants.ELT_EVENT) public static class LogEventWithAdditionalFields { @@ -237,7 +239,8 @@ public Map getAdditionalFields() { @JsonGetter("timestamp") public String getTimestamp() { - return ISO_ZONED_DATE_TIME.format(ZonedDateTime.from(ofEpochMilli(logEvent.getTimeMillis()).atZone(ZoneId.systemDefault()))); + return ISO_ZONED_DATE_TIME.format( + ZonedDateTime.from(ofEpochMilli(logEvent.getTimeMillis()).atZone(ZoneId.systemDefault()))); } } } diff --git a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LambdaLoggingAspect.java b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LambdaLoggingAspect.java index d489e093b..4a98735af 100644 --- a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LambdaLoggingAspect.java +++ b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LambdaLoggingAspect.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,12 +11,37 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.logging.internal; +import static java.nio.charset.StandardCharsets.UTF_8; +import static java.util.Optional.empty; +import static java.util.Optional.ofNullable; +import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.coldStartDone; +import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.extractContext; +import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.getXrayTraceId; +import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.isColdStart; +import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.isHandlerMethod; +import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.placedOnRequestHandler; +import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.placedOnStreamHandler; +import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.serviceName; +import static software.amazon.lambda.powertools.logging.LoggingUtils.appendKey; +import static software.amazon.lambda.powertools.logging.LoggingUtils.appendKeys; +import static software.amazon.lambda.powertools.logging.LoggingUtils.objectMapper; + import com.amazonaws.services.lambda.runtime.Context; import com.fasterxml.jackson.core.JsonPointer; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.util.Map; +import java.util.Optional; +import java.util.Random; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -32,31 +57,6 @@ import software.amazon.lambda.powertools.logging.Logging; import software.amazon.lambda.powertools.logging.LoggingUtils; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStreamWriter; -import java.util.Map; -import java.util.Optional; -import java.util.Random; - -import static java.nio.charset.StandardCharsets.UTF_8; -import static java.util.Optional.empty; -import static java.util.Optional.ofNullable; -import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.coldStartDone; -import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.extractContext; -import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.getXrayTraceId; -import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.isColdStart; -import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.isHandlerMethod; -import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.placedOnRequestHandler; -import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.placedOnStreamHandler; -import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.serviceName; -import static software.amazon.lambda.powertools.logging.LoggingUtils.appendKey; -import static software.amazon.lambda.powertools.logging.LoggingUtils.appendKeys; -import static software.amazon.lambda.powertools.logging.LoggingUtils.objectMapper; - @Aspect @DeclarePrecedence("*, software.amazon.lambda.powertools.logging.internal.LambdaLoggingAspect") public final class LambdaLoggingAspect { @@ -76,6 +76,12 @@ public final class LambdaLoggingAspect { LEVEL_AT_INITIALISATION = LOG.getLevel(); } + private static void resetLogLevels(Level logLevel) { + LoggerContext ctx = (LoggerContext) LogManager.getContext(false); + Configurator.setAllLevels(LogManager.getRootLogger().getName(), logLevel); + ctx.updateLoggers(); + } + @SuppressWarnings({"EmptyMethod"}) @Pointcut("@annotation(logging)") public void callAt(Logging logging) { @@ -90,7 +96,7 @@ public Object around(ProceedingJoinPoint pjp, Context extractedContext = extractContext(pjp); - if(null != extractedContext) { + if (null != extractedContext) { appendKeys(DefaultLambdaFields.values(extractedContext)); appendKey("coldStart", isColdStart() ? "true" : "false"); appendKey("service", serviceName()); @@ -108,7 +114,7 @@ public Object around(ProceedingJoinPoint pjp, Object proceed = pjp.proceed(proceedArgs); - if(logging.clearState()) { + if (logging.clearState()) { ThreadContext.clearMap(); } @@ -116,12 +122,6 @@ public Object around(ProceedingJoinPoint pjp, return proceed; } - private static void resetLogLevels(Level logLevel) { - LoggerContext ctx = (LoggerContext) LogManager.getContext(false); - Configurator.setAllLevels(LogManager.getRootLogger().getName(), logLevel); - ctx.updateLoggers(); - } - private void setLogLevelBasedOnSamplingRate(final ProceedingJoinPoint pjp, final Logging logging) { double samplingRate = samplingRate(logging); @@ -129,7 +129,8 @@ private void setLogLevelBasedOnSamplingRate(final ProceedingJoinPoint pjp, if (isHandlerMethod(pjp)) { if (samplingRate < 0 || samplingRate > 1) { - LOG.debug("Skipping sampling rate configuration because of invalid value. Sampling rate: {}", samplingRate); + LOG.debug("Skipping sampling rate configuration because of invalid value. Sampling rate: {}", + samplingRate); return; } diff --git a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/PowertoolsResolver.java b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/PowertoolsResolver.java index c392e2ed9..c7b7c5d53 100644 --- a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/PowertoolsResolver.java +++ b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/PowertoolsResolver.java @@ -1,3 +1,17 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.logging.internal; import org.apache.logging.log4j.core.LogEvent; @@ -25,12 +39,13 @@ public void resolve(LogEvent logEvent, JsonWriter jsonWriter) { // Inject all the context information. ReadOnlyStringMap contextData = logEvent.getContextData(); - contextData.forEach((key, value) -> { + contextData.forEach((key, value) -> + { jsonWriter.writeSeparator(); jsonWriter.writeString(key); stringBuilder.append(':'); jsonWriter.writeValue(value); - }); + }); } }; } diff --git a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/PowertoolsResolverFactory.java b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/PowertoolsResolverFactory.java index 5683c9688..7d688f469 100644 --- a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/PowertoolsResolverFactory.java +++ b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/PowertoolsResolverFactory.java @@ -1,3 +1,17 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.logging.internal; import org.apache.logging.log4j.core.LogEvent; @@ -14,7 +28,8 @@ public final class PowertoolsResolverFactory implements EventResolverFactory { private static final PowertoolsResolverFactory INSTANCE = new PowertoolsResolverFactory(); - private PowertoolsResolverFactory() {} + private PowertoolsResolverFactory() { + } @PluginFactory public static PowertoolsResolverFactory getInstance() { diff --git a/powertools-logging/src/test/java/org/apache/logging/log4j/core/layout/LambdaJsonLayoutTest.java b/powertools-logging/src/test/java/org/apache/logging/log4j/core/layout/LambdaJsonLayoutTest.java index 60f0806a9..47b495da3 100644 --- a/powertools-logging/src/test/java/org/apache/logging/log4j/core/layout/LambdaJsonLayoutTest.java +++ b/powertools-logging/src/test/java/org/apache/logging/log4j/core/layout/LambdaJsonLayoutTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,20 +11,20 @@ * limitations under the License. * */ + package org.apache.logging.log4j.core.layout; +import static java.util.Collections.emptyMap; +import static org.apache.commons.lang3.reflect.FieldUtils.writeStaticField; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.openMocks; + import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import org.apache.logging.log4j.Level; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.Mock; -import software.amazon.lambda.powertools.logging.handlers.PowerLogToolEnabled; -import software.amazon.lambda.powertools.logging.handlers.PowerLogToolSamplingEnabled; -import software.amazon.lambda.powertools.logging.internal.LambdaLoggingAspect; - import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -33,13 +33,13 @@ import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.util.Map; - -import static java.util.Collections.emptyMap; -import static org.apache.commons.lang3.reflect.FieldUtils.writeStaticField; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.fail; -import static org.mockito.Mockito.when; -import static org.mockito.MockitoAnnotations.openMocks; +import org.apache.logging.log4j.Level; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import software.amazon.lambda.powertools.logging.handlers.PowerLogToolEnabled; +import software.amazon.lambda.powertools.logging.handlers.PowerLogToolSamplingEnabled; +import software.amazon.lambda.powertools.logging.internal.LambdaLoggingAspect; class LambdaJsonLayoutTest { @@ -74,22 +74,24 @@ void shouldLogInStructuredFormat() throws IOException { } @Test - void shouldModifyLogLevelBasedOnEnvVariable() throws IllegalAccessException, IOException, NoSuchMethodException, InvocationTargetException { + void shouldModifyLogLevelBasedOnEnvVariable() + throws IllegalAccessException, IOException, NoSuchMethodException, InvocationTargetException { resetLogLevel(Level.DEBUG); handler.handleRequest("test", context); assertThat(Files.lines(Paths.get("target/logfile.json"))) .hasSize(2) - .satisfies(line -> { - assertThat(parseToMap(line.get(0))) - .containsEntry("level", "INFO") - .containsEntry("message", "Test event"); - - assertThat(parseToMap(line.get(1))) - .containsEntry("level", "DEBUG") - .containsEntry("message", "Test debug event"); - }); + .satisfies(line -> + { + assertThat(parseToMap(line.get(0))) + .containsEntry("level", "INFO") + .containsEntry("message", "Test event"); + + assertThat(parseToMap(line.get(1))) + .containsEntry("level", "DEBUG") + .containsEntry("message", "Test debug event"); + }); } @Test @@ -100,22 +102,24 @@ void shouldModifyLogLevelBasedOnSamplingRule() throws IOException { assertThat(Files.lines(Paths.get("target/logfile.json"))) .hasSize(3) - .satisfies(line -> { - assertThat(parseToMap(line.get(0))) - .containsEntry("level", "DEBUG") - .containsEntry("loggerName", LambdaLoggingAspect.class.getCanonicalName()); - - assertThat(parseToMap(line.get(1))) - .containsEntry("level", "INFO") - .containsEntry("message", "Test event"); - - assertThat(parseToMap(line.get(2))) - .containsEntry("level", "DEBUG") - .containsEntry("message", "Test debug event"); - }); + .satisfies(line -> + { + assertThat(parseToMap(line.get(0))) + .containsEntry("level", "DEBUG") + .containsEntry("loggerName", LambdaLoggingAspect.class.getCanonicalName()); + + assertThat(parseToMap(line.get(1))) + .containsEntry("level", "INFO") + .containsEntry("message", "Test event"); + + assertThat(parseToMap(line.get(2))) + .containsEntry("level", "DEBUG") + .containsEntry("message", "Test debug event"); + }); } - private void resetLogLevel(Level level) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { + private void resetLogLevel(Level level) + throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { Method resetLogLevels = LambdaLoggingAspect.class.getDeclaredMethod("resetLogLevels", Level.class); resetLogLevels.setAccessible(true); resetLogLevels.invoke(null, level); diff --git a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/LoggingUtilsTest.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/LoggingUtilsTest.java index eee8ace05..8889fb93c 100644 --- a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/LoggingUtilsTest.java +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/LoggingUtilsTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,16 +11,16 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.logging; -import org.apache.logging.log4j.ThreadContext; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; +import static org.assertj.core.api.Assertions.assertThat; import java.util.HashMap; import java.util.Map; - -import static org.assertj.core.api.Assertions.assertThat; +import org.apache.logging.log4j.ThreadContext; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; class LoggingUtilsTest { diff --git a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerLogToolApiGatewayHttpApiCorrelationId.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerLogToolApiGatewayHttpApiCorrelationId.java index 4e40e0f97..54d87d5cb 100644 --- a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerLogToolApiGatewayHttpApiCorrelationId.java +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerLogToolApiGatewayHttpApiCorrelationId.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,8 +11,11 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.logging.handlers; +import static software.amazon.lambda.powertools.logging.CorrelationIdPathConstants.API_GATEWAY_HTTP; + import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPEvent; @@ -20,8 +23,6 @@ import org.apache.logging.log4j.Logger; import software.amazon.lambda.powertools.logging.Logging; -import static software.amazon.lambda.powertools.logging.CorrelationIdPathConstants.API_GATEWAY_HTTP; - public class PowerLogToolApiGatewayHttpApiCorrelationId implements RequestHandler { private final Logger LOG = LogManager.getLogger(PowerLogToolApiGatewayHttpApiCorrelationId.class); diff --git a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerLogToolApiGatewayRestApiCorrelationId.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerLogToolApiGatewayRestApiCorrelationId.java index e3cadaf84..2b6e5a8d4 100644 --- a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerLogToolApiGatewayRestApiCorrelationId.java +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerLogToolApiGatewayRestApiCorrelationId.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,8 +11,11 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.logging.handlers; +import static software.amazon.lambda.powertools.logging.CorrelationIdPathConstants.API_GATEWAY_REST; + import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; @@ -20,8 +23,6 @@ import org.apache.logging.log4j.Logger; import software.amazon.lambda.powertools.logging.Logging; -import static software.amazon.lambda.powertools.logging.CorrelationIdPathConstants.API_GATEWAY_REST; - public class PowerLogToolApiGatewayRestApiCorrelationId implements RequestHandler { private final Logger LOG = LogManager.getLogger(PowerLogToolApiGatewayRestApiCorrelationId.class); diff --git a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerLogToolEnabled.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerLogToolEnabled.java index e154bbcf3..df68ea14f 100644 --- a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerLogToolEnabled.java +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerLogToolEnabled.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.logging.handlers; import com.amazonaws.services.lambda.runtime.Context; diff --git a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerLogToolEnabledForStream.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerLogToolEnabledForStream.java index e2c2d66d0..83a370437 100644 --- a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerLogToolEnabledForStream.java +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerLogToolEnabledForStream.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,14 +11,14 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.logging.handlers; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestStreamHandler; -import software.amazon.lambda.powertools.logging.Logging; - import java.io.InputStream; import java.io.OutputStream; +import software.amazon.lambda.powertools.logging.Logging; public class PowerLogToolEnabledForStream implements RequestStreamHandler { diff --git a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerLogToolSamplingEnabled.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerLogToolSamplingEnabled.java index 9d3d68e2e..357520395 100644 --- a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerLogToolSamplingEnabled.java +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerLogToolSamplingEnabled.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.logging.handlers; import com.amazonaws.services.lambda.runtime.Context; diff --git a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerToolDisabled.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerToolDisabled.java index 0391a5177..48a2e3b81 100644 --- a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerToolDisabled.java +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerToolDisabled.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.logging.handlers; import com.amazonaws.services.lambda.runtime.Context; diff --git a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerToolDisabledForStream.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerToolDisabledForStream.java index 15b39c6c5..7f93145c7 100644 --- a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerToolDisabledForStream.java +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerToolDisabledForStream.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,11 +11,11 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.logging.handlers; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestStreamHandler; - import java.io.InputStream; import java.io.OutputStream; diff --git a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerToolLogEventEnabled.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerToolLogEventEnabled.java index 152eb284d..8a960fa87 100644 --- a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerToolLogEventEnabled.java +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerToolLogEventEnabled.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.logging.handlers; import com.amazonaws.services.lambda.runtime.Context; diff --git a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerToolLogEventEnabledForStream.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerToolLogEventEnabledForStream.java index 473042e6c..9de76586f 100644 --- a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerToolLogEventEnabledForStream.java +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerToolLogEventEnabledForStream.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,17 +11,17 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.logging.handlers; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestStreamHandler; import com.fasterxml.jackson.databind.ObjectMapper; -import software.amazon.lambda.powertools.logging.Logging; - import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Map; +import software.amazon.lambda.powertools.logging.Logging; public class PowerToolLogEventEnabledForStream implements RequestStreamHandler { diff --git a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerToolLogEventEnabledWithCustomMapper.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerToolLogEventEnabledWithCustomMapper.java index f1c2f62c8..838de1216 100644 --- a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerToolLogEventEnabledWithCustomMapper.java +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowerToolLogEventEnabledWithCustomMapper.java @@ -1,3 +1,17 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.logging.handlers; import com.amazonaws.services.lambda.runtime.Context; @@ -8,11 +22,10 @@ import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.ser.std.StdSerializer; +import java.io.IOException; import software.amazon.lambda.powertools.logging.Logging; import software.amazon.lambda.powertools.logging.LoggingUtils; -import java.io.IOException; - public class PowerToolLogEventEnabledWithCustomMapper implements RequestHandler { static { @@ -40,7 +53,8 @@ public S3EventNotificationSerializer(Class t) { } @Override - public void serialize(S3EventNotification o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { + public void serialize(S3EventNotification o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) + throws IOException { jsonGenerator.writeStartObject(); jsonGenerator.writeStringField("eventSource", o.getRecords().get(0).getEventSource()); jsonGenerator.writeEndObject(); diff --git a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogAlbCorrelationId.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogAlbCorrelationId.java index c06f8326e..a32e3e06e 100644 --- a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogAlbCorrelationId.java +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogAlbCorrelationId.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,8 +11,11 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.logging.handlers; +import static software.amazon.lambda.powertools.logging.CorrelationIdPathConstants.APPLICATION_LOAD_BALANCER; + import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerRequestEvent; @@ -20,8 +23,6 @@ import org.apache.logging.log4j.Logger; import software.amazon.lambda.powertools.logging.Logging; -import static software.amazon.lambda.powertools.logging.CorrelationIdPathConstants.APPLICATION_LOAD_BALANCER; - public class PowertoolsLogAlbCorrelationId implements RequestHandler { private final Logger LOG = LogManager.getLogger(PowertoolsLogAlbCorrelationId.class); diff --git a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogEnabledWithClearState.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogEnabledWithClearState.java index 8b7d4fcaa..f21d9f118 100644 --- a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogEnabledWithClearState.java +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogEnabledWithClearState.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.logging.handlers; import com.amazonaws.services.lambda.runtime.Context; @@ -21,8 +22,8 @@ import software.amazon.lambda.powertools.logging.LoggingUtils; public class PowertoolsLogEnabledWithClearState implements RequestHandler { - public static int COUNT = 1; private static final Logger LOG = LogManager.getLogger(PowertoolsLogEnabledWithClearState.class); + public static int COUNT = 1; @Override @Logging(clearState = true) diff --git a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogEventBridgeCorrelationId.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogEventBridgeCorrelationId.java index f03983aff..53e06cb2e 100644 --- a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogEventBridgeCorrelationId.java +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/handlers/PowertoolsLogEventBridgeCorrelationId.java @@ -11,19 +11,19 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.logging.handlers; +import static software.amazon.lambda.powertools.logging.CorrelationIdPathConstants.EVENT_BRIDGE; + import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestStreamHandler; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import software.amazon.lambda.powertools.logging.Logging; - import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; - -import static software.amazon.lambda.powertools.logging.CorrelationIdPathConstants.EVENT_BRIDGE; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import software.amazon.lambda.powertools.logging.Logging; public class PowertoolsLogEventBridgeCorrelationId implements RequestStreamHandler { diff --git a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/internal/LambdaLoggingAspectTest.java b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/internal/LambdaLoggingAspectTest.java index 8e7d2d3e6..b78710586 100644 --- a/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/internal/LambdaLoggingAspectTest.java +++ b/powertools-logging/src/test/java/software/amazon/lambda/powertools/logging/internal/LambdaLoggingAspectTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,8 +11,21 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.logging.internal; +import static java.util.Collections.emptyMap; +import static java.util.Collections.singletonList; +import static java.util.stream.Collectors.joining; +import static org.apache.commons.lang3.reflect.FieldUtils.writeStaticField; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.openMocks; +import static org.skyscreamer.jsonassert.JSONAssert.assertEquals; +import static software.amazon.lambda.powertools.core.internal.SystemWrapper.getenv; + import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.lambda.runtime.RequestStreamHandler; @@ -23,6 +36,21 @@ import com.amazonaws.services.lambda.runtime.tests.annotations.Event; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.nio.channels.FileChannel; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.ThreadContext; import org.json.JSONException; @@ -46,34 +74,6 @@ import software.amazon.lambda.powertools.logging.handlers.PowertoolsLogEnabledWithClearState; import software.amazon.lambda.powertools.logging.handlers.PowertoolsLogEventBridgeCorrelationId; -import java.io.BufferedReader; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStreamReader; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.nio.channels.FileChannel; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.nio.file.StandardOpenOption; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -import static java.util.Collections.emptyMap; -import static java.util.Collections.singletonList; -import static java.util.stream.Collectors.joining; -import static org.apache.commons.lang3.reflect.FieldUtils.writeStaticField; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.fail; -import static org.mockito.Mockito.mockStatic; -import static org.mockito.Mockito.when; -import static org.mockito.MockitoAnnotations.openMocks; -import static org.skyscreamer.jsonassert.JSONAssert.assertEquals; -import static software.amazon.lambda.powertools.core.internal.SystemWrapper.getenv; - class LambdaLoggingAspectTest { private static final int EXPECTED_CONTEXT_SIZE = 8; @@ -115,7 +115,8 @@ void shouldSetLambdaContextWhenEnabled() { void shouldSetLambdaContextForStreamHandlerWhenEnabled() throws IOException { requestStreamHandler = new PowerLogToolEnabledForStream(); - requestStreamHandler.handleRequest(new ByteArrayInputStream(new byte[]{}), new ByteArrayOutputStream(), context); + requestStreamHandler.handleRequest(new ByteArrayInputStream(new byte[] {}), new ByteArrayOutputStream(), + context); assertThat(ThreadContext.getImmutableContext()) .hasSize(EXPECTED_CONTEXT_SIZE) @@ -130,13 +131,15 @@ void shouldSetLambdaContextForStreamHandlerWhenEnabled() throws IOException { @Test void shouldSetColdStartFlag() throws IOException { - requestStreamHandler.handleRequest(new ByteArrayInputStream(new byte[]{}), new ByteArrayOutputStream(), context); + requestStreamHandler.handleRequest(new ByteArrayInputStream(new byte[] {}), new ByteArrayOutputStream(), + context); assertThat(ThreadContext.getImmutableContext()) .hasSize(EXPECTED_CONTEXT_SIZE) .containsEntry("coldStart", "true"); - requestStreamHandler.handleRequest(new ByteArrayInputStream(new byte[]{}), new ByteArrayOutputStream(), context); + requestStreamHandler.handleRequest(new ByteArrayInputStream(new byte[] {}), new ByteArrayOutputStream(), + context); assertThat(ThreadContext.getImmutableContext()) .hasSize(EXPECTED_CONTEXT_SIZE) @@ -184,7 +187,8 @@ void shouldLogEventForHandler() throws IOException, JSONException { String event = (String) log.get("message"); - String expectEvent = new BufferedReader(new InputStreamReader(this.getClass().getResourceAsStream("/s3EventNotification.json"))) + String expectEvent = new BufferedReader( + new InputStreamReader(this.getClass().getResourceAsStream("/s3EventNotification.json"))) .lines().collect(joining("\n")); assertEquals(expectEvent, event, false); @@ -201,7 +205,8 @@ void shouldLogEventForHandlerWithOverriddenObjectMapper() throws IOException, JS String event = (String) log.get("message"); - String expectEvent = new BufferedReader(new InputStreamReader(this.getClass().getResourceAsStream("/customizedLogEvent.json"))) + String expectEvent = new BufferedReader( + new InputStreamReader(this.getClass().getResourceAsStream("/customizedLogEvent.json"))) .lines().collect(joining("\n")); assertEquals(expectEvent, event, false); @@ -213,7 +218,8 @@ void shouldLogEventForStreamAndLambdaStreamIsValid() throws IOException, JSONExc ByteArrayOutputStream output = new ByteArrayOutputStream(); S3EventNotification s3EventNotification = s3EventNotification(); - requestStreamHandler.handleRequest(new ByteArrayInputStream(new ObjectMapper().writeValueAsBytes(s3EventNotification)), output, context); + requestStreamHandler.handleRequest( + new ByteArrayInputStream(new ObjectMapper().writeValueAsBytes(s3EventNotification)), output, context); assertThat(new String(output.toByteArray(), StandardCharsets.UTF_8)) .isNotEmpty(); @@ -222,7 +228,8 @@ void shouldLogEventForStreamAndLambdaStreamIsValid() throws IOException, JSONExc String event = (String) log.get("message"); - String expectEvent = new BufferedReader(new InputStreamReader(this.getClass().getResourceAsStream("/s3EventNotification.json"))) + String expectEvent = new BufferedReader( + new InputStreamReader(this.getClass().getResourceAsStream("/s3EventNotification.json"))) .lines().collect(joining("\n")); assertEquals(expectEvent, event, false); @@ -243,7 +250,8 @@ void shouldLogxRayTraceIdEnvVarSet() { String xRayTraceId = "1-5759e988-bd862e3fe1be46a994272793"; try (MockedStatic mocked = mockStatic(SystemWrapper.class)) { - mocked.when(() -> getenv("_X_AMZN_TRACE_ID")).thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1\""); + mocked.when(() -> getenv("_X_AMZN_TRACE_ID")) + .thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1\""); requestHandler.handleRequest(new Object(), context); @@ -328,7 +336,8 @@ private void setupContext() { when(context.getAwsRequestId()).thenReturn("RequestId"); } - private void resetLogLevel(Level level) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { + private void resetLogLevel(Level level) + throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { Method resetLogLevels = LambdaLoggingAspect.class.getDeclaredMethod("resetLogLevels", Level.class); resetLogLevels.setAccessible(true); resetLogLevels.invoke(null, level); @@ -336,25 +345,27 @@ private void resetLogLevel(Level level) throws NoSuchMethodException, IllegalAcc } private S3EventNotification s3EventNotification() { - S3EventNotification.S3EventNotificationRecord record = new S3EventNotification.S3EventNotificationRecord("us-west-2", - "ObjectCreated:Put", - "aws:s3", - null, - "2.1", - new S3EventNotification.RequestParametersEntity("127.0.0.1"), - new S3EventNotification.ResponseElementsEntity("C3D13FE58DE4C810", "FMyUVURIY8/IgAtTv8xRjskZQpcIZ9KG4V5Wp6S7S/JRWeUWerMUE5JgHvANOjpD"), - new S3EventNotification.S3Entity("testConfigRule", - new S3EventNotification.S3BucketEntity("mybucket", - new S3EventNotification.UserIdentityEntity("A3NL1KOZZKExample"), - "arn:aws:s3:::mybucket"), - new S3EventNotification.S3ObjectEntity("HappyFace.jpg", - 1024L, - "d41d8cd98f00b204e9800998ecf8427e", - "096fKKXTRTtl3on89fVO.nfljtsv6qko", - "0055AED6DCD90281E5"), - "1.0"), - new S3EventNotification.UserIdentityEntity("AIDAJDPLRKLG7UEXAMPLE") - ); + S3EventNotification.S3EventNotificationRecord record = + new S3EventNotification.S3EventNotificationRecord("us-west-2", + "ObjectCreated:Put", + "aws:s3", + null, + "2.1", + new S3EventNotification.RequestParametersEntity("127.0.0.1"), + new S3EventNotification.ResponseElementsEntity("C3D13FE58DE4C810", + "FMyUVURIY8/IgAtTv8xRjskZQpcIZ9KG4V5Wp6S7S/JRWeUWerMUE5JgHvANOjpD"), + new S3EventNotification.S3Entity("testConfigRule", + new S3EventNotification.S3BucketEntity("mybucket", + new S3EventNotification.UserIdentityEntity("A3NL1KOZZKExample"), + "arn:aws:s3:::mybucket"), + new S3EventNotification.S3ObjectEntity("HappyFace.jpg", + 1024L, + "d41d8cd98f00b204e9800998ecf8427e", + "096fKKXTRTtl3on89fVO.nfljtsv6qko", + "0055AED6DCD90281E5"), + "1.0"), + new S3EventNotification.UserIdentityEntity("AIDAJDPLRKLG7UEXAMPLE") + ); return new S3EventNotification(singletonList(record)); } diff --git a/powertools-metrics/pom.xml b/powertools-metrics/pom.xml index ed2d2f815..7b2e99045 100644 --- a/powertools-metrics/pom.xml +++ b/powertools-metrics/pom.xml @@ -1,4 +1,18 @@ + + @@ -112,4 +126,12 @@ + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + + \ No newline at end of file diff --git a/powertools-metrics/src/main/java/software/amazon/cloudwatchlogs/emf/model/MetricsLoggerHelper.java b/powertools-metrics/src/main/java/software/amazon/cloudwatchlogs/emf/model/MetricsLoggerHelper.java index f59658e9c..e2d886fe5 100644 --- a/powertools-metrics/src/main/java/software/amazon/cloudwatchlogs/emf/model/MetricsLoggerHelper.java +++ b/powertools-metrics/src/main/java/software/amazon/cloudwatchlogs/emf/model/MetricsLoggerHelper.java @@ -1,9 +1,23 @@ -package software.amazon.cloudwatchlogs.emf.model; +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ -import java.lang.reflect.Field; +package software.amazon.cloudwatchlogs.emf.model; import static software.amazon.lambda.powertools.metrics.MetricsUtils.metricsLogger; +import java.lang.reflect.Field; + public final class MetricsLoggerHelper { private MetricsLoggerHelper() { } diff --git a/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/Metrics.java b/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/Metrics.java index 2a4f4d472..fb92c900d 100644 --- a/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/Metrics.java +++ b/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/Metrics.java @@ -1,3 +1,17 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.metrics; import java.lang.annotation.ElementType; @@ -44,7 +58,10 @@ @Target(ElementType.METHOD) public @interface Metrics { String namespace() default ""; + String service() default ""; + boolean captureColdStart() default false; + boolean raiseOnEmptyMetrics() default false; } diff --git a/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/MetricsUtils.java b/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/MetricsUtils.java index 8705c2da4..1da100f26 100644 --- a/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/MetricsUtils.java +++ b/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/MetricsUtils.java @@ -1,9 +1,28 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.metrics; +import static java.util.Objects.requireNonNull; +import static java.util.Optional.ofNullable; +import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.getXrayTraceId; +import static software.amazon.lambda.powertools.metrics.internal.LambdaMetricsAspect.REQUEST_ID_PROPERTY; +import static software.amazon.lambda.powertools.metrics.internal.LambdaMetricsAspect.TRACE_ID_PROPERTY; + import java.util.Arrays; import java.util.Optional; import java.util.function.Consumer; - import software.amazon.cloudwatchlogs.emf.config.SystemWrapper; import software.amazon.cloudwatchlogs.emf.environment.EnvironmentProvider; import software.amazon.cloudwatchlogs.emf.logger.MetricsLogger; @@ -12,16 +31,10 @@ import software.amazon.cloudwatchlogs.emf.model.MetricsLoggerHelper; import software.amazon.cloudwatchlogs.emf.model.Unit; -import static java.util.Objects.requireNonNull; -import static java.util.Optional.ofNullable; -import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.getXrayTraceId; -import static software.amazon.lambda.powertools.metrics.internal.LambdaMetricsAspect.REQUEST_ID_PROPERTY; -import static software.amazon.lambda.powertools.metrics.internal.LambdaMetricsAspect.TRACE_ID_PROPERTY; - /** * A class used to retrieve the instance of the {@code MetricsLogger} used by * {@code Metrics}. - * + *

* {@see Metrics} */ public final class MetricsUtils { @@ -43,6 +56,7 @@ public static MetricsLogger metricsLogger() { /** * Configure default dimension to be used by logger. * By default, @{@link Metrics} annotation captures configured service as a dimension Service + * * @param dimensionSets Default value of dimensions set for logger */ public static void defaultDimensions(final DimensionSet... dimensionSets) { @@ -52,15 +66,15 @@ public static void defaultDimensions(final DimensionSet... dimensionSets) { /** * Configure default dimension to be used by logger. * By default, @{@link Metrics} annotation captures configured service as a dimension Service + * * @param dimensionSet Default value of dimension set for logger * @deprecated use {@link #defaultDimensions(DimensionSet...)} instead - * */ @Deprecated public static void defaultDimensionSet(final DimensionSet dimensionSet) { requireNonNull(dimensionSet, "Null dimension set not allowed"); - if(dimensionSet.getDimensionKeys().size() > 0) { + if (dimensionSet.getDimensionKeys().size() > 0) { defaultDimensions(dimensionSet); } } @@ -81,10 +95,11 @@ public static void withSingleMetric(final String name, final double value, final Unit unit, final Consumer logger) { - withMetricsLogger(metricsLogger -> { - metricsLogger.putMetric(name, value, unit); - logger.accept(metricsLogger); - }); + withMetricsLogger(metricsLogger -> + { + metricsLogger.putMetric(name, value, unit); + logger.accept(metricsLogger); + }); } /** @@ -92,22 +107,23 @@ public static void withSingleMetric(final String name, * It by default captures function_request_id as property if used together with {@link Metrics} annotation. It will also * capture xray_trace_id as property if tracing is enabled. * - * @param name the name of the metric - * @param value the value of the metric - * @param unit the unit type of the metric + * @param name the name of the metric + * @param value the value of the metric + * @param unit the unit type of the metric * @param namespace the namespace associated with the metric - * @param logger the MetricsLogger + * @param logger the MetricsLogger */ public static void withSingleMetric(final String name, final double value, final Unit unit, final String namespace, final Consumer logger) { - withMetricsLogger(metricsLogger -> { - metricsLogger.setNamespace(namespace); - metricsLogger.putMetric(name, value, unit); - logger.accept(metricsLogger); - }); + withMetricsLogger(metricsLogger -> + { + metricsLogger.setNamespace(namespace); + metricsLogger.putMetric(name, value, unit); + logger.accept(metricsLogger); + }); } /** @@ -137,7 +153,6 @@ public static void withMetricsLogger(final Consumer logger) { * capture xray_trace_id as property if tracing is enabled. * * @param logger the MetricsLogger - * * @deprecated use {@link MetricsUtils#withMetricsLogger} instead */ @Deprecated @@ -164,7 +179,7 @@ private static void captureRequestAndTraceId(MetricsLogger metricsLogger) { private static String defaultNameSpace() { MetricsContext context = MetricsLoggerHelper.metricsContext(); return "aws-embedded-metrics".equals(context.getNamespace()) ? - SystemWrapper.getenv("POWERTOOLS_METRICS_NAMESPACE"): context.getNamespace(); + SystemWrapper.getenv("POWERTOOLS_METRICS_NAMESPACE") : context.getNamespace(); } private static Optional awsRequestId() { diff --git a/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/ValidationException.java b/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/ValidationException.java index 2da9a539c..a553abbbd 100644 --- a/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/ValidationException.java +++ b/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/ValidationException.java @@ -1,3 +1,17 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.metrics; public class ValidationException extends RuntimeException { diff --git a/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/internal/LambdaMetricsAspect.java b/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/internal/LambdaMetricsAspect.java index 927359fc5..8ca069b01 100644 --- a/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/internal/LambdaMetricsAspect.java +++ b/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/internal/LambdaMetricsAspect.java @@ -1,8 +1,31 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.metrics.internal; -import java.lang.reflect.Field; +import static software.amazon.cloudwatchlogs.emf.model.MetricsLoggerHelper.dimensionsCount; +import static software.amazon.cloudwatchlogs.emf.model.MetricsLoggerHelper.hasNoMetrics; +import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.coldStartDone; +import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.extractContext; +import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.isColdStart; +import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.isHandlerMethod; +import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.serviceName; +import static software.amazon.lambda.powertools.metrics.MetricsUtils.hasDefaultDimension; +import static software.amazon.lambda.powertools.metrics.MetricsUtils.metricsLogger; import com.amazonaws.services.lambda.runtime.Context; +import java.lang.reflect.Field; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; @@ -16,21 +39,33 @@ import software.amazon.lambda.powertools.metrics.MetricsUtils; import software.amazon.lambda.powertools.metrics.ValidationException; -import static software.amazon.cloudwatchlogs.emf.model.MetricsLoggerHelper.dimensionsCount; -import static software.amazon.cloudwatchlogs.emf.model.MetricsLoggerHelper.hasNoMetrics; -import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.coldStartDone; -import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.extractContext; -import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.isColdStart; -import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.isHandlerMethod; -import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.serviceName; -import static software.amazon.lambda.powertools.metrics.MetricsUtils.hasDefaultDimension; -import static software.amazon.lambda.powertools.metrics.MetricsUtils.metricsLogger; - @Aspect public class LambdaMetricsAspect { - private static final String NAMESPACE = System.getenv("POWERTOOLS_METRICS_NAMESPACE"); public static final String TRACE_ID_PROPERTY = "xray_trace_id"; public static final String REQUEST_ID_PROPERTY = "function_request_id"; + private static final String NAMESPACE = System.getenv("POWERTOOLS_METRICS_NAMESPACE"); + + private static String service(Metrics metrics) { + return !"".equals(metrics.service()) ? metrics.service() : serviceName(); + } + + // This can be simplified after this issues https://github.com/awslabs/aws-embedded-metrics-java/issues/35 is fixed + public static void refreshMetricsContext(Metrics metrics) { + try { + Field f = metricsLogger().getClass().getDeclaredField("context"); + f.setAccessible(true); + MetricsContext context = new MetricsContext(); + + DimensionSet[] defaultDimensions = hasDefaultDimension() ? MetricsUtils.getDefaultDimensions() + : new DimensionSet[] {DimensionSet.of("Service", service(metrics))}; + + context.setDimensions(defaultDimensions); + + f.set(metricsLogger(), context); + } catch (NoSuchFieldException | IllegalAccessException e) { + throw new RuntimeException(e); + } + } @SuppressWarnings({"EmptyMethod"}) @Pointcut("@annotation(metrics)") @@ -52,8 +87,9 @@ public Object around(ProceedingJoinPoint pjp, Context extractedContext = extractContext(pjp); - if( null != extractedContext) { - coldStartSingleMetricIfApplicable(extractedContext.getAwsRequestId(), extractedContext.getFunctionName(), metrics); + if (null != extractedContext) { + coldStartSingleMetricIfApplicable(extractedContext.getAwsRequestId(), + extractedContext.getFunctionName(), metrics); logger.putProperty(REQUEST_ID_PROPERTY, extractedContext.getAwsRequestId()); } @@ -79,12 +115,12 @@ private void coldStartSingleMetricIfApplicable(final String awsRequestId, final Metrics metrics) { if (metrics.captureColdStart() && isColdStart()) { - MetricsLogger metricsLogger = new MetricsLogger(); - metricsLogger.setNamespace(namespace(metrics)); - metricsLogger.putMetric("ColdStart", 1, Unit.COUNT); - metricsLogger.setDimensions(DimensionSet.of("Service", service(metrics), "FunctionName", functionName)); - metricsLogger.putProperty(REQUEST_ID_PROPERTY, awsRequestId); - metricsLogger.flush(); + MetricsLogger metricsLogger = new MetricsLogger(); + metricsLogger.setNamespace(namespace(metrics)); + metricsLogger.putMetric("ColdStart", 1, Unit.COUNT); + metricsLogger.setDimensions(DimensionSet.of("Service", service(metrics), "FunctionName", functionName)); + metricsLogger.putProperty(REQUEST_ID_PROPERTY, awsRequestId); + metricsLogger.flush(); } } @@ -104,34 +140,12 @@ private String namespace(Metrics metrics) { return !"".equals(metrics.namespace()) ? metrics.namespace() : NAMESPACE; } - private static String service(Metrics metrics) { - return !"".equals(metrics.service()) ? metrics.service() : serviceName(); - } - private void validateMetricsAndRefreshOnFailure(Metrics metrics) { try { validateBeforeFlushingMetrics(metrics); - } catch (ValidationException e){ + } catch (ValidationException e) { refreshMetricsContext(metrics); throw e; } } - - // This can be simplified after this issues https://github.com/awslabs/aws-embedded-metrics-java/issues/35 is fixed - public static void refreshMetricsContext(Metrics metrics) { - try { - Field f = metricsLogger().getClass().getDeclaredField("context"); - f.setAccessible(true); - MetricsContext context = new MetricsContext(); - - DimensionSet[] defaultDimensions = hasDefaultDimension() ? MetricsUtils.getDefaultDimensions() - : new DimensionSet[]{DimensionSet.of("Service", service(metrics))}; - - context.setDimensions(defaultDimensions); - - f.set(metricsLogger(), context); - } catch (NoSuchFieldException | IllegalAccessException e) { - throw new RuntimeException(e); - } - } } diff --git a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/MetricsLoggerTest.java b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/MetricsLoggerTest.java index 6ebf30e04..da4162ea0 100644 --- a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/MetricsLoggerTest.java +++ b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/MetricsLoggerTest.java @@ -1,12 +1,31 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.metrics; +import static java.util.Collections.emptyMap; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatNullPointerException; +import static org.mockito.Mockito.mockStatic; +import static software.amazon.lambda.powertools.core.internal.SystemWrapper.getenv; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; import java.io.ByteArrayOutputStream; import java.io.PrintStream; import java.util.Map; import java.util.function.Consumer; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; @@ -17,18 +36,19 @@ import software.amazon.cloudwatchlogs.emf.model.DimensionSet; import software.amazon.cloudwatchlogs.emf.model.Unit; -import static java.util.Collections.emptyMap; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatNullPointerException; -import static org.mockito.Mockito.mockStatic; -import static software.amazon.lambda.powertools.core.internal.SystemWrapper.getenv; - class MetricsLoggerTest { private final ByteArrayOutputStream out = new ByteArrayOutputStream(); private final PrintStream originalOut = System.out; private final ObjectMapper mapper = new ObjectMapper(); + @BeforeAll + static void beforeAll() { + try (MockedStatic mocked = mockStatic(SystemWrapper.class)) { + mocked.when(() -> SystemWrapper.getenv("AWS_EMF_ENVIRONMENT")).thenReturn("Lambda"); + } + } + @BeforeEach void setUp() { System.setOut(new PrintStream(out)); @@ -39,88 +59,92 @@ void tearDown() { System.setOut(originalOut); } - @BeforeAll - static void beforeAll() { - try (MockedStatic mocked = mockStatic(SystemWrapper.class)) { - mocked.when(() -> SystemWrapper.getenv("AWS_EMF_ENVIRONMENT")).thenReturn("Lambda"); - } - } - @Test void singleMetricsCaptureUtilityWithDefaultDimension() { try (MockedStatic mocked = mockStatic(SystemWrapper.class); - MockedStatic internalWrapper = mockStatic(software.amazon.lambda.powertools.core.internal.SystemWrapper.class)) { + MockedStatic internalWrapper = mockStatic( + software.amazon.lambda.powertools.core.internal.SystemWrapper.class)) { mocked.when(() -> SystemWrapper.getenv("AWS_EMF_ENVIRONMENT")).thenReturn("Lambda"); - internalWrapper.when(() -> getenv("_X_AMZN_TRACE_ID")).thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1\""); + internalWrapper.when(() -> getenv("_X_AMZN_TRACE_ID")) + .thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1\""); MetricsUtils.defaultDimensions(DimensionSet.of("Service", "Booking")); MetricsUtils.withSingleMetric("Metric1", 1, Unit.COUNT, "test", - metricsLogger -> {}); + metricsLogger -> + { + }); assertThat(out.toString()) - .satisfies(s -> { - Map logAsJson = readAsJson(s); - - assertThat(logAsJson) - .containsEntry("Metric1", 1.0) - .containsEntry("Service", "Booking") - .containsKey("_aws") - .containsEntry("xray_trace_id", "1-5759e988-bd862e3fe1be46a994272793"); - }); + .satisfies(s -> + { + Map logAsJson = readAsJson(s); + + assertThat(logAsJson) + .containsEntry("Metric1", 1.0) + .containsEntry("Service", "Booking") + .containsKey("_aws") + .containsEntry("xray_trace_id", "1-5759e988-bd862e3fe1be46a994272793"); + }); } } @Test void singleMetricsCaptureUtility() { try (MockedStatic mocked = mockStatic(SystemWrapper.class); - MockedStatic internalWrapper = mockStatic(software.amazon.lambda.powertools.core.internal.SystemWrapper.class)) { + MockedStatic internalWrapper = mockStatic( + software.amazon.lambda.powertools.core.internal.SystemWrapper.class)) { mocked.when(() -> SystemWrapper.getenv("AWS_EMF_ENVIRONMENT")).thenReturn("Lambda"); - internalWrapper.when(() -> getenv("_X_AMZN_TRACE_ID")).thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1\""); + internalWrapper.when(() -> getenv("_X_AMZN_TRACE_ID")) + .thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1\""); MetricsUtils.withSingleMetric("Metric1", 1, Unit.COUNT, "test", metricsLogger -> metricsLogger.setDimensions(DimensionSet.of("Dimension1", "Value1"))); assertThat(out.toString()) - .satisfies(s -> { - Map logAsJson = readAsJson(s); - - assertThat(logAsJson) - .containsEntry("Metric1", 1.0) - .containsEntry("Dimension1", "Value1") - .containsKey("_aws") - .containsEntry("xray_trace_id", "1-5759e988-bd862e3fe1be46a994272793"); - }); + .satisfies(s -> + { + Map logAsJson = readAsJson(s); + + assertThat(logAsJson) + .containsEntry("Metric1", 1.0) + .containsEntry("Dimension1", "Value1") + .containsKey("_aws") + .containsEntry("xray_trace_id", "1-5759e988-bd862e3fe1be46a994272793"); + }); } } @Test void singleMetricsCaptureUtilityWithDefaultNameSpace() { try (MockedStatic mocked = mockStatic(SystemWrapper.class); - MockedStatic internalWrapper = mockStatic(software.amazon.lambda.powertools.core.internal.SystemWrapper.class)) { + MockedStatic internalWrapper = mockStatic( + software.amazon.lambda.powertools.core.internal.SystemWrapper.class)) { mocked.when(() -> SystemWrapper.getenv("AWS_EMF_ENVIRONMENT")).thenReturn("Lambda"); mocked.when(() -> SystemWrapper.getenv("POWERTOOLS_METRICS_NAMESPACE")).thenReturn("GlobalName"); - internalWrapper.when(() -> getenv("_X_AMZN_TRACE_ID")).thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1\""); + internalWrapper.when(() -> getenv("_X_AMZN_TRACE_ID")) + .thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1\""); MetricsUtils.withSingleMetric("Metric1", 1, Unit.COUNT, metricsLogger -> metricsLogger.setDimensions(DimensionSet.of("Dimension1", "Value1"))); assertThat(out.toString()) - .satisfies(s -> { - Map logAsJson = readAsJson(s); - - assertThat(logAsJson) - .containsEntry("Metric1", 1.0) - .containsEntry("Dimension1", "Value1") - .containsKey("_aws") - .containsEntry("xray_trace_id", "1-5759e988-bd862e3fe1be46a994272793"); - - Map aws = (Map) logAsJson.get("_aws"); - - assertThat(aws.get("CloudWatchMetrics")) - .asString() - .contains("Namespace=GlobalName"); - }); + .satisfies(s -> + { + Map logAsJson = readAsJson(s); + + assertThat(logAsJson) + .containsEntry("Metric1", 1.0) + .containsEntry("Dimension1", "Value1") + .containsKey("_aws") + .containsEntry("xray_trace_id", "1-5759e988-bd862e3fe1be46a994272793"); + + Map aws = (Map) logAsJson.get("_aws"); + + assertThat(aws.get("CloudWatchMetrics")) + .asString() + .contains("Namespace=GlobalName"); + }); } } @@ -143,32 +167,36 @@ void shouldThrowExceptionWhenDefaultDimensionIsNull() { private void testLogger(Consumer> methodToTest) { try (MockedStatic mocked = mockStatic(SystemWrapper.class); - MockedStatic internalWrapper = mockStatic(software.amazon.lambda.powertools.core.internal.SystemWrapper.class)) { + MockedStatic internalWrapper = mockStatic( + software.amazon.lambda.powertools.core.internal.SystemWrapper.class)) { mocked.when(() -> SystemWrapper.getenv("AWS_EMF_ENVIRONMENT")).thenReturn("Lambda"); mocked.when(() -> SystemWrapper.getenv("POWERTOOLS_METRICS_NAMESPACE")).thenReturn("GlobalName"); - internalWrapper.when(() -> getenv("_X_AMZN_TRACE_ID")).thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1\""); + internalWrapper.when(() -> getenv("_X_AMZN_TRACE_ID")) + .thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1\""); - methodToTest.accept(metricsLogger -> { - metricsLogger.setDimensions(DimensionSet.of("Dimension1", "Value1")); - metricsLogger.putMetric("Metric1", 1, Unit.COUNT); - }); + methodToTest.accept(metricsLogger -> + { + metricsLogger.setDimensions(DimensionSet.of("Dimension1", "Value1")); + metricsLogger.putMetric("Metric1", 1, Unit.COUNT); + }); assertThat(out.toString()) - .satisfies(s -> { - Map logAsJson = readAsJson(s); - - assertThat(logAsJson) - .containsEntry("Metric1", 1.0) - .containsEntry("Dimension1", "Value1") - .containsKey("_aws") - .containsEntry("xray_trace_id", "1-5759e988-bd862e3fe1be46a994272793"); - - Map aws = (Map) logAsJson.get("_aws"); - - assertThat(aws.get("CloudWatchMetrics")) - .asString() - .contains("Namespace=GlobalName"); - }); + .satisfies(s -> + { + Map logAsJson = readAsJson(s); + + assertThat(logAsJson) + .containsEntry("Metric1", 1.0) + .containsEntry("Dimension1", "Value1") + .containsKey("_aws") + .containsEntry("xray_trace_id", "1-5759e988-bd862e3fe1be46a994272793"); + + Map aws = (Map) logAsJson.get("_aws"); + + assertThat(aws.get("CloudWatchMetrics")) + .asString() + .contains("Namespace=GlobalName"); + }); } } diff --git a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsColdStartEnabledHandler.java b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsColdStartEnabledHandler.java index a722bd689..e3a0fa22e 100644 --- a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsColdStartEnabledHandler.java +++ b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsColdStartEnabledHandler.java @@ -1,13 +1,27 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.metrics.handlers; +import static software.amazon.lambda.powertools.metrics.MetricsUtils.metricsLogger; + import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import software.amazon.cloudwatchlogs.emf.logger.MetricsLogger; import software.amazon.cloudwatchlogs.emf.model.Unit; import software.amazon.lambda.powertools.metrics.Metrics; -import static software.amazon.lambda.powertools.metrics.MetricsUtils.metricsLogger; - public class PowertoolsMetricsColdStartEnabledHandler implements RequestHandler { @Override diff --git a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsEnabledDefaultDimensionHandler.java b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsEnabledDefaultDimensionHandler.java index f66269546..5d7fb7120 100644 --- a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsEnabledDefaultDimensionHandler.java +++ b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsEnabledDefaultDimensionHandler.java @@ -1,5 +1,23 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.metrics.handlers; +import static software.amazon.lambda.powertools.metrics.MetricsUtils.defaultDimensions; +import static software.amazon.lambda.powertools.metrics.MetricsUtils.metricsLogger; +import static software.amazon.lambda.powertools.metrics.MetricsUtils.withSingleMetric; + import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import software.amazon.cloudwatchlogs.emf.logger.MetricsLogger; @@ -7,10 +25,6 @@ import software.amazon.cloudwatchlogs.emf.model.Unit; import software.amazon.lambda.powertools.metrics.Metrics; -import static software.amazon.lambda.powertools.metrics.MetricsUtils.defaultDimensions; -import static software.amazon.lambda.powertools.metrics.MetricsUtils.metricsLogger; -import static software.amazon.lambda.powertools.metrics.MetricsUtils.withSingleMetric; - public class PowertoolsMetricsEnabledDefaultDimensionHandler implements RequestHandler { static { @@ -23,7 +37,9 @@ public Object handleRequest(Object input, Context context) { MetricsLogger metricsLogger = metricsLogger(); metricsLogger.putMetric("Metric1", 1, Unit.BYTES); - withSingleMetric("Metric2", 1, Unit.COUNT, log -> {}); + withSingleMetric("Metric2", 1, Unit.COUNT, log -> + { + }); return null; } diff --git a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsEnabledDefaultNoDimensionHandler.java b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsEnabledDefaultNoDimensionHandler.java index 761362f43..0a0079b80 100644 --- a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsEnabledDefaultNoDimensionHandler.java +++ b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsEnabledDefaultNoDimensionHandler.java @@ -1,5 +1,22 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.metrics.handlers; +import static software.amazon.lambda.powertools.metrics.MetricsUtils.metricsLogger; +import static software.amazon.lambda.powertools.metrics.MetricsUtils.withSingleMetric; + import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import software.amazon.cloudwatchlogs.emf.logger.MetricsLogger; @@ -7,9 +24,6 @@ import software.amazon.lambda.powertools.metrics.Metrics; import software.amazon.lambda.powertools.metrics.MetricsUtils; -import static software.amazon.lambda.powertools.metrics.MetricsUtils.metricsLogger; -import static software.amazon.lambda.powertools.metrics.MetricsUtils.withSingleMetric; - public class PowertoolsMetricsEnabledDefaultNoDimensionHandler implements RequestHandler { static { @@ -22,7 +36,9 @@ public Object handleRequest(Object input, Context context) { MetricsLogger metricsLogger = metricsLogger(); metricsLogger.putMetric("Metric1", 1, Unit.BYTES); - withSingleMetric("Metric2", 1, Unit.COUNT, log -> {}); + withSingleMetric("Metric2", 1, Unit.COUNT, log -> + { + }); return null; } diff --git a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsEnabledHandler.java b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsEnabledHandler.java index 160109787..7cfee533d 100644 --- a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsEnabledHandler.java +++ b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsEnabledHandler.java @@ -1,5 +1,22 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.metrics.handlers; +import static software.amazon.lambda.powertools.metrics.MetricsUtils.metricsLogger; +import static software.amazon.lambda.powertools.metrics.MetricsUtils.withSingleMetric; + import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import software.amazon.cloudwatchlogs.emf.logger.MetricsLogger; @@ -7,9 +24,6 @@ import software.amazon.cloudwatchlogs.emf.model.Unit; import software.amazon.lambda.powertools.metrics.Metrics; -import static software.amazon.lambda.powertools.metrics.MetricsUtils.metricsLogger; -import static software.amazon.lambda.powertools.metrics.MetricsUtils.withSingleMetric; - public class PowertoolsMetricsEnabledHandler implements RequestHandler { @Override diff --git a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsEnabledStreamHandler.java b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsEnabledStreamHandler.java index 2eb877dc3..1600f4a64 100644 --- a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsEnabledStreamHandler.java +++ b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsEnabledStreamHandler.java @@ -1,16 +1,29 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.metrics.handlers; -import java.io.InputStream; -import java.io.OutputStream; +import static software.amazon.lambda.powertools.metrics.MetricsUtils.metricsLogger; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestStreamHandler; +import java.io.InputStream; +import java.io.OutputStream; import software.amazon.cloudwatchlogs.emf.logger.MetricsLogger; import software.amazon.cloudwatchlogs.emf.model.Unit; import software.amazon.lambda.powertools.metrics.Metrics; -import static software.amazon.lambda.powertools.metrics.MetricsUtils.metricsLogger; - public class PowertoolsMetricsEnabledStreamHandler implements RequestStreamHandler { @Override diff --git a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsExceptionWhenNoMetricsHandler.java b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsExceptionWhenNoMetricsHandler.java index 8ada044ee..42e0b3ad4 100644 --- a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsExceptionWhenNoMetricsHandler.java +++ b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsExceptionWhenNoMetricsHandler.java @@ -1,12 +1,26 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.metrics.handlers; +import static software.amazon.lambda.powertools.metrics.MetricsUtils.metricsLogger; + import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import software.amazon.cloudwatchlogs.emf.logger.MetricsLogger; import software.amazon.lambda.powertools.metrics.Metrics; -import static software.amazon.lambda.powertools.metrics.MetricsUtils.metricsLogger; - public class PowertoolsMetricsExceptionWhenNoMetricsHandler implements RequestHandler { @Override diff --git a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsNoDimensionsHandler.java b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsNoDimensionsHandler.java index 1c4cc3f77..04b02e166 100644 --- a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsNoDimensionsHandler.java +++ b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsNoDimensionsHandler.java @@ -1,13 +1,27 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.metrics.handlers; +import static software.amazon.lambda.powertools.metrics.MetricsUtils.metricsLogger; + import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import software.amazon.cloudwatchlogs.emf.logger.MetricsLogger; import software.amazon.cloudwatchlogs.emf.model.DimensionSet; import software.amazon.lambda.powertools.metrics.Metrics; -import static software.amazon.lambda.powertools.metrics.MetricsUtils.metricsLogger; - public class PowertoolsMetricsNoDimensionsHandler implements RequestHandler { @Override diff --git a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsNoExceptionWhenNoMetricsHandler.java b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsNoExceptionWhenNoMetricsHandler.java index 3639542f8..c08ce2f86 100644 --- a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsNoExceptionWhenNoMetricsHandler.java +++ b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsNoExceptionWhenNoMetricsHandler.java @@ -1,12 +1,26 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.metrics.handlers; +import static software.amazon.lambda.powertools.metrics.MetricsUtils.metricsLogger; + import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import software.amazon.cloudwatchlogs.emf.logger.MetricsLogger; import software.amazon.lambda.powertools.metrics.Metrics; -import static software.amazon.lambda.powertools.metrics.MetricsUtils.metricsLogger; - public class PowertoolsMetricsNoExceptionWhenNoMetricsHandler implements RequestHandler { @Override diff --git a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsTooManyDimensionsHandler.java b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsTooManyDimensionsHandler.java index ccec863f9..bc8a6e949 100644 --- a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsTooManyDimensionsHandler.java +++ b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsTooManyDimensionsHandler.java @@ -1,15 +1,28 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.metrics.handlers; -import java.util.stream.IntStream; +import static software.amazon.lambda.powertools.metrics.MetricsUtils.metricsLogger; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; +import java.util.stream.IntStream; import software.amazon.cloudwatchlogs.emf.logger.MetricsLogger; import software.amazon.cloudwatchlogs.emf.model.DimensionSet; import software.amazon.lambda.powertools.metrics.Metrics; -import static software.amazon.lambda.powertools.metrics.MetricsUtils.metricsLogger; - public class PowertoolsMetricsTooManyDimensionsHandler implements RequestHandler { @Override diff --git a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsWithExceptionInHandler.java b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsWithExceptionInHandler.java index db75d9f95..da9028a70 100644 --- a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsWithExceptionInHandler.java +++ b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/handlers/PowertoolsMetricsWithExceptionInHandler.java @@ -1,12 +1,26 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.metrics.handlers; +import static software.amazon.lambda.powertools.metrics.MetricsUtils.metricsLogger; + import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import software.amazon.cloudwatchlogs.emf.logger.MetricsLogger; import software.amazon.lambda.powertools.metrics.Metrics; -import static software.amazon.lambda.powertools.metrics.MetricsUtils.metricsLogger; - public class PowertoolsMetricsWithExceptionInHandler implements RequestHandler { @Override diff --git a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/internal/LambdaMetricsAspectTest.java b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/internal/LambdaMetricsAspectTest.java index 6c18d5d7a..44202b8b8 100644 --- a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/internal/LambdaMetricsAspectTest.java +++ b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/internal/LambdaMetricsAspectTest.java @@ -1,16 +1,38 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.metrics.internal; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.PrintStream; -import java.util.Map; +import static java.util.Collections.emptyMap; +import static org.apache.commons.lang3.reflect.FieldUtils.writeStaticField; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.openMocks; +import static software.amazon.lambda.powertools.core.internal.SystemWrapper.getenv; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.lambda.runtime.RequestStreamHandler; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.util.Map; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; @@ -18,7 +40,6 @@ import org.mockito.Mock; import org.mockito.MockedStatic; import software.amazon.cloudwatchlogs.emf.config.SystemWrapper; -import software.amazon.cloudwatchlogs.emf.model.DimensionSet; import software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor; import software.amazon.lambda.powertools.metrics.MetricsUtils; import software.amazon.lambda.powertools.metrics.ValidationException; @@ -33,22 +54,12 @@ import software.amazon.lambda.powertools.metrics.handlers.PowertoolsMetricsTooManyDimensionsHandler; import software.amazon.lambda.powertools.metrics.handlers.PowertoolsMetricsWithExceptionInHandler; -import static java.util.Collections.emptyMap; -import static org.apache.commons.lang3.reflect.FieldUtils.writeStaticField; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.mockito.Mockito.mockStatic; -import static org.mockito.Mockito.when; -import static org.mockito.MockitoAnnotations.openMocks; -import static software.amazon.lambda.powertools.core.internal.SystemWrapper.getenv; - public class LambdaMetricsAspectTest { - @Mock - private Context context; - private final ByteArrayOutputStream out = new ByteArrayOutputStream(); private final PrintStream originalOut = System.out; private final ObjectMapper mapper = new ObjectMapper(); + @Mock + private Context context; private RequestHandler requestHandler; @@ -75,10 +86,12 @@ void tearDown() { @Test public void metricsWithoutColdStart() { try (MockedStatic mocked = mockStatic(SystemWrapper.class); - MockedStatic internalWrapper = mockStatic(software.amazon.lambda.powertools.core.internal.SystemWrapper.class)) { + MockedStatic internalWrapper = mockStatic( + software.amazon.lambda.powertools.core.internal.SystemWrapper.class)) { mocked.when(() -> SystemWrapper.getenv("AWS_EMF_ENVIRONMENT")).thenReturn("Lambda"); - internalWrapper.when(() -> getenv("_X_AMZN_TRACE_ID")).thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1\""); + internalWrapper.when(() -> getenv("_X_AMZN_TRACE_ID")) + .thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1\""); MetricsUtils.defaultDimensions(null); requestHandler = new PowertoolsMetricsEnabledHandler(); @@ -86,40 +99,43 @@ public void metricsWithoutColdStart() { assertThat(out.toString().split("\n")) .hasSize(2) - .satisfies(s -> { - Map logAsJson = readAsJson(s[0]); - - assertThat(logAsJson) - .containsEntry("Metric2", 1.0) - .containsEntry("Dimension1", "Value1") - .containsKey("_aws") - .containsEntry("xray_trace_id", "1-5759e988-bd862e3fe1be46a994272793") - .containsEntry("function_request_id", "123ABC"); - - Map aws = (Map) logAsJson.get("_aws"); - - assertThat(aws.get("CloudWatchMetrics")) - .asString() - .contains("Namespace=ExampleApplication"); - - logAsJson = readAsJson(s[1]); - - assertThat(logAsJson) - .containsEntry("Metric1", 1.0) - .containsEntry("Service", "booking") - .containsEntry("function_request_id", "123ABC") - .containsKey("_aws"); - }); + .satisfies(s -> + { + Map logAsJson = readAsJson(s[0]); + + assertThat(logAsJson) + .containsEntry("Metric2", 1.0) + .containsEntry("Dimension1", "Value1") + .containsKey("_aws") + .containsEntry("xray_trace_id", "1-5759e988-bd862e3fe1be46a994272793") + .containsEntry("function_request_id", "123ABC"); + + Map aws = (Map) logAsJson.get("_aws"); + + assertThat(aws.get("CloudWatchMetrics")) + .asString() + .contains("Namespace=ExampleApplication"); + + logAsJson = readAsJson(s[1]); + + assertThat(logAsJson) + .containsEntry("Metric1", 1.0) + .containsEntry("Service", "booking") + .containsEntry("function_request_id", "123ABC") + .containsKey("_aws"); + }); } } @Test public void metricsWithDefaultDimensionSpecified() { try (MockedStatic mocked = mockStatic(SystemWrapper.class); - MockedStatic internalWrapper = mockStatic(software.amazon.lambda.powertools.core.internal.SystemWrapper.class)) { + MockedStatic internalWrapper = mockStatic( + software.amazon.lambda.powertools.core.internal.SystemWrapper.class)) { mocked.when(() -> SystemWrapper.getenv("AWS_EMF_ENVIRONMENT")).thenReturn("Lambda"); - internalWrapper.when(() -> getenv("_X_AMZN_TRACE_ID")).thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1\""); + internalWrapper.when(() -> getenv("_X_AMZN_TRACE_ID")) + .thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1\""); requestHandler = new PowertoolsMetricsEnabledDefaultDimensionHandler(); @@ -127,40 +143,43 @@ public void metricsWithDefaultDimensionSpecified() { assertThat(out.toString().split("\n")) .hasSize(2) - .satisfies(s -> { - Map logAsJson = readAsJson(s[0]); - - assertThat(logAsJson) - .containsEntry("Metric2", 1.0) - .containsEntry("CustomDimension", "booking") - .containsKey("_aws") - .containsEntry("xray_trace_id", "1-5759e988-bd862e3fe1be46a994272793") - .containsEntry("function_request_id", "123ABC"); - - Map aws = (Map) logAsJson.get("_aws"); - - assertThat(aws.get("CloudWatchMetrics")) - .asString() - .contains("Namespace=ExampleApplication"); - - logAsJson = readAsJson(s[1]); - - assertThat(logAsJson) - .containsEntry("Metric1", 1.0) - .containsEntry("CustomDimension", "booking") - .containsEntry("function_request_id", "123ABC") - .containsKey("_aws"); - }); + .satisfies(s -> + { + Map logAsJson = readAsJson(s[0]); + + assertThat(logAsJson) + .containsEntry("Metric2", 1.0) + .containsEntry("CustomDimension", "booking") + .containsKey("_aws") + .containsEntry("xray_trace_id", "1-5759e988-bd862e3fe1be46a994272793") + .containsEntry("function_request_id", "123ABC"); + + Map aws = (Map) logAsJson.get("_aws"); + + assertThat(aws.get("CloudWatchMetrics")) + .asString() + .contains("Namespace=ExampleApplication"); + + logAsJson = readAsJson(s[1]); + + assertThat(logAsJson) + .containsEntry("Metric1", 1.0) + .containsEntry("CustomDimension", "booking") + .containsEntry("function_request_id", "123ABC") + .containsKey("_aws"); + }); } } @Test public void metricsWithDefaultNoDimensionSpecified() { try (MockedStatic mocked = mockStatic(SystemWrapper.class); - MockedStatic internalWrapper = mockStatic(software.amazon.lambda.powertools.core.internal.SystemWrapper.class)) { + MockedStatic internalWrapper = mockStatic( + software.amazon.lambda.powertools.core.internal.SystemWrapper.class)) { mocked.when(() -> SystemWrapper.getenv("AWS_EMF_ENVIRONMENT")).thenReturn("Lambda"); - internalWrapper.when(() -> getenv("_X_AMZN_TRACE_ID")).thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1\""); + internalWrapper.when(() -> getenv("_X_AMZN_TRACE_ID")) + .thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1\""); requestHandler = new PowertoolsMetricsEnabledDefaultNoDimensionHandler(); @@ -168,28 +187,29 @@ public void metricsWithDefaultNoDimensionSpecified() { assertThat(out.toString().split("\n")) .hasSize(2) - .satisfies(s -> { - Map logAsJson = readAsJson(s[0]); + .satisfies(s -> + { + Map logAsJson = readAsJson(s[0]); - assertThat(logAsJson) - .containsEntry("Metric2", 1.0) - .containsKey("_aws") - .containsEntry("xray_trace_id", "1-5759e988-bd862e3fe1be46a994272793") - .containsEntry("function_request_id", "123ABC"); + assertThat(logAsJson) + .containsEntry("Metric2", 1.0) + .containsKey("_aws") + .containsEntry("xray_trace_id", "1-5759e988-bd862e3fe1be46a994272793") + .containsEntry("function_request_id", "123ABC"); - Map aws = (Map) logAsJson.get("_aws"); + Map aws = (Map) logAsJson.get("_aws"); - assertThat(aws.get("CloudWatchMetrics")) - .asString() - .contains("Namespace=ExampleApplication"); + assertThat(aws.get("CloudWatchMetrics")) + .asString() + .contains("Namespace=ExampleApplication"); - logAsJson = readAsJson(s[1]); + logAsJson = readAsJson(s[1]); - assertThat(logAsJson) - .containsEntry("Metric1", 1.0) - .containsEntry("function_request_id", "123ABC") - .containsKey("_aws"); - }); + assertThat(logAsJson) + .containsEntry("Metric1", 1.0) + .containsEntry("function_request_id", "123ABC") + .containsKey("_aws"); + }); } } @@ -207,25 +227,26 @@ public void metricsWithColdStart() { assertThat(out.toString().split("\n")) .hasSize(2) - .satisfies(s -> { - Map logAsJson = readAsJson(s[0]); - - assertThat(logAsJson) - .doesNotContainKey("Metric1") - .containsEntry("ColdStart", 1.0) - .containsEntry("Service", "booking") - .containsEntry("function_request_id", "123ABC") - .containsKey("_aws"); - - logAsJson = readAsJson(s[1]); - - assertThat(logAsJson) - .doesNotContainKey("ColdStart") - .containsEntry("Metric1", 1.0) - .containsEntry("Service", "booking") - .containsEntry("function_request_id", "123ABC") - .containsKey("_aws"); - }); + .satisfies(s -> + { + Map logAsJson = readAsJson(s[0]); + + assertThat(logAsJson) + .doesNotContainKey("Metric1") + .containsEntry("ColdStart", 1.0) + .containsEntry("Service", "booking") + .containsEntry("function_request_id", "123ABC") + .containsKey("_aws"); + + logAsJson = readAsJson(s[1]); + + assertThat(logAsJson) + .doesNotContainKey("ColdStart") + .containsEntry("Metric1", 1.0) + .containsEntry("Service", "booking") + .containsEntry("function_request_id", "123ABC") + .containsKey("_aws"); + }); } } @@ -243,34 +264,35 @@ public void noColdStartMetricsWhenColdStartDone() { assertThat(out.toString().split("\n")) .hasSize(3) - .satisfies(s -> { - Map logAsJson = readAsJson(s[0]); - - assertThat(logAsJson) - .doesNotContainKey("Metric1") - .containsEntry("ColdStart", 1.0) - .containsEntry("Service", "booking") - .containsEntry("function_request_id", "123ABC") - .containsKey("_aws"); - - logAsJson = readAsJson(s[1]); - - assertThat(logAsJson) - .doesNotContainKey("ColdStart") - .containsEntry("Metric1", 1.0) - .containsEntry("Service", "booking") - .containsEntry("function_request_id", "123ABC") - .containsKey("_aws"); - - logAsJson = readAsJson(s[2]); - - assertThat(logAsJson) - .doesNotContainKey("ColdStart") - .containsEntry("Metric1", 1.0) - .containsEntry("Service", "booking") - .containsEntry("function_request_id", "123ABC") - .containsKey("_aws"); - }); + .satisfies(s -> + { + Map logAsJson = readAsJson(s[0]); + + assertThat(logAsJson) + .doesNotContainKey("Metric1") + .containsEntry("ColdStart", 1.0) + .containsEntry("Service", "booking") + .containsEntry("function_request_id", "123ABC") + .containsKey("_aws"); + + logAsJson = readAsJson(s[1]); + + assertThat(logAsJson) + .doesNotContainKey("ColdStart") + .containsEntry("Metric1", 1.0) + .containsEntry("Service", "booking") + .containsEntry("function_request_id", "123ABC") + .containsKey("_aws"); + + logAsJson = readAsJson(s[2]); + + assertThat(logAsJson) + .doesNotContainKey("ColdStart") + .containsEntry("Metric1", 1.0) + .containsEntry("Service", "booking") + .containsEntry("function_request_id", "123ABC") + .containsKey("_aws"); + }); } } @@ -283,18 +305,19 @@ public void metricsWithStreamHandler() throws IOException { MetricsUtils.defaultDimensions(null); RequestStreamHandler streamHandler = new PowertoolsMetricsEnabledStreamHandler(); - streamHandler.handleRequest(new ByteArrayInputStream(new byte[]{}), new ByteArrayOutputStream(), context); + streamHandler.handleRequest(new ByteArrayInputStream(new byte[] {}), new ByteArrayOutputStream(), context); assertThat(out.toString()) - .satisfies(s -> { - Map logAsJson = readAsJson(s); - - assertThat(logAsJson) - .containsEntry("Metric1", 1.0) - .containsEntry("Service", "booking") - .containsEntry("function_request_id", "123ABC") - .containsKey("_aws"); - }); + .satisfies(s -> + { + Map logAsJson = readAsJson(s); + + assertThat(logAsJson) + .containsEntry("Metric1", 1.0) + .containsEntry("Service", "booking") + .containsEntry("function_request_id", "123ABC") + .containsKey("_aws"); + }); } } @@ -324,13 +347,14 @@ public void noExceptionWhenNoMetricsEmitted() { requestHandler.handleRequest("input", context); assertThat(out.toString()) - .satisfies(s -> { - Map logAsJson = readAsJson(s); - - assertThat(logAsJson) - .containsEntry("Service", "booking") - .doesNotContainKey("_aws"); - }); + .satisfies(s -> + { + Map logAsJson = readAsJson(s); + + assertThat(logAsJson) + .containsEntry("Service", "booking") + .doesNotContainKey("_aws"); + }); } } @@ -344,13 +368,14 @@ public void allowWhenNoDimensionsSet() { requestHandler.handleRequest("input", context); assertThat(out.toString()) - .satisfies(s -> { - Map logAsJson = readAsJson(s); - assertThat(logAsJson) - .containsEntry("CoolMetric", 1.0) - .containsEntry("function_request_id", "123ABC") - .containsKey("_aws"); - }); + .satisfies(s -> + { + Map logAsJson = readAsJson(s); + assertThat(logAsJson) + .containsEntry("CoolMetric", 1.0) + .containsEntry("function_request_id", "123ABC") + .containsKey("_aws"); + }); } } @@ -383,14 +408,15 @@ public void metricsPublishedEvenHandlerThrowsException() { .withMessage("Whoops, unexpected exception"); assertThat(out.toString()) - .satisfies(s -> { - Map logAsJson = readAsJson(s); - assertThat(logAsJson) - .containsEntry("CoolMetric", 1.0) - .containsEntry("Service", "booking") - .containsEntry("function_request_id", "123ABC") - .containsKey("_aws"); - }); + .satisfies(s -> + { + Map logAsJson = readAsJson(s); + assertThat(logAsJson) + .containsEntry("CoolMetric", 1.0) + .containsEntry("Service", "booking") + .containsEntry("function_request_id", "123ABC") + .containsKey("_aws"); + }); } } diff --git a/powertools-parameters/pom.xml b/powertools-parameters/pom.xml index b50844d99..fab72a9b7 100644 --- a/powertools-parameters/pom.xml +++ b/powertools-parameters/pom.xml @@ -1,4 +1,18 @@ + + @@ -144,7 +158,10 @@ + + org.apache.maven.plugins + maven-checkstyle-plugin + - diff --git a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/AppConfigProvider.java b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/AppConfigProvider.java index c62d7a2e5..130be25a3 100644 --- a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/AppConfigProvider.java +++ b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/AppConfigProvider.java @@ -1,5 +1,21 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.parameters; +import java.util.HashMap; +import java.util.Map; import software.amazon.awssdk.core.SdkSystemSetting; import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; import software.amazon.awssdk.regions.Region; @@ -10,15 +26,12 @@ import software.amazon.lambda.powertools.parameters.cache.CacheManager; import software.amazon.lambda.powertools.parameters.transform.TransformationManager; -import java.util.HashMap; -import java.util.Map; - /** * Implements a {@link ParamProvider} on top of the AppConfig service. AppConfig provides * a mechanism to retrieve and update configuration of applications over time. * AppConfig requires the user to create an application, environment, and configuration profile. * The configuration profile's value can then be retrieved, by key name, through this provider. - * + *

* Because AppConfig is designed to handle rollouts of configuration over time, we must first * establish a session for each key we wish to retrieve, and then poll the session for the latest * value when the user re-requests it. This means we must hold a keyed set of session tokens @@ -27,24 +40,11 @@ * @see Parameters provider documentation * @see AppConfig documentation */ -public class AppConfigProvider extends BaseProvider{ - - private static class EstablishedSession { - private final String nextSessionToken; - private final String lastConfigurationValue; - - private EstablishedSession(String nextSessionToken, String value) { - this.nextSessionToken = nextSessionToken; - this.lastConfigurationValue = value; - } - } +public class AppConfigProvider extends BaseProvider { private final AppConfigDataClient client; - private final String application; - private final String environment; - private final HashMap establishedSessions = new HashMap<>(); AppConfigProvider(CacheManager cacheManager, AppConfigDataClient client, String environment, String application) { @@ -54,6 +54,14 @@ private EstablishedSession(String nextSessionToken, String value) { this.environment = environment; } + /** + * Create a builder that can be used to configure and create a {@link AppConfigProvider}. + * + * @return a new instance of {@link AppConfigProvider.Builder} + */ + public static AppConfigProvider.Builder builder() { + return new AppConfigProvider.Builder(); + } /** * Retrieve the parameter value from the AppConfig parameter store.
@@ -67,18 +75,18 @@ protected String getValue(String key) { // so that we can the initial token. If we already have a session, we can take // the next request token from there. EstablishedSession establishedSession = establishedSessions.getOrDefault(key, null); - String sessionToken = establishedSession != null? + String sessionToken = establishedSession != null ? establishedSession.nextSessionToken : client.startConfigurationSession(StartConfigurationSessionRequest.builder() - .applicationIdentifier(this.application) - .environmentIdentifier(this.environment) - .configurationProfileIdentifier(key) - .build()) - .initialConfigurationToken(); + .applicationIdentifier(this.application) + .environmentIdentifier(this.environment) + .configurationProfileIdentifier(key) + .build()) + .initialConfigurationToken(); // Get the configuration using the token GetLatestConfigurationResponse response = client.getLatestConfiguration(GetLatestConfigurationRequest.builder() - .configurationToken(sessionToken) + .configurationToken(sessionToken) .build()); // Get the next session token we'll use next time we are asked for this key @@ -87,11 +95,12 @@ protected String getValue(String key) { // Get the value of the key. Note that AppConfig will return null if the value // has not changed since we last asked for it in this session - in this case // we return the value we stashed at last request. - String value = response.configuration() != null? + String value = response.configuration() != null ? response.configuration().asUtf8String() : // if we have a new value, use it - establishedSession != null? - establishedSession.lastConfigurationValue : // if we don't but we have a previous value, use that - null; // otherwise we've got no value + establishedSession != null ? + establishedSession.lastConfigurationValue : + // if we don't but we have a previous value, use that + null; // otherwise we've got no value // Update the cache so we can get the next value later establishedSessions.put(key, new EstablishedSession(nextSessionToken, value)); @@ -102,16 +111,18 @@ protected String getValue(String key) { @Override protected Map getMultipleValues(String path) { // Retrieving multiple values is not supported with the AppConfig provider. - throw new RuntimeException("Retrieving multiple parameter values is not supported with the AWS App Config Provider"); + throw new RuntimeException( + "Retrieving multiple parameter values is not supported with the AWS App Config Provider"); } - /** - * Create a builder that can be used to configure and create a {@link AppConfigProvider}. - * - * @return a new instance of {@link AppConfigProvider.Builder} - */ - public static AppConfigProvider.Builder builder() { - return new AppConfigProvider.Builder(); + private static class EstablishedSession { + private final String nextSessionToken; + private final String lastConfigurationValue; + + private EstablishedSession(String nextSessionToken, String value) { + this.nextSessionToken = nextSessionToken; + this.lastConfigurationValue = value; + } } static class Builder { diff --git a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/BaseProvider.java b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/BaseProvider.java index fb539f850..e7bfdf835 100644 --- a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/BaseProvider.java +++ b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/BaseProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,8 +11,14 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.parameters; +import java.time.Clock; +import java.time.Duration; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.Map; import software.amazon.awssdk.annotations.NotThreadSafe; import software.amazon.lambda.powertools.parameters.cache.CacheManager; import software.amazon.lambda.powertools.parameters.exception.TransformationException; @@ -20,12 +26,6 @@ import software.amazon.lambda.powertools.parameters.transform.TransformationManager; import software.amazon.lambda.powertools.parameters.transform.Transformer; -import java.time.Clock; -import java.time.Duration; -import java.time.Instant; -import java.time.temporal.ChronoUnit; -import java.util.Map; - /** * Base class for all parameter providers. */ @@ -106,7 +106,8 @@ public BaseProvider withMaxAge(int maxAge, ChronoUnit unit) { */ public BaseProvider withTransformation(Class transformerClass) { if (transformationManager == null) { - throw new IllegalStateException("Trying to add transformation while no TransformationManager has been provided."); + throw new IllegalStateException( + "Trying to add transformation while no TransformationManager has been provided."); } transformationManager.setTransformer(transformerClass); return this; @@ -126,15 +127,16 @@ public Map getMultiple(String path) { // remove trailing whitespace String pathWithoutTrailingSlash = path.replaceAll("\\/+$", ""); try { - return (Map) cacheManager.getIfNotExpired(pathWithoutTrailingSlash, now()).orElseGet(() -> { - Map params = getMultipleValues(pathWithoutTrailingSlash); + return (Map) cacheManager.getIfNotExpired(pathWithoutTrailingSlash, now()).orElseGet(() -> + { + Map params = getMultipleValues(pathWithoutTrailingSlash); - cacheManager.putInCache(pathWithoutTrailingSlash, params); + cacheManager.putInCache(pathWithoutTrailingSlash, params); - params.forEach((k, v) -> cacheManager.putInCache(pathWithoutTrailingSlash + "/" + k, v)); + params.forEach((k, v) -> cacheManager.putInCache(pathWithoutTrailingSlash + "/" + k, v)); - return params; - }); + return params; + }); } finally { resetToDefaults(); } @@ -148,24 +150,25 @@ public Map getMultiple(String path) { * * @param key key of the parameter * @return the String value of the parameter - * @throws IllegalStateException if a wrong transformer class is provided through {@link #withTransformation(Class)}. Needs to be a {@link BasicTransformer}. - * @throws TransformationException if the transformation could not be done, because of a wrong format or an error during transformation. + * @throws IllegalStateException if a wrong transformer class is provided through {@link #withTransformation(Class)}. Needs to be a {@link BasicTransformer}. + * @throws TransformationException if the transformation could not be done, because of a wrong format or an error during transformation. */ @Override public String get(final String key) { try { - return (String) cacheManager.getIfNotExpired(key, now()).orElseGet(() -> { - String value = getValue(key); + return (String) cacheManager.getIfNotExpired(key, now()).orElseGet(() -> + { + String value = getValue(key); - String transformedValue = value; - if (transformationManager != null && transformationManager.shouldTransform()) { - transformedValue = transformationManager.performBasicTransformation(value); - } + String transformedValue = value; + if (transformationManager != null && transformationManager.shouldTransform()) { + transformedValue = transformationManager.performBasicTransformation(value); + } - cacheManager.putInCache(key, transformedValue); + cacheManager.putInCache(key, transformedValue); - return transformedValue; - }); + return transformedValue; + }); } finally { // in all case, we reset options to default, for next call resetToDefaults(); @@ -181,24 +184,26 @@ public String get(final String key) { * @param key key of the parameter * @param targetClass class of the target Object (after transformation) * @return the Object (T) value of the parameter - * @throws IllegalStateException if no transformation class was provided through {@link #withTransformation(Class)} - * @throws TransformationException if the transformation could not be done, because of a wrong format or an error during transformation. + * @throws IllegalStateException if no transformation class was provided through {@link #withTransformation(Class)} + * @throws TransformationException if the transformation could not be done, because of a wrong format or an error during transformation. */ @Override public T get(final String key, final Class targetClass) { try { - return (T) cacheManager.getIfNotExpired(key, now()).orElseGet(() -> { - String value = getValue(key); + return (T) cacheManager.getIfNotExpired(key, now()).orElseGet(() -> + { + String value = getValue(key); - if (transformationManager == null) { - throw new IllegalStateException("Trying to transform value while no TransformationManager has been provided."); - } - T transformedValue = transformationManager.performComplexTransformation(value, targetClass); + if (transformationManager == null) { + throw new IllegalStateException( + "Trying to transform value while no TransformationManager has been provided."); + } + T transformedValue = transformationManager.performComplexTransformation(value, targetClass); - cacheManager.putInCache(key, transformedValue); + cacheManager.putInCache(key, transformedValue); - return transformedValue; - }); + return transformedValue; + }); } finally { // in all case, we reset options to default, for next call resetToDefaults(); @@ -225,6 +230,7 @@ protected void setTransformationManager(TransformationManager transformationMana /** * For test purpose + * * @param clock */ void setClock(Clock clock) { diff --git a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/DynamoDbProvider.java b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/DynamoDbProvider.java index e09f23348..2b0694a5d 100644 --- a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/DynamoDbProvider.java +++ b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/DynamoDbProvider.java @@ -1,5 +1,22 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.parameters; +import java.util.Collections; +import java.util.Map; +import java.util.stream.Collectors; import software.amazon.awssdk.core.SdkSystemSetting; import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; import software.amazon.awssdk.regions.Region; @@ -13,16 +30,11 @@ import software.amazon.lambda.powertools.parameters.exception.DynamoDbProviderSchemaException; import software.amazon.lambda.powertools.parameters.transform.TransformationManager; -import java.util.Collections; -import java.util.Map; -import java.util.stream.Collectors; - /** * Implements a {@link ParamProvider} on top of DynamoDB. The schema of the table * is described in the Powertools for AWS Lambda (Java) documentation. * * @see Parameters provider documentation - * */ public class DynamoDbProvider extends BaseProvider { @@ -39,6 +51,15 @@ public class DynamoDbProvider extends BaseProvider { this(cacheManager, Builder.createClient(), tableName); } + /** + * Create a builder that can be used to configure and create a {@link DynamoDbProvider}. + * + * @return a new instance of {@link DynamoDbProvider.Builder} + */ + public static DynamoDbProvider.Builder builder() { + return new DynamoDbProvider.Builder(); + } + /** * Return a single value from the DynamoDB parameter provider. * @@ -82,9 +103,10 @@ protected Map getMultipleValues(String path) { .build()); return resp - .items() - .stream() - .peek((i) -> { + .items() + .stream() + .peek((i) -> + { if (!i.containsKey("sk")) { throw new DynamoDbProviderSchemaException("Missing 'sk': " + i.toString()); } @@ -92,29 +114,27 @@ protected Map getMultipleValues(String path) { throw new DynamoDbProviderSchemaException("Missing 'value': " + i.toString()); } }) - .collect( - Collectors.toMap( - (i) -> i.get("sk").s(), - (i) -> i.get("value").s())); + .collect( + Collectors.toMap( + (i) -> i.get("sk").s(), + (i) -> i.get("value").s())); } - /** - * Create a builder that can be used to configure and create a {@link DynamoDbProvider}. - * - * @return a new instance of {@link DynamoDbProvider.Builder} - */ - public static DynamoDbProvider.Builder builder() { - return new DynamoDbProvider.Builder(); - } - static class Builder { private DynamoDbClient client; private String table; private CacheManager cacheManager; private TransformationManager transformationManager; + private static DynamoDbClient createClient() { + return DynamoDbClient.builder() + .httpClientBuilder(UrlConnectionHttpClient.builder()) + .region(Region.of(System.getenv(SdkSystemSetting.AWS_REGION.environmentVariable()))) + .build(); + } + /** * Create a {@link DynamoDbProvider} instance. * @@ -183,12 +203,5 @@ public DynamoDbProvider.Builder withTransformationManager(TransformationManager this.transformationManager = transformationManager; return this; } - - private static DynamoDbClient createClient() { - return DynamoDbClient.builder() - .httpClientBuilder(UrlConnectionHttpClient.builder()) - .region(Region.of(System.getenv(SdkSystemSetting.AWS_REGION.environmentVariable()))) - .build(); - } } } diff --git a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/Param.java b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/Param.java index ef3d08b72..7ffb0310c 100644 --- a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/Param.java +++ b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/Param.java @@ -1,11 +1,24 @@ -package software.amazon.lambda.powertools.parameters; +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ -import software.amazon.lambda.powertools.parameters.transform.Transformer; +package software.amazon.lambda.powertools.parameters; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import software.amazon.lambda.powertools.parameters.transform.Transformer; /** * {@code Param} is used to signal that the annotated field should be @@ -25,6 +38,8 @@ @Target(ElementType.FIELD) public @interface Param { String key(); + Class provider() default SSMProvider.class; + Class transformer() default Transformer.class; } diff --git a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/ParamManager.java b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/ParamManager.java index c8abedf06..6fee0f114 100644 --- a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/ParamManager.java +++ b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/ParamManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,8 +11,11 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.parameters; +import java.lang.reflect.Constructor; +import java.util.concurrent.ConcurrentHashMap; import software.amazon.awssdk.services.appconfigdata.AppConfigDataClient; import software.amazon.awssdk.services.dynamodb.DynamoDbClient; import software.amazon.awssdk.services.secretsmanager.SecretsManagerClient; @@ -20,9 +23,6 @@ import software.amazon.lambda.powertools.parameters.cache.CacheManager; import software.amazon.lambda.powertools.parameters.transform.TransformationManager; -import java.lang.reflect.Constructor; -import java.util.concurrent.ConcurrentHashMap; - /** * Utility class to retrieve instances of parameter providers. * Each instance is unique (singleton). @@ -39,8 +39,9 @@ public final class ParamManager { * Get a concrete implementation of {@link BaseProvider}.
* You can specify {@link SecretsProvider}, {@link SSMProvider} or create your * custom provider by extending {@link BaseProvider} if you need to integrate with a different parameter store. - * @deprecated You should not use this method directly but a typed one (getSecretsProvider, getSsmProvider, getDynamoDbProvider, getAppConfigProvider), will be removed in v2 + * * @return a {@link SecretsProvider} + * @deprecated You should not use this method directly but a typed one (getSecretsProvider, getSsmProvider, getDynamoDbProvider, getAppConfigProvider), will be removed in v2 */ // TODO in v2: remove public access to this and review how we get providers (it was not designed for DDB and AppConfig in mind initially) public static T getProvider(Class providerClass) { @@ -48,7 +49,8 @@ public static T getProvider(Class providerClass) { throw new IllegalStateException("providerClass cannot be null."); } if (providerClass == DynamoDbProvider.class || providerClass == AppConfigProvider.class) { - throw new IllegalArgumentException(providerClass + " cannot be instantiated like this, additional parameters are required"); + throw new IllegalArgumentException( + providerClass + " cannot be instantiated like this, additional parameters are required"); } return (T) providers.computeIfAbsent(providerClass, ParamManager::createProvider); } @@ -56,6 +58,7 @@ public static T getProvider(Class providerClass) { /** * Get a {@link SecretsProvider} with default {@link SecretsManagerClient}.
* If you need to customize the region, or other part of the client, use {@link ParamManager#getSecretsProvider(SecretsManagerClient)} instead. + * * @return a {@link SecretsProvider} */ public static SecretsProvider getSecretsProvider() { @@ -65,6 +68,7 @@ public static SecretsProvider getSecretsProvider() { /** * Get a {@link SSMProvider} with default {@link SsmClient}.
* If you need to customize the region, or other part of the client, use {@link ParamManager#getSsmProvider(SsmClient)} instead. + * * @return a {@link SSMProvider} */ public static SSMProvider getSsmProvider() { @@ -89,6 +93,7 @@ public static DynamoDbProvider getDynamoDbProvider(String tableName) { /** * Get a {@link AppConfigProvider} with default {@link AppConfigDataClient}.
* If you need to customize the region, or other part of the client, use {@link ParamManager#getAppConfigProvider(AppConfigDataClient, String, String)} instead. + * * @return a {@link AppConfigProvider} */ public static AppConfigProvider getAppConfigProvider(String environment, String application) { @@ -107,6 +112,7 @@ public static AppConfigProvider getAppConfigProvider(String environment, String /** * Get a {@link SecretsProvider} with your custom {@link SecretsManagerClient}.
* Use this to configure region or other part of the client. Use {@link ParamManager#getSsmProvider()} if you don't need this customization. + * * @return a {@link SecretsProvider} */ public static SecretsProvider getSecretsProvider(SecretsManagerClient client) { @@ -120,6 +126,7 @@ public static SecretsProvider getSecretsProvider(SecretsManagerClient client) { /** * Get a {@link SSMProvider} with your custom {@link SsmClient}.
* Use this to configure region or other part of the client. Use {@link ParamManager#getSsmProvider()} if you don't need this customization. + * * @return a {@link SSMProvider} */ public static SSMProvider getSsmProvider(SsmClient client) { @@ -133,6 +140,7 @@ public static SSMProvider getSsmProvider(SsmClient client) { /** * Get a {@link DynamoDbProvider} with your custom {@link DynamoDbClient}.
* Use this to configure region or other part of the client. Use {@link ParamManager#getDynamoDbProvider(String)} )} if you don't need this customization. + * * @return a {@link DynamoDbProvider} */ public static DynamoDbProvider getDynamoDbProvider(DynamoDbClient client, String table) { @@ -143,13 +151,15 @@ public static DynamoDbProvider getDynamoDbProvider(DynamoDbClient client, String .withTransformationManager(transformationManager) .build()); } - + /** * Get a {@link AppConfigProvider} with your custom {@link AppConfigDataClient}.
* Use this to configure region or other part of the client. Use {@link ParamManager#getAppConfigProvider(String, String)} if you don't need this customization. + * * @return a {@link AppConfigProvider} */ - public static AppConfigProvider getAppConfigProvider(AppConfigDataClient client, String environment, String application) { + public static AppConfigProvider getAppConfigProvider(AppConfigDataClient client, String environment, + String application) { return (AppConfigProvider) providers.computeIfAbsent(AppConfigProvider.class, (k) -> AppConfigProvider.builder() .withClient(client) .withCacheManager(cacheManager) @@ -168,10 +178,11 @@ public static TransformationManager getTransformationManager() { return transformationManager; } - static T createProvider(Class providerClass) { + static T createProvider(Class providerClass) { try { Constructor constructor = providerClass.getDeclaredConstructor(CacheManager.class); - T provider = constructor.newInstance(cacheManager); // FIXME: avoid reflection here as we may have issues (#1280) + T provider = + constructor.newInstance(cacheManager); // FIXME: avoid reflection here as we may have issues (#1280) provider.setTransformationManager(transformationManager); return provider; } catch (ReflectiveOperationException e) { diff --git a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/ParamProvider.java b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/ParamProvider.java index b496ed4f3..ba4232261 100644 --- a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/ParamProvider.java +++ b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/ParamProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.parameters; import java.util.Map; diff --git a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/SSMProvider.java b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/SSMProvider.java index 1fa4dbaab..b24b1e319 100644 --- a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/SSMProvider.java +++ b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/SSMProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,8 +11,12 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.parameters; +import java.time.temporal.ChronoUnit; +import java.util.HashMap; +import java.util.Map; import software.amazon.awssdk.core.SdkSystemSetting; import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; import software.amazon.awssdk.regions.Region; @@ -25,10 +29,6 @@ import software.amazon.lambda.powertools.parameters.transform.TransformationManager; import software.amazon.lambda.powertools.parameters.transform.Transformer; -import java.time.temporal.ChronoUnit; -import java.util.HashMap; -import java.util.Map; - /** * AWS System Manager Parameter Store Provider

* @@ -88,14 +88,24 @@ public class SSMProvider extends BaseProvider { /** * Constructor with only a CacheManager
- * + *

* Used in {@link ParamManager#createProvider(Class)} + * * @param cacheManager handles the parameter caching */ SSMProvider(CacheManager cacheManager) { this(cacheManager, Builder.createClient()); } + /** + * Create a builder that can be used to configure and create a {@link SSMProvider}. + * + * @return a new instance of {@link SSMProvider.Builder} + */ + public static SSMProvider.Builder builder() { + return new SSMProvider.Builder(); + } + /** * Retrieve the parameter value from the AWS System Manager Parameter Store. * @@ -194,19 +204,20 @@ private Map getMultipleBis(String path, String nextToken) { // not using the client.getParametersByPathPaginator() as hardly testable GetParametersByPathResponse res = client.getParametersByPath(request); if (res.hasParameters()) { - res.parameters().forEach(parameter -> { + res.parameters().forEach(parameter -> + { /* Standardize the parameter name The parameter name returned by SSM will contained the full path. However, for readability, we should return only the part after the path. */ - String name = parameter.name(); - if (name.startsWith(path)) { - name = name.replaceFirst(path, ""); - } - name = name.replaceFirst("/", ""); - params.put(name, parameter.value()); - }); + String name = parameter.name(); + if (name.startsWith(path)) { + name = name.replaceFirst(path, ""); + } + name = name.replaceFirst("/", ""); + params.put(name, parameter.value()); + }); } if (!StringUtils.isEmpty(res.nextToken())) { @@ -228,21 +239,18 @@ SsmClient getClient() { return client; } - /** - * Create a builder that can be used to configure and create a {@link SSMProvider}. - * - * @return a new instance of {@link SSMProvider.Builder} - */ - public static SSMProvider.Builder builder() { - return new SSMProvider.Builder(); - } - - static class Builder { private SsmClient client; private CacheManager cacheManager; private TransformationManager transformationManager; + private static SsmClient createClient() { + return SsmClient.builder() + .httpClientBuilder(UrlConnectionHttpClient.builder()) + .region(Region.of(System.getenv(SdkSystemSetting.AWS_REGION.environmentVariable()))) + .build(); + } + /** * Create a {@link SSMProvider} instance. * @@ -277,13 +285,6 @@ public SSMProvider.Builder withClient(SsmClient client) { return this; } - private static SsmClient createClient() { - return SsmClient.builder() - .httpClientBuilder(UrlConnectionHttpClient.builder()) - .region(Region.of(System.getenv(SdkSystemSetting.AWS_REGION.environmentVariable()))) - .build(); - } - /** * Mandatory. Provide a CacheManager to the {@link SSMProvider} * diff --git a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/SecretsProvider.java b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/SecretsProvider.java index fd45da881..99b87f84b 100644 --- a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/SecretsProvider.java +++ b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/SecretsProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,8 +11,14 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.parameters; +import static java.nio.charset.StandardCharsets.UTF_8; + +import java.time.temporal.ChronoUnit; +import java.util.Base64; +import java.util.Map; import software.amazon.awssdk.core.SdkSystemSetting; import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; import software.amazon.awssdk.regions.Region; @@ -22,12 +28,6 @@ import software.amazon.lambda.powertools.parameters.transform.TransformationManager; import software.amazon.lambda.powertools.parameters.transform.Transformer; -import java.time.temporal.ChronoUnit; -import java.util.Base64; -import java.util.Map; - -import static java.nio.charset.StandardCharsets.UTF_8; - /** * AWS Secrets Manager Parameter Provider

* @@ -59,7 +59,7 @@ public class SecretsProvider extends BaseProvider { /** * Constructor with custom {@link SecretsManagerClient}.
* Use when you need to customize region or any other attribute of the client.

- * + *

* Use the {@link Builder} to create an instance of it. * * @param client custom client you would like to use. @@ -71,14 +71,24 @@ public class SecretsProvider extends BaseProvider { /** * Constructor with only a CacheManager
- * + *

* Used in {@link ParamManager#createProvider(Class)} + * * @param cacheManager handles the parameter caching */ SecretsProvider(CacheManager cacheManager) { this(cacheManager, Builder.createClient()); } + /** + * Create a builder that can be used to configure and create a {@link SecretsProvider}. + * + * @return a new instance of {@link SecretsProvider.Builder} + */ + public static Builder builder() { + return new Builder(); + } + /** * Retrieve the parameter value from the AWS Secrets Manager. * @@ -91,13 +101,14 @@ protected String getValue(String key) { String secretValue = client.getSecretValue(request).secretString(); if (secretValue == null) { - secretValue = new String(Base64.getDecoder().decode(client.getSecretValue(request).secretBinary().asByteArray()), UTF_8); + secretValue = + new String(Base64.getDecoder().decode(client.getSecretValue(request).secretBinary().asByteArray()), + UTF_8); } return secretValue; } /** - * * @throws UnsupportedOperationException as it is not possible to get multiple values simultaneously from Secrets Manager */ @Override @@ -137,21 +148,19 @@ SecretsManagerClient getClient() { return client; } - /** - * Create a builder that can be used to configure and create a {@link SecretsProvider}. - * - * @return a new instance of {@link SecretsProvider.Builder} - */ - public static Builder builder() { - return new Builder(); - } - static class Builder { private SecretsManagerClient client; private CacheManager cacheManager; private TransformationManager transformationManager; + private static SecretsManagerClient createClient() { + return SecretsManagerClient.builder() + .httpClientBuilder(UrlConnectionHttpClient.builder()) + .region(Region.of(System.getenv(SdkSystemSetting.AWS_REGION.environmentVariable()))) + .build(); + } + /** * Create a {@link SecretsProvider} instance. * @@ -186,13 +195,6 @@ public Builder withClient(SecretsManagerClient client) { return this; } - private static SecretsManagerClient createClient() { - return SecretsManagerClient.builder() - .httpClientBuilder(UrlConnectionHttpClient.builder()) - .region(Region.of(System.getenv(SdkSystemSetting.AWS_REGION.environmentVariable()))) - .build(); - } - /** * Mandatory. Provide a CacheManager to the {@link SecretsProvider} * diff --git a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/cache/CacheManager.java b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/cache/CacheManager.java index 687337a96..b868cb642 100644 --- a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/cache/CacheManager.java +++ b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/cache/CacheManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,15 +11,16 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.parameters.cache; +import static java.time.temporal.ChronoUnit.SECONDS; + import java.time.Clock; import java.time.Duration; import java.time.Instant; import java.util.Optional; -import static java.time.temporal.ChronoUnit.SECONDS; - public class CacheManager { static final Duration DEFAULT_MAX_AGE_SECS = Duration.of(5, SECONDS); diff --git a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/cache/DataStore.java b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/cache/DataStore.java index 9ad8df12c..737faa353 100644 --- a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/cache/DataStore.java +++ b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/cache/DataStore.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.parameters.cache; import java.time.Instant; @@ -27,21 +28,11 @@ public DataStore() { this.store = new ConcurrentHashMap<>(); } - static class ValueNode { - public final Object value; - public final Instant time; - - public ValueNode(Object value, Instant time){ - this.value = value; - this.time = time; - } - } - - public void put(String key, Object value, Instant time){ + public void put(String key, Object value, Instant time) { store.put(key, new ValueNode(value, time)); } - public void remove(String Key){ + public void remove(String Key) { store.remove(Key); } @@ -51,11 +42,21 @@ public Object get(String key) { } public boolean hasExpired(String key, Instant now) { - boolean hasExpired = !store.containsKey(key) || now.isAfter(store.get(key).time); + boolean hasExpired = !store.containsKey(key) || now.isAfter(store.get(key).time); // Auto-clean if the parameter has expired if (hasExpired) { remove(key); } return hasExpired; } + + static class ValueNode { + public final Object value; + public final Instant time; + + public ValueNode(Object value, Instant time) { + this.value = value; + this.time = time; + } + } } diff --git a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/exception/DynamoDbProviderSchemaException.java b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/exception/DynamoDbProviderSchemaException.java index b7574e81d..77df6e3d3 100644 --- a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/exception/DynamoDbProviderSchemaException.java +++ b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/exception/DynamoDbProviderSchemaException.java @@ -1,3 +1,17 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.parameters.exception; /** diff --git a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/exception/TransformationException.java b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/exception/TransformationException.java index 7d28d12d1..f071c8a6b 100644 --- a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/exception/TransformationException.java +++ b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/exception/TransformationException.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.parameters.exception; public class TransformationException extends RuntimeException { @@ -19,6 +20,7 @@ public TransformationException(Exception e) { super(e); } - public TransformationException(String message) { super(message); + public TransformationException(String message) { + super(message); } } diff --git a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/internal/LambdaParametersAspect.java b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/internal/LambdaParametersAspect.java index 8de2f3f57..081af108d 100644 --- a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/internal/LambdaParametersAspect.java +++ b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/internal/LambdaParametersAspect.java @@ -1,3 +1,17 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.parameters.internal; import org.aspectj.lang.ProceedingJoinPoint; @@ -20,12 +34,12 @@ public void getParam(Param paramAnnotation) { public Object injectParam(final ProceedingJoinPoint joinPoint, final Param paramAnnotation) { BaseProvider provider = ParamManager.getProvider(paramAnnotation.provider()); - if(paramAnnotation.transformer().isInterface()) { + if (paramAnnotation.transformer().isInterface()) { // No transformation return provider.get(paramAnnotation.key()); } else { FieldSignature s = (FieldSignature) joinPoint.getSignature(); - if(String.class.isAssignableFrom(s.getFieldType())) { + if (String.class.isAssignableFrom(s.getFieldType())) { // Basic transformation return provider .withTransformation(paramAnnotation.transformer()) diff --git a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/transform/Base64Transformer.java b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/transform/Base64Transformer.java index c666edce7..e8557ebfd 100644 --- a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/transform/Base64Transformer.java +++ b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/transform/Base64Transformer.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,13 +11,13 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.parameters.transform; -import software.amazon.lambda.powertools.parameters.exception.TransformationException; +import static java.nio.charset.StandardCharsets.UTF_8; import java.util.Base64; - -import static java.nio.charset.StandardCharsets.UTF_8; +import software.amazon.lambda.powertools.parameters.exception.TransformationException; /** * Transformer that take a base64 encoded string and return a decoded string. diff --git a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/transform/BasicTransformer.java b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/transform/BasicTransformer.java index 5251d9f16..92e73d9b0 100644 --- a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/transform/BasicTransformer.java +++ b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/transform/BasicTransformer.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.parameters.transform; import software.amazon.lambda.powertools.parameters.exception.TransformationException; diff --git a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/transform/JsonTransformer.java b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/transform/JsonTransformer.java index d84a1ab3a..0eff58ea8 100644 --- a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/transform/JsonTransformer.java +++ b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/transform/JsonTransformer.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.parameters.transform; import com.fasterxml.jackson.core.JsonProcessingException; diff --git a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/transform/TransformationManager.java b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/transform/TransformationManager.java index 00e6f84a9..d3fbce14f 100644 --- a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/transform/TransformationManager.java +++ b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/transform/TransformationManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,11 +11,11 @@ * limitations under the License. * */ -package software.amazon.lambda.powertools.parameters.transform; -import software.amazon.lambda.powertools.parameters.exception.TransformationException; +package software.amazon.lambda.powertools.parameters.transform; import java.lang.reflect.InvocationTargetException; +import software.amazon.lambda.powertools.parameters.exception.TransformationException; /** * Manager in charge of transforming parameter values in another format.
@@ -50,15 +50,18 @@ public boolean shouldTransform() { */ public String performBasicTransformation(String value) { if (transformer == null) { - throw new IllegalStateException("You cannot perform a transformation without Transformer, use the provider.withTransformation() method to specify it."); + throw new IllegalStateException( + "You cannot perform a transformation without Transformer, use the provider.withTransformation() method to specify it."); } if (!BasicTransformer.class.isAssignableFrom(transformer)) { throw new IllegalStateException("Wrong Transformer for a String, choose a BasicTransformer."); } try { - BasicTransformer basicTransformer = (BasicTransformer) transformer.getDeclaredConstructor().newInstance(null); + BasicTransformer basicTransformer = + (BasicTransformer) transformer.getDeclaredConstructor().newInstance(null); return basicTransformer.applyTransformation(value); - } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) { + } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | + InvocationTargetException e) { throw new TransformationException(e); } } @@ -66,19 +69,21 @@ public String performBasicTransformation(String value) { /** * Transform a String in a Java Object. * - * @param value the value to transform + * @param value the value to transform * @param targetClass the type of the target object. * @return the value transformed in an object ot type T. */ public T performComplexTransformation(String value, Class targetClass) { if (transformer == null) { - throw new IllegalStateException("You cannot perform a transformation without Transformer, use the provider.withTransformation() method to specify it."); + throw new IllegalStateException( + "You cannot perform a transformation without Transformer, use the provider.withTransformation() method to specify it."); } try { Transformer complexTransformer = transformer.getDeclaredConstructor().newInstance(null); return complexTransformer.applyTransformation(value, targetClass); - } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) { + } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | + InvocationTargetException e) { throw new TransformationException(e); } } diff --git a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/transform/Transformer.java b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/transform/Transformer.java index 3c57b2aa9..d9aea2644 100644 --- a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/transform/Transformer.java +++ b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/transform/Transformer.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.parameters.transform; import software.amazon.lambda.powertools.parameters.exception.TransformationException; @@ -34,7 +35,8 @@ public interface Transformer { /** * Apply a transformation on the input value (String) - * @param value the parameter value to transform + * + * @param value the parameter value to transform * @param targetClass class of the target object * @return a transformed parameter * @throws TransformationException when a transformation error occurs diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/AppConfigProviderTest.java b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/AppConfigProviderTest.java index 23f6271da..f467dca72 100644 --- a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/AppConfigProviderTest.java +++ b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/AppConfigProviderTest.java @@ -1,5 +1,24 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.parameters; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalStateException; +import static org.assertj.core.api.Assertions.assertThatRuntimeException; +import static org.mockito.MockitoAnnotations.openMocks; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; @@ -15,23 +34,18 @@ import software.amazon.lambda.powertools.parameters.cache.CacheManager; import software.amazon.lambda.powertools.parameters.transform.TransformationManager; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatIllegalStateException; -import static org.assertj.core.api.Assertions.assertThatRuntimeException; -import static org.mockito.MockitoAnnotations.openMocks; - public class AppConfigProviderTest { private final String environmentName = "test"; private final String applicationName = "fakeApp"; private final String defaultTestKey = "key1"; - + @Mock AppConfigDataClient client; - + @Captor ArgumentCaptor startSessionRequestCaptor; - + @Captor ArgumentCaptor getLatestConfigurationRequestCaptor; private AppConfigProvider provider; @@ -89,13 +103,17 @@ public void getValueRetrievesValue() { // Assert assertThat(returnedValue1).isEqualTo(firstResponse.configuration().asUtf8String()); assertThat(returnedValue2).isEqualTo(secondResponse.configuration().asUtf8String()); - assertThat(returnedValue3).isEqualTo(secondResponse.configuration().asUtf8String()); // Third response is mocked to return null and should re-use previous value + assertThat(returnedValue3).isEqualTo(secondResponse.configuration() + .asUtf8String()); // Third response is mocked to return null and should re-use previous value assertThat(startSessionRequestCaptor.getValue().applicationIdentifier()).isEqualTo(applicationName); assertThat(startSessionRequestCaptor.getValue().environmentIdentifier()).isEqualTo(environmentName); assertThat(startSessionRequestCaptor.getValue().configurationProfileIdentifier()).isEqualTo(defaultTestKey); - assertThat(getLatestConfigurationRequestCaptor.getAllValues().get(0).configurationToken()).isEqualTo(firstSession.initialConfigurationToken()); - assertThat(getLatestConfigurationRequestCaptor.getAllValues().get(1).configurationToken()).isEqualTo(firstResponse.nextPollConfigurationToken()); - assertThat(getLatestConfigurationRequestCaptor.getAllValues().get(2).configurationToken()).isEqualTo(secondResponse.nextPollConfigurationToken()); + assertThat(getLatestConfigurationRequestCaptor.getAllValues().get(0).configurationToken()).isEqualTo( + firstSession.initialConfigurationToken()); + assertThat(getLatestConfigurationRequestCaptor.getAllValues().get(1).configurationToken()).isEqualTo( + firstResponse.nextPollConfigurationToken()); + assertThat(getLatestConfigurationRequestCaptor.getAllValues().get(2).configurationToken()).isEqualTo( + secondResponse.nextPollConfigurationToken()); } @Test @@ -156,10 +174,14 @@ public void multipleKeysRetrievalWorks() { // Assert assertThat(firstKeyValue).isEqualTo(param1Response.configuration().asUtf8String()); assertThat(secondKeyValue).isEqualTo(param2Response.configuration().asUtf8String()); - assertThat(startSessionRequestCaptor.getAllValues().get(0).configurationProfileIdentifier()).isEqualTo(param1Key); - assertThat(startSessionRequestCaptor.getAllValues().get(1).configurationProfileIdentifier()).isEqualTo(param2Key); - assertThat(getLatestConfigurationRequestCaptor.getAllValues().get(0).configurationToken()).isEqualTo(param1Session.initialConfigurationToken()); - assertThat(getLatestConfigurationRequestCaptor.getAllValues().get(1).configurationToken()).isEqualTo(param2Session.initialConfigurationToken()); + assertThat(startSessionRequestCaptor.getAllValues().get(0).configurationProfileIdentifier()).isEqualTo( + param1Key); + assertThat(startSessionRequestCaptor.getAllValues().get(1).configurationProfileIdentifier()).isEqualTo( + param2Key); + assertThat(getLatestConfigurationRequestCaptor.getAllValues().get(0).configurationToken()).isEqualTo( + param1Session.initialConfigurationToken()); + assertThat(getLatestConfigurationRequestCaptor.getAllValues().get(1).configurationToken()).isEqualTo( + param2Session.initialConfigurationToken()); } diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/BaseProviderTest.java b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/BaseProviderTest.java index 8dd2d7658..edc671e2c 100644 --- a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/BaseProviderTest.java +++ b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/BaseProviderTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,20 +11,8 @@ * limitations under the License. * */ -package software.amazon.lambda.powertools.parameters; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import software.amazon.lambda.powertools.parameters.cache.CacheManager; -import software.amazon.lambda.powertools.parameters.transform.ObjectToDeserialize; -import software.amazon.lambda.powertools.parameters.transform.TransformationManager; -import software.amazon.lambda.powertools.parameters.transform.Transformer; -import java.time.Clock; -import java.time.temporal.ChronoUnit; -import java.util.Base64; -import java.util.HashMap; -import java.util.Map; +package software.amazon.lambda.powertools.parameters; import static java.time.Clock.offset; import static java.time.Duration.of; @@ -36,6 +24,18 @@ import static software.amazon.lambda.powertools.parameters.transform.Transformer.base64; import static software.amazon.lambda.powertools.parameters.transform.Transformer.json; +import java.time.Clock; +import java.time.temporal.ChronoUnit; +import java.util.Base64; +import java.util.HashMap; +import java.util.Map; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import software.amazon.lambda.powertools.parameters.cache.CacheManager; +import software.amazon.lambda.powertools.parameters.transform.ObjectToDeserialize; +import software.amazon.lambda.powertools.parameters.transform.TransformationManager; +import software.amazon.lambda.powertools.parameters.transform.Transformer; + public class BaseProviderTest { Clock clock; @@ -45,33 +45,6 @@ public class BaseProviderTest { boolean getFromStore = false; - class BasicProvider extends BaseProvider { - - public BasicProvider(CacheManager cacheManager) { - super(cacheManager); - } - - private String value = "valueFromStore"; - - public void setValue(String value) { - this.value = value; - } - - @Override - protected String getValue(String key) { - getFromStore = true; - return value; - } - - @Override - protected Map getMultipleValues(String path) { - getFromStore = true; - Map map = new HashMap<>(); - map.put(path, value); - return map; - } - } - @BeforeEach public void setup() { openMocks(this); @@ -224,10 +197,11 @@ public void get_basicTransformation_shouldTransformInString() { public void get_complexTransformation_shouldTransformInObject() { provider.setValue("{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}"); - ObjectToDeserialize objectToDeserialize = provider.withTransformation(json).get("foo", ObjectToDeserialize.class); + ObjectToDeserialize objectToDeserialize = + provider.withTransformation(json).get("foo", ObjectToDeserialize.class); assertThat(objectToDeserialize).matches( - o -> o.getFoo().equals("Foo") + o -> o.getFoo().equals("Foo") && o.getBar() == 42 && o.getBaz() == 123456789); } @@ -400,4 +374,31 @@ public void getTwoParams_shouldResetTransformationOptionsInBetween() { assertThat(foob64).isEqualTo("base64encoded"); assertThat(foostr).isEqualTo("string"); } + + class BasicProvider extends BaseProvider { + + private String value = "valueFromStore"; + + public BasicProvider(CacheManager cacheManager) { + super(cacheManager); + } + + public void setValue(String value) { + this.value = value; + } + + @Override + protected String getValue(String key) { + getFromStore = true; + return value; + } + + @Override + protected Map getMultipleValues(String path) { + getFromStore = true; + Map map = new HashMap<>(); + map.put(path, value); + return map; + } + } } diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/DynamoDbProviderE2ETest.java b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/DynamoDbProviderE2ETest.java index c9397676b..18212b45c 100644 --- a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/DynamoDbProviderE2ETest.java +++ b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/DynamoDbProviderE2ETest.java @@ -1,5 +1,23 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.parameters; +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.HashMap; +import java.util.Map; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; @@ -8,16 +26,11 @@ import software.amazon.awssdk.services.dynamodb.model.PutItemRequest; import software.amazon.lambda.powertools.parameters.cache.CacheManager; -import java.util.HashMap; -import java.util.Map; - -import static org.assertj.core.api.Assertions.assertThat; - /** * This class provides simple end-to-end style testing of the DynamoDBProvider class. * It is ignored, for now, as it requires AWS access and that's not yet run as part * of our unit test suite in the cloud. - * + *

* The test is kept here for 1/ local development and 2/ in preparation for future * E2E tests running in the cloud CI. Once the E2E test structure is merged we * will move this across. @@ -46,8 +59,8 @@ public void TestGetValue() { testItem.put("id", AttributeValue.fromS("test_param")); testItem.put("value", AttributeValue.fromS("the_value_is_hello!")); ddbClient.putItem(PutItemRequest.builder() - .tableName(ParamsTestTable) - .item(testItem) + .tableName(ParamsTestTable) + .item(testItem) .build()); // Act diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/DynamoDbProviderTest.java b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/DynamoDbProviderTest.java index d6818a64f..abfc9ab8a 100644 --- a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/DynamoDbProviderTest.java +++ b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/DynamoDbProviderTest.java @@ -1,5 +1,26 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.parameters; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalStateException; +import static org.mockito.MockitoAnnotations.openMocks; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -17,31 +38,18 @@ import software.amazon.lambda.powertools.parameters.exception.DynamoDbProviderSchemaException; import software.amazon.lambda.powertools.parameters.transform.TransformationManager; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatIllegalStateException; -import static org.mockito.MockitoAnnotations.openMocks; - public class DynamoDbProviderTest { + private final String tableName = "ddb-test-table"; @Mock DynamoDbClient client; - @Mock TransformationManager transformationManager; - @Captor ArgumentCaptor getItemValueCaptor; - @Captor ArgumentCaptor queryRequestCaptor; - - private DynamoDbProvider provider; - private final String tableName = "ddb-test-table"; @BeforeEach public void init() { @@ -114,9 +122,10 @@ public void getValueWithMalformedRowThrows() { .item(responseData) .build()); // Act - Assertions.assertThrows(DynamoDbProviderSchemaException.class, () -> { - provider.getValue(key); - }); + Assertions.assertThrows(DynamoDbProviderSchemaException.class, () -> + { + provider.getValue(key); + }); } @@ -180,10 +189,11 @@ public void getMultipleValuesMissingSortKey_throwsException() { Mockito.when(client.query(queryRequestCaptor.capture())).thenReturn(response); // Assert - Assertions.assertThrows(DynamoDbProviderSchemaException.class, () -> { - // Act - provider.getMultipleValues(key); - }); + Assertions.assertThrows(DynamoDbProviderSchemaException.class, () -> + { + // Act + provider.getMultipleValues(key); + }); } @Test @@ -200,10 +210,11 @@ public void getValuesWithMalformedRowThrows() { Mockito.when(client.query(queryRequestCaptor.capture())).thenReturn(response); // Assert - Assertions.assertThrows(DynamoDbProviderSchemaException.class, () -> { - // Act - provider.getMultipleValues(key); - }); + Assertions.assertThrows(DynamoDbProviderSchemaException.class, () -> + { + // Act + provider.getMultipleValues(key); + }); } @Test @@ -214,6 +225,7 @@ public void testDynamoDBBuilderMissingCacheManager_throwsException() { .withTable("table") .build()); } + @Test public void testDynamoDBBuilderMissingTable_throwsException() { diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/ParamManagerIntegrationTest.java b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/ParamManagerIntegrationTest.java index e1cb72be9..d6fbe66f0 100644 --- a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/ParamManagerIntegrationTest.java +++ b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/ParamManagerIntegrationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,8 +11,21 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.parameters; +import static org.apache.commons.lang3.reflect.FieldUtils.writeStaticField; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.openMocks; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import org.assertj.core.data.MapEntry; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -31,19 +44,6 @@ import software.amazon.awssdk.services.ssm.model.GetParametersByPathResponse; import software.amazon.awssdk.services.ssm.model.Parameter; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -import static org.apache.commons.lang3.reflect.FieldUtils.writeStaticField; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.mockito.MockitoAnnotations.openMocks; - public class ParamManagerIntegrationTest { @Mock @@ -51,21 +51,16 @@ public class ParamManagerIntegrationTest { @Mock DynamoDbClient ddbClient; - - @Mock - private AppConfigDataClient appConfigDataClient; - @Captor ArgumentCaptor ssmParamCaptor; - @Captor ArgumentCaptor ssmParamByPathCaptor; - @Mock SecretsManagerClient secretsManagerClient; - @Captor ArgumentCaptor secretsCaptor; + @Mock + private AppConfigDataClient appConfigDataClient; @BeforeEach public void setup() throws IllegalAccessException { diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/ParamManagerTest.java b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/ParamManagerTest.java index a21a6082c..b84fcf743 100644 --- a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/ParamManagerTest.java +++ b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/ParamManagerTest.java @@ -11,18 +11,19 @@ * limitations under the License. * */ -package software.amazon.lambda.powertools.parameters; -import org.junit.jupiter.api.Test; -import software.amazon.lambda.powertools.parameters.cache.CacheManager; -import software.amazon.lambda.powertools.parameters.internal.CustomProvider; -import software.amazon.lambda.powertools.parameters.transform.TransformationManager; +package software.amazon.lambda.powertools.parameters; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; import static org.assertj.core.api.Assertions.assertThatIllegalStateException; import static org.assertj.core.api.Assertions.assertThatRuntimeException; import static org.junit.jupiter.api.Assertions.assertNotNull; +import org.junit.jupiter.api.Test; +import software.amazon.lambda.powertools.parameters.cache.CacheManager; +import software.amazon.lambda.powertools.parameters.internal.CustomProvider; +import software.amazon.lambda.powertools.parameters.transform.TransformationManager; + public class ParamManagerTest { @Test diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/SSMProviderTest.java b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/SSMProviderTest.java index e55f3d7e6..6a5aa3e68 100644 --- a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/SSMProviderTest.java +++ b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/SSMProviderTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,8 +11,22 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.parameters; +import static java.util.Arrays.asList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalStateException; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.openMocks; + +import java.time.temporal.ChronoUnit; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; import org.assertj.core.data.MapEntry; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -28,20 +42,6 @@ import software.amazon.lambda.powertools.parameters.cache.CacheManager; import software.amazon.lambda.powertools.parameters.transform.TransformationManager; -import java.time.temporal.ChronoUnit; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -import static java.util.Arrays.asList; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatIllegalStateException; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.mockito.MockitoAnnotations.openMocks; - public class SSMProviderTest { @Mock @@ -165,7 +165,8 @@ public void getMultipleWithNextToken() { List parameters1 = new ArrayList<>(); parameters1.add(Parameter.builder().name("/prod/app1/key1").value("foo1").build()); parameters1.add(Parameter.builder().name("/prod/app1/key2").value("foo2").build()); - GetParametersByPathResponse response1 = GetParametersByPathResponse.builder().parameters(parameters1).nextToken("123abc").build(); + GetParametersByPathResponse response1 = + GetParametersByPathResponse.builder().parameters(parameters1).nextToken("123abc").build(); List parameters2 = new ArrayList<>(); parameters2.add(Parameter.builder().name("/prod/app1/key3").value("foo3").build()); @@ -184,11 +185,12 @@ public void getMultipleWithNextToken() { GetParametersByPathRequest request1 = requestParams.get(0); GetParametersByPathRequest request2 = requestParams.get(1); - assertThat(asList(request1, request2)).allSatisfy(req -> { - assertThat(req.path()).isEqualTo("/prod/app1"); - assertThat(req.withDecryption()).isFalse(); - assertThat(req.recursive()).isFalse(); - }); + assertThat(asList(request1, request2)).allSatisfy(req -> + { + assertThat(req.path()).isEqualTo("/prod/app1"); + assertThat(req.withDecryption()).isFalse(); + assertThat(req.recursive()).isFalse(); + }); assertThat(request1.nextToken()).isNull(); assertThat(request2.nextToken()).isEqualTo("123abc"); diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/SecretsProviderTest.java b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/SecretsProviderTest.java index 2ab72ffdd..f4f2d9bee 100644 --- a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/SecretsProviderTest.java +++ b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/SecretsProviderTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,8 +11,16 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.parameters; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalStateException; +import static org.assertj.core.api.Assertions.assertThatRuntimeException; +import static org.mockito.MockitoAnnotations.openMocks; + +import java.time.temporal.ChronoUnit; +import java.util.Base64; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; @@ -26,14 +34,6 @@ import software.amazon.lambda.powertools.parameters.cache.CacheManager; import software.amazon.lambda.powertools.parameters.transform.TransformationManager; -import java.time.temporal.ChronoUnit; -import java.util.Base64; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatIllegalStateException; -import static org.assertj.core.api.Assertions.assertThatRuntimeException; -import static org.mockito.MockitoAnnotations.openMocks; - public class SecretsProviderTest { @Mock @@ -76,7 +76,8 @@ public void getValueBase64() { String key = "Key2"; String expectedValue = "Value2"; byte[] valueb64 = Base64.getEncoder().encode(expectedValue.getBytes()); - GetSecretValueResponse response = GetSecretValueResponse.builder().secretBinary(SdkBytes.fromByteArray(valueb64)).build(); + GetSecretValueResponse response = + GetSecretValueResponse.builder().secretBinary(SdkBytes.fromByteArray(valueb64)).build(); Mockito.when(client.getSecretValue(paramCaptor.capture())).thenReturn(response); String value = provider.getValue(key); @@ -99,9 +100,9 @@ public void testSecretsProviderBuilderMissingCacheManager_throwsException() { // Act & Assert assertThatIllegalStateException().isThrownBy(() -> SecretsProvider.builder() - .withClient(client) - .withTransformationManager(transformationManager) - .build()) + .withClient(client) + .withTransformationManager(transformationManager) + .build()) .withMessage("No CacheManager provided, please provide one"); } } diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/cache/CacheManagerTest.java b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/cache/CacheManagerTest.java index 2464b4278..2bcfcc566 100644 --- a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/cache/CacheManagerTest.java +++ b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/cache/CacheManagerTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,19 +11,19 @@ * limitations under the License. * */ -package software.amazon.lambda.powertools.parameters.cache; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import java.time.Clock; -import java.util.Optional; +package software.amazon.lambda.powertools.parameters.cache; import static java.time.Clock.offset; import static java.time.Duration.of; import static java.time.temporal.ChronoUnit.SECONDS; import static org.assertj.core.api.Assertions.assertThat; +import java.time.Clock; +import java.util.Optional; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + public class CacheManagerTest { CacheManager manager; diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/cache/DataStoreTest.java b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/cache/DataStoreTest.java index c68992bf1..e86ded9be 100644 --- a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/cache/DataStoreTest.java +++ b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/cache/DataStoreTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,19 +11,19 @@ * limitations under the License. * */ -package software.amazon.lambda.powertools.parameters.cache; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import java.time.Clock; -import java.time.Instant; +package software.amazon.lambda.powertools.parameters.cache; import static java.time.Clock.offset; import static java.time.Duration.of; import static java.time.temporal.ChronoUnit.SECONDS; import static org.assertj.core.api.Assertions.assertThat; +import java.time.Clock; +import java.time.Instant; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + public class DataStoreTest { Clock clock; diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/internal/AnotherObject.java b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/internal/AnotherObject.java index b58ad7b3d..074a08844 100644 --- a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/internal/AnotherObject.java +++ b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/internal/AnotherObject.java @@ -1,11 +1,25 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.parameters.internal; public class AnotherObject { - public AnotherObject() {} - private String another; private int object; + public AnotherObject() { + } public String getAnother() { return another; diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/internal/CustomProvider.java b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/internal/CustomProvider.java index e58ef746c..2c9db3712 100644 --- a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/internal/CustomProvider.java +++ b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/internal/CustomProvider.java @@ -1,11 +1,24 @@ -package software.amazon.lambda.powertools.parameters.internal; +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ -import software.amazon.lambda.powertools.parameters.BaseProvider; -import software.amazon.lambda.powertools.parameters.cache.CacheManager; +package software.amazon.lambda.powertools.parameters.internal; import java.util.Base64; import java.util.HashMap; import java.util.Map; +import software.amazon.lambda.powertools.parameters.BaseProvider; +import software.amazon.lambda.powertools.parameters.cache.CacheManager; public class CustomProvider extends BaseProvider { diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/internal/LambdaParametersAspectTest.java b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/internal/LambdaParametersAspectTest.java index c390a051e..d346a1aa4 100644 --- a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/internal/LambdaParametersAspectTest.java +++ b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/internal/LambdaParametersAspectTest.java @@ -1,5 +1,27 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.parameters.internal; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.openMocks; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; @@ -12,14 +34,6 @@ import software.amazon.lambda.powertools.parameters.transform.JsonTransformer; import software.amazon.lambda.powertools.parameters.transform.ObjectToDeserialize; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.mockito.Mockito.mockStatic; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.mockito.MockitoAnnotations.openMocks; - public class LambdaParametersAspectTest { @Mock @@ -75,14 +89,17 @@ public void testWithComplexTransform() { .isInstanceOf(ObjectToDeserialize.class) .matches( o -> o.getFoo().equals("Foo") && - o.getBar() == 42 && - o.getBaz() == 123456789); + o.getBar() == 42 && + o.getBaz() == 123456789); } @Test public void testWithComplexTransformWrongTargetClass_ShouldThrowException() { assertThatExceptionOfType(TransformationException.class) - .isThrownBy(() -> {AnotherObject obj = wrongTransform; }); + .isThrownBy(() -> + { + AnotherObject obj = wrongTransform; + }); } } diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/transform/Base64TransformerTest.java b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/transform/Base64TransformerTest.java index 428b7e0ab..ea713b552 100644 --- a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/transform/Base64TransformerTest.java +++ b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/transform/Base64TransformerTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,16 +11,16 @@ * limitations under the License. * */ -package software.amazon.lambda.powertools.parameters.transform; -import org.junit.jupiter.api.Test; -import software.amazon.lambda.powertools.parameters.exception.TransformationException; - -import java.util.Base64; +package software.amazon.lambda.powertools.parameters.transform; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import java.util.Base64; +import org.junit.jupiter.api.Test; +import software.amazon.lambda.powertools.parameters.exception.TransformationException; + public class Base64TransformerTest { @Test diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/transform/JsonTransformerTest.java b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/transform/JsonTransformerTest.java index fe4fae0bb..5cb980cc7 100644 --- a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/transform/JsonTransformerTest.java +++ b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/transform/JsonTransformerTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,35 +11,38 @@ * limitations under the License. * */ -package software.amazon.lambda.powertools.parameters.transform; -import org.junit.jupiter.api.Test; -import software.amazon.lambda.powertools.parameters.exception.TransformationException; - -import java.util.Map; +package software.amazon.lambda.powertools.parameters.transform; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.assertj.core.data.MapEntry.entry; +import java.util.Map; +import org.junit.jupiter.api.Test; +import software.amazon.lambda.powertools.parameters.exception.TransformationException; + public class JsonTransformerTest { @Test public void transform_json_shouldTransformInObject() throws TransformationException { JsonTransformer transformation = new JsonTransformer<>(); - ObjectToDeserialize objectToDeserialize = transformation.applyTransformation("{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}", ObjectToDeserialize.class); + ObjectToDeserialize objectToDeserialize = + transformation.applyTransformation("{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}", + ObjectToDeserialize.class); assertThat(objectToDeserialize).matches( o -> o.getFoo().equals("Foo") - && o.getBar() == 42 - && o.getBaz() == 123456789); + && o.getBar() == 42 + && o.getBaz() == 123456789); } @Test public void transform_json_shouldTransformInHashMap() throws TransformationException { JsonTransformer transformation = new JsonTransformer<>(); - Map map = transformation.applyTransformation("{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}", Map.class); + Map map = + transformation.applyTransformation("{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}", Map.class); assertThat(map).contains( entry("foo", "Foo"), entry("bar", 42), @@ -51,6 +54,7 @@ public void transform_badJson_shouldThrowException() { JsonTransformer transformation = new JsonTransformer<>(); assertThatExceptionOfType(TransformationException.class) - .isThrownBy(() -> transformation.applyTransformation("{\"fo\":\"Foo\", \"bat\":42, \"bau\":123456789}", ObjectToDeserialize.class)); + .isThrownBy(() -> transformation.applyTransformation("{\"fo\":\"Foo\", \"bat\":42, \"bau\":123456789}", + ObjectToDeserialize.class)); } } diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/transform/ObjectToDeserialize.java b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/transform/ObjectToDeserialize.java index 0e1fd0f5c..1d09fbeda 100644 --- a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/transform/ObjectToDeserialize.java +++ b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/transform/ObjectToDeserialize.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,16 +11,16 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.parameters.transform; public class ObjectToDeserialize { - public ObjectToDeserialize() { - } - private String foo; private int bar; private long baz; + public ObjectToDeserialize() { + } public String getFoo() { return foo; diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/transform/TransformationManagerTest.java b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/transform/TransformationManagerTest.java index 6b6548071..39e69f9e0 100644 --- a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/transform/TransformationManagerTest.java +++ b/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/transform/TransformationManagerTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,13 +11,8 @@ * limitations under the License. * */ -package software.amazon.lambda.powertools.parameters.transform; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import software.amazon.lambda.powertools.parameters.exception.TransformationException; -import java.util.Base64; +package software.amazon.lambda.powertools.parameters.transform; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; @@ -25,6 +20,11 @@ import static software.amazon.lambda.powertools.parameters.transform.Transformer.base64; import static software.amazon.lambda.powertools.parameters.transform.Transformer.json; +import java.util.Base64; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import software.amazon.lambda.powertools.parameters.exception.TransformationException; + public class TransformationManagerTest { TransformationManager manager; @@ -90,7 +90,9 @@ public void performComplexTransformation_noTransformer_shouldThrowException() { public void performComplexTransformation_shouldPerformTransformation() { manager.setTransformer(json); - ObjectToDeserialize object = manager.performComplexTransformation("{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}", ObjectToDeserialize.class); + ObjectToDeserialize object = + manager.performComplexTransformation("{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}", + ObjectToDeserialize.class); assertThat(object).isNotNull(); } diff --git a/powertools-serialization/pom.xml b/powertools-serialization/pom.xml index f21ecb412..2a57f21e3 100644 --- a/powertools-serialization/pom.xml +++ b/powertools-serialization/pom.xml @@ -1,4 +1,18 @@ + + @@ -82,6 +96,10 @@ true + + org.apache.maven.plugins + maven-checkstyle-plugin + diff --git a/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/EventDeserializationException.java b/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/EventDeserializationException.java index 2b06c9256..ae97232b0 100644 --- a/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/EventDeserializationException.java +++ b/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/EventDeserializationException.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.utilities; public class EventDeserializationException extends RuntimeException { diff --git a/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/EventDeserializer.java b/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/EventDeserializer.java index f1b248fae..22712e8ce 100644 --- a/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/EventDeserializer.java +++ b/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/EventDeserializer.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,8 +11,13 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.utilities; +import static java.nio.charset.StandardCharsets.UTF_8; +import static software.amazon.lambda.powertools.utilities.jmespath.Base64Function.decode; +import static software.amazon.lambda.powertools.utilities.jmespath.Base64GZipFunction.decompress; + import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; import com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPEvent; import com.amazonaws.services.lambda.runtime.events.ActiveMQEvent; @@ -30,17 +35,12 @@ import com.amazonaws.services.lambda.runtime.events.ScheduledEvent; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectReader; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.io.IOException; import java.util.List; import java.util.Map; import java.util.stream.Collectors; - -import static java.nio.charset.StandardCharsets.UTF_8; -import static software.amazon.lambda.powertools.utilities.jmespath.Base64Function.decode; -import static software.amazon.lambda.powertools.utilities.jmespath.Base64GZipFunction.decompress; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Class that can be used to extract the meaningful part of an event and deserialize it into a Java object.
@@ -136,12 +136,14 @@ public static EventPart extractDataFrom(Object object) { .map(r -> decode(r.getData())) .collect(Collectors.toList())); } else if (object instanceof KinesisAnalyticsFirehoseInputPreprocessingEvent) { - KinesisAnalyticsFirehoseInputPreprocessingEvent event = (KinesisAnalyticsFirehoseInputPreprocessingEvent) object; + KinesisAnalyticsFirehoseInputPreprocessingEvent event = + (KinesisAnalyticsFirehoseInputPreprocessingEvent) object; return new EventPart(event.getRecords().stream() .map(r -> decode(r.getData())) .collect(Collectors.toList())); } else if (object instanceof KinesisAnalyticsStreamsInputPreprocessingEvent) { - KinesisAnalyticsStreamsInputPreprocessingEvent event = (KinesisAnalyticsStreamsInputPreprocessingEvent) object; + KinesisAnalyticsStreamsInputPreprocessingEvent event = + (KinesisAnalyticsStreamsInputPreprocessingEvent) object; return new EventPart(event.getRecords().stream() .map(r -> decode(r.getData())) .collect(Collectors.toList())); @@ -181,8 +183,9 @@ private EventPart(Object content) { /** * Deserialize this part of event from JSON to an object of type T + * * @param clazz the target type for deserialization - * @param type of object to return + * @param type of object to return * @return an Object of type T (deserialized from the content) */ public T as(Class clazz) { @@ -201,7 +204,8 @@ public T as(Class clazz) { return (T) contentObject; } if (contentList != null) { - throw new EventDeserializationException("The content of this event is a list, consider using 'asListOf' instead"); + throw new EventDeserializationException( + "The content of this event is a list, consider using 'asListOf' instead"); } // should not occur, except if the event is malformed (missing fields) throw new IllegalStateException("Event content is null: the event may be malformed (missing fields)"); @@ -212,14 +216,16 @@ public T as(Class clazz) { /** * Deserialize this part of event from JSON to a list of objects of type T + * * @param clazz the target type for deserialization - * @param type of object to return + * @param type of object to return * @return a list of objects of type T (deserialized from the content) */ public List asListOf(Class clazz) { if (contentList == null && content == null) { if (contentMap != null || contentObject != null) { - throw new EventDeserializationException("The content of this event is not a list, consider using 'as' instead"); + throw new EventDeserializationException( + "The content of this event is not a list, consider using 'as' instead"); } // should not occur, except if the event is really malformed throw new IllegalStateException("Event content is null: the event may be malformed (missing fields)"); @@ -229,16 +235,20 @@ public List asListOf(Class clazz) { try { return reader.readValue(content); } catch (JsonProcessingException e) { - throw new EventDeserializationException("Cannot load the event as a list of " + clazz.getSimpleName() + ", consider using 'as' instead", e); + throw new EventDeserializationException( + "Cannot load the event as a list of " + clazz.getSimpleName() + + ", consider using 'as' instead", e); } } else { - return contentList.stream().map(s -> { - try { - return s == null ? null : JsonConfig.get().getObjectMapper().reader().readValue(s, clazz); - } catch (IOException e) { - throw new EventDeserializationException("Cannot load the event as a list of " + clazz.getSimpleName(), e); - } - }).collect(Collectors.toList()); + return contentList.stream().map(s -> + { + try { + return s == null ? null : JsonConfig.get().getObjectMapper().reader().readValue(s, clazz); + } catch (IOException e) { + throw new EventDeserializationException( + "Cannot load the event as a list of " + clazz.getSimpleName(), e); + } + }).collect(Collectors.toList()); } } } diff --git a/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/JsonConfig.java b/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/JsonConfig.java index 549418263..f5a6d8c11 100644 --- a/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/JsonConfig.java +++ b/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/JsonConfig.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.utilities; import com.fasterxml.jackson.databind.JsonNode; @@ -25,19 +26,7 @@ import software.amazon.lambda.powertools.utilities.jmespath.JsonFunction; public class JsonConfig { - private JsonConfig() { - } - - private static class ConfigHolder { - private final static JsonConfig instance = new JsonConfig(); - } - - public static JsonConfig get() { - return ConfigHolder.instance; - } - private static final ThreadLocal om = ThreadLocal.withInitial(ObjectMapper::new); - private final FunctionRegistry defaultFunctions = FunctionRegistry.defaultRegistry(); private final FunctionRegistry customFunctions = defaultFunctions.extend( new Base64Function(), @@ -49,6 +38,12 @@ public static JsonConfig get() { .withFunctionRegistry(customFunctions) .build(); private JmesPath jmesPath = new JacksonRuntime(configuration, getObjectMapper()); + private JsonConfig() { + } + + public static JsonConfig get() { + return ConfigHolder.instance; + } /** * Return an Object Mapper. Use this to customize (de)serialization config. @@ -73,7 +68,7 @@ public JmesPath getJmesPath() { * {@link Base64Function} and {@link Base64GZipFunction} are already built-in. * * @param function the function to add - * @param Must extends {@link BaseFunction} + * @param Must extends {@link BaseFunction} */ public void addFunction(T function) { FunctionRegistry functionRegistryWithExtendedFunctions = configuration.functionRegistry().extend(function); @@ -84,4 +79,8 @@ public void addFunction(T function) { jmesPath = new JacksonRuntime(updatedConfig, getObjectMapper()); } + + private static class ConfigHolder { + private final static JsonConfig instance = new JsonConfig(); + } } diff --git a/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/jmespath/Base64Function.java b/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/jmespath/Base64Function.java index 737d96835..26b655fbd 100644 --- a/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/jmespath/Base64Function.java +++ b/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/jmespath/Base64Function.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,20 +11,20 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.utilities.jmespath; +import static java.nio.charset.StandardCharsets.UTF_8; + import io.burt.jmespath.Adapter; import io.burt.jmespath.JmesPathType; import io.burt.jmespath.function.ArgumentConstraints; import io.burt.jmespath.function.BaseFunction; import io.burt.jmespath.function.FunctionArgument; - import java.nio.ByteBuffer; import java.util.Base64; import java.util.List; -import static java.nio.charset.StandardCharsets.UTF_8; - /** * Function used by JMESPath to decode a Base64 encoded String into a decoded String */ @@ -34,16 +34,6 @@ public Base64Function() { super("powertools_base64", ArgumentConstraints.typeOf(JmesPathType.STRING)); } - @Override - protected T callFunction(Adapter runtime, List> arguments) { - T value = arguments.get(0).value(); - String encodedString = runtime.toString(value); - - String decodedString = decode(encodedString); - - return runtime.createString(decodedString); - } - public static String decode(String encodedString) { return new String(decode(encodedString.getBytes(UTF_8)), UTF_8); } @@ -55,4 +45,14 @@ public static String decode(ByteBuffer byteBuffer) { public static byte[] decode(byte[] encoded) { return Base64.getDecoder().decode(encoded); } + + @Override + protected T callFunction(Adapter runtime, List> arguments) { + T value = arguments.get(0).value(); + String encodedString = runtime.toString(value); + + String decodedString = decode(encodedString); + + return runtime.createString(decodedString); + } } diff --git a/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/jmespath/Base64GZipFunction.java b/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/jmespath/Base64GZipFunction.java index 8628fd1d2..f5d5beeb1 100644 --- a/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/jmespath/Base64GZipFunction.java +++ b/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/jmespath/Base64GZipFunction.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,25 +11,24 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.utilities.jmespath; +import static java.nio.charset.StandardCharsets.UTF_8; +import static software.amazon.lambda.powertools.utilities.jmespath.Base64Function.decode; + import io.burt.jmespath.Adapter; import io.burt.jmespath.JmesPathType; import io.burt.jmespath.function.ArgumentConstraints; import io.burt.jmespath.function.BaseFunction; import io.burt.jmespath.function.FunctionArgument; - import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStreamReader; -import java.util.Arrays; import java.util.List; import java.util.zip.GZIPInputStream; -import static java.nio.charset.StandardCharsets.UTF_8; -import static software.amazon.lambda.powertools.utilities.jmespath.Base64Function.decode; - /** * Function used by JMESPath to decode a Base64 encoded GZipped String into a decoded String */ @@ -39,20 +38,6 @@ public Base64GZipFunction() { super("powertools_base64_gzip", ArgumentConstraints.typeOf(JmesPathType.STRING)); } - @Override - protected T callFunction(Adapter runtime, List> arguments) { - T value = arguments.get(0).value(); - String encodedString = runtime.toString(value); - - String decompressString = decompress(decode(encodedString.getBytes(UTF_8))); - - if (decompressString == null) { - return runtime.createNull(); - } - - return runtime.createString(decompressString); - } - public static String decompress(byte[] compressed) { if (compressed == null || compressed.length == 0) { return null; @@ -77,6 +62,21 @@ public static String decompress(byte[] compressed) { } public static boolean isCompressed(final byte[] compressed) { - return (compressed[0] == (byte) (GZIPInputStream.GZIP_MAGIC)) && (compressed[1] == (byte) (GZIPInputStream.GZIP_MAGIC >> 8)); + return (compressed[0] == (byte) (GZIPInputStream.GZIP_MAGIC)) && + (compressed[1] == (byte) (GZIPInputStream.GZIP_MAGIC >> 8)); + } + + @Override + protected T callFunction(Adapter runtime, List> arguments) { + T value = arguments.get(0).value(); + String encodedString = runtime.toString(value); + + String decompressString = decompress(decode(encodedString.getBytes(UTF_8))); + + if (decompressString == null) { + return runtime.createNull(); + } + + return runtime.createString(decompressString); } } diff --git a/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/jmespath/JsonFunction.java b/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/jmespath/JsonFunction.java index 584b544bf..b7661b5af 100644 --- a/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/jmespath/JsonFunction.java +++ b/powertools-serialization/src/main/java/software/amazon/lambda/powertools/utilities/jmespath/JsonFunction.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.utilities.jmespath; import io.burt.jmespath.Adapter; @@ -18,7 +19,6 @@ import io.burt.jmespath.function.ArgumentConstraints; import io.burt.jmespath.function.BaseFunction; import io.burt.jmespath.function.FunctionArgument; - import java.util.List; public class JsonFunction extends BaseFunction { diff --git a/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/EventDeserializerTest.java b/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/EventDeserializerTest.java index 5055d7086..fcfdb47e3 100644 --- a/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/EventDeserializerTest.java +++ b/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/EventDeserializerTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,8 +11,13 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.utilities; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static software.amazon.lambda.powertools.utilities.EventDeserializer.extractDataFrom; + import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; import com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPEvent; import com.amazonaws.services.lambda.runtime.events.ActiveMQEvent; @@ -29,20 +34,15 @@ import com.amazonaws.services.lambda.runtime.events.SQSEvent; import com.amazonaws.services.lambda.runtime.events.ScheduledEvent; import com.amazonaws.services.lambda.runtime.tests.annotations.Event; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import software.amazon.lambda.powertools.utilities.model.Basket; import software.amazon.lambda.powertools.utilities.model.Order; import software.amazon.lambda.powertools.utilities.model.Product; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static software.amazon.lambda.powertools.utilities.EventDeserializer.extractDataFrom; - public class EventDeserializerTest { @Test @@ -61,7 +61,8 @@ public void testDeserializeStringAsObject_shouldReturnObject() { @Test public void testDeserializeStringArrayAsList_shouldReturnList() { - String productStr = "[{\"id\":1234, \"name\":\"product\", \"price\":42}, {\"id\":2345, \"name\":\"product2\", \"price\":43}]"; + String productStr = + "[{\"id\":1234, \"name\":\"product\", \"price\":42}, {\"id\":2345, \"name\":\"product2\", \"price\":43}]"; List products = extractDataFrom(productStr).asListOf(Product.class); assertThat(products).hasSize(2); assertProduct(products.get(0)); @@ -254,7 +255,8 @@ public void testDeserializeRabbitMQEventMessageAsListShouldReturnList(RabbitMQEv @ParameterizedTest @Event(value = "kasip_event.json", type = KinesisAnalyticsStreamsInputPreprocessingEvent.class) - public void testDeserializeKasipEventMessageAsListShouldReturnList(KinesisAnalyticsStreamsInputPreprocessingEvent event) { + public void testDeserializeKasipEventMessageAsListShouldReturnList( + KinesisAnalyticsStreamsInputPreprocessingEvent event) { List products = extractDataFrom(event).asListOf(Product.class); assertThat(products).hasSize(1); assertProduct(products.get(0)); @@ -262,7 +264,8 @@ public void testDeserializeKasipEventMessageAsListShouldReturnList(KinesisAnalyt @ParameterizedTest @Event(value = "kafip_event.json", type = KinesisAnalyticsFirehoseInputPreprocessingEvent.class) - public void testDeserializeKafipEventMessageAsListShouldReturnList(KinesisAnalyticsFirehoseInputPreprocessingEvent event) { + public void testDeserializeKafipEventMessageAsListShouldReturnList( + KinesisAnalyticsFirehoseInputPreprocessingEvent event) { List products = extractDataFrom(event).asListOf(Product.class); assertThat(products).hasSize(1); assertProduct(products.get(0)); diff --git a/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/jmespath/Base64FunctionTest.java b/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/jmespath/Base64FunctionTest.java index 5f243537c..d86af6671 100644 --- a/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/jmespath/Base64FunctionTest.java +++ b/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/jmespath/Base64FunctionTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,24 +11,26 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.utilities.jmespath; +import static org.assertj.core.api.Assertions.assertThat; + import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.JsonNodeType; import io.burt.jmespath.Expression; +import java.io.IOException; import org.junit.jupiter.api.Test; import software.amazon.lambda.powertools.utilities.JsonConfig; -import java.io.IOException; - -import static org.assertj.core.api.Assertions.assertThat; - public class Base64FunctionTest { @Test public void testPowertoolsBase64() throws IOException { - JsonNode event = JsonConfig.get().getObjectMapper().readTree(this.getClass().getResourceAsStream("/custom_event.json")); - Expression expression = JsonConfig.get().getJmesPath().compile("basket.powertools_base64(hiddenProduct)"); + JsonNode event = + JsonConfig.get().getObjectMapper().readTree(this.getClass().getResourceAsStream("/custom_event.json")); + Expression expression = + JsonConfig.get().getJmesPath().compile("basket.powertools_base64(hiddenProduct)"); JsonNode result = expression.search(event); assertThat(result.getNodeType()).isEqualTo(JsonNodeType.STRING); assertThat(result.asText()).isEqualTo("{\n" + diff --git a/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/jmespath/Base64GZipFunctionTest.java b/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/jmespath/Base64GZipFunctionTest.java index 8e574eba6..eeb605076 100644 --- a/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/jmespath/Base64GZipFunctionTest.java +++ b/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/jmespath/Base64GZipFunctionTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,26 +11,27 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.utilities.jmespath; +import static org.assertj.core.api.Assertions.assertThat; + import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.JsonNodeType; import io.burt.jmespath.Expression; import io.burt.jmespath.JmesPathType; +import java.io.IOException; import org.junit.jupiter.api.Test; import software.amazon.lambda.powertools.utilities.JsonConfig; -import java.io.IOException; - -import static org.assertj.core.api.Assertions.assertThat; - public class Base64GZipFunctionTest { @Test public void testConstructor() { Base64GZipFunction base64GZipFunction = new Base64GZipFunction(); assertThat(base64GZipFunction.name()).isEqualTo("powertools_base64_gzip"); - assertThat(base64GZipFunction.argumentConstraints().expectedType().toLowerCase()).isEqualTo(JmesPathType.STRING.name().toLowerCase()); + assertThat(base64GZipFunction.argumentConstraints().expectedType().toLowerCase()).isEqualTo( + JmesPathType.STRING.name().toLowerCase()); assertThat(base64GZipFunction.argumentConstraints().minArity()).isEqualTo(1); assertThat(base64GZipFunction.argumentConstraints().maxArity()).isEqualTo(1); @@ -38,8 +39,10 @@ public void testConstructor() { @Test public void testPowertoolsGzip() throws IOException { - JsonNode event = JsonConfig.get().getObjectMapper().readTree(this.getClass().getResourceAsStream("/custom_event_gzip.json")); - Expression expression = JsonConfig.get().getJmesPath().compile("basket.powertools_base64_gzip(hiddenProduct)"); + JsonNode event = JsonConfig.get().getObjectMapper() + .readTree(this.getClass().getResourceAsStream("/custom_event_gzip.json")); + Expression expression = + JsonConfig.get().getJmesPath().compile("basket.powertools_base64_gzip(hiddenProduct)"); JsonNode result = expression.search(event); assertThat(result.getNodeType()).isEqualTo(JsonNodeType.STRING); assertThat(result.asText()).isEqualTo("{ \"id\": 43242, \"name\": \"FooBar XY\", \"price\": 258}"); @@ -47,7 +50,8 @@ public void testPowertoolsGzip() throws IOException { @Test public void testPowertoolsGzipEmptyJsonAttribute() throws IOException { - JsonNode event = JsonConfig.get().getObjectMapper().readTree(this.getClass().getResourceAsStream("/custom_event_gzip.json")); + JsonNode event = JsonConfig.get().getObjectMapper() + .readTree(this.getClass().getResourceAsStream("/custom_event_gzip.json")); Expression expression = JsonConfig.get().getJmesPath().compile("basket.powertools_base64_gzip('')"); JsonNode result = expression.search(event); assertThat(result.getNodeType()).isEqualTo(JsonNodeType.NULL); @@ -55,7 +59,8 @@ public void testPowertoolsGzipEmptyJsonAttribute() throws IOException { @Test public void testPowertoolsGzipWrongArgumentType() throws IOException { - JsonNode event = JsonConfig.get().getObjectMapper().readTree(this.getClass().getResourceAsStream("/custom_event_gzip.json")); + JsonNode event = JsonConfig.get().getObjectMapper() + .readTree(this.getClass().getResourceAsStream("/custom_event_gzip.json")); Expression expression = JsonConfig.get().getJmesPath().compile("basket.powertools_base64_gzip(null)"); JsonNode result = expression.search(event); @@ -70,8 +75,10 @@ public void testBase64GzipDecompressNull() { @Test public void testPowertoolsGzipNotCompressedJsonAttribute() throws IOException { - JsonNode event = JsonConfig.get().getObjectMapper().readTree(this.getClass().getResourceAsStream("/custom_event_gzip.json")); - Expression expression = JsonConfig.get().getJmesPath().compile("basket.powertools_base64_gzip(encodedString)"); + JsonNode event = JsonConfig.get().getObjectMapper() + .readTree(this.getClass().getResourceAsStream("/custom_event_gzip.json")); + Expression expression = + JsonConfig.get().getJmesPath().compile("basket.powertools_base64_gzip(encodedString)"); JsonNode result = expression.search(event); assertThat(result.getNodeType()).isEqualTo(JsonNodeType.STRING); assertThat(result.asText()).isEqualTo("test"); diff --git a/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/jmespath/JsonFunctionTest.java b/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/jmespath/JsonFunctionTest.java index 4ea4eed35..0bfb635fa 100644 --- a/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/jmespath/JsonFunctionTest.java +++ b/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/jmespath/JsonFunctionTest.java @@ -1,20 +1,34 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.utilities.jmespath; +import static org.assertj.core.api.Assertions.assertThat; + import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.JsonNodeType; import io.burt.jmespath.Expression; +import java.io.IOException; import org.junit.jupiter.api.Test; import software.amazon.lambda.powertools.utilities.JsonConfig; -import java.io.IOException; - -import static org.assertj.core.api.Assertions.assertThat; - public class JsonFunctionTest { @Test public void testJsonFunction() throws IOException { - JsonNode event = JsonConfig.get().getObjectMapper().readTree(this.getClass().getResourceAsStream("/custom_event_json.json")); + JsonNode event = JsonConfig.get().getObjectMapper() + .readTree(this.getClass().getResourceAsStream("/custom_event_json.json")); Expression expression = JsonConfig.get().getJmesPath().compile("powertools_json(body)"); JsonNode result = expression.search(event); assertThat(result.getNodeType()).isEqualTo(JsonNodeType.OBJECT); @@ -25,7 +39,8 @@ public void testJsonFunction() throws IOException { @Test public void testJsonFunctionChild() throws IOException { - JsonNode event = JsonConfig.get().getObjectMapper().readTree(this.getClass().getResourceAsStream("/custom_event_json.json")); + JsonNode event = JsonConfig.get().getObjectMapper() + .readTree(this.getClass().getResourceAsStream("/custom_event_json.json")); Expression expression = JsonConfig.get().getJmesPath().compile("powertools_json(body).list[0].item"); JsonNode result = expression.search(event); assertThat(result.getNodeType()).isEqualTo(JsonNodeType.STRING); diff --git a/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/model/Basket.java b/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/model/Basket.java index 228089c52..4bf427a21 100644 --- a/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/model/Basket.java +++ b/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/model/Basket.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.utilities.model; import java.util.ArrayList; @@ -21,19 +22,19 @@ public class Basket { private List products = new ArrayList<>(); - public List getProducts() { - return products; + public Basket() { } - public void setProducts(List products) { - this.products = products; + public Basket(Product... p) { + products.addAll(Arrays.asList(p)); } - public Basket() { + public List getProducts() { + return products; } - public Basket( Product ...p){ - products.addAll(Arrays.asList(p)); + public void setProducts(List products) { + this.products = products; } public void add(Product product) { @@ -42,8 +43,12 @@ public void add(Product product) { @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } Basket basket = (Basket) o; return products.equals(basket.products); } diff --git a/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/model/Order.java b/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/model/Order.java index eca36c222..6b48ccd1d 100644 --- a/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/model/Order.java +++ b/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/model/Order.java @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.utilities.model; import java.util.HashMap; diff --git a/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/model/Product.java b/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/model/Product.java index f03f6d426..c90a4632e 100644 --- a/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/model/Product.java +++ b/powertools-serialization/src/test/java/software/amazon/lambda/powertools/utilities/model/Product.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.utilities.model; import java.util.Objects; @@ -57,8 +58,12 @@ public void setPrice(double price) { @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } Product product = (Product) o; return id == product.id && Double.compare(product.price, price) == 0 && Objects.equals(name, product.name); } diff --git a/powertools-sqs/pom.xml b/powertools-sqs/pom.xml index 618aa948c..d8afac783 100644 --- a/powertools-sqs/pom.xml +++ b/powertools-sqs/pom.xml @@ -1,4 +1,18 @@ + + @@ -117,4 +131,13 @@ + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + + + \ No newline at end of file diff --git a/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/SQSBatchProcessingException.java b/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/SQSBatchProcessingException.java index 85231a003..7adc2afe5 100644 --- a/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/SQSBatchProcessingException.java +++ b/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/SQSBatchProcessingException.java @@ -1,15 +1,27 @@ -package software.amazon.lambda.powertools.sqs; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ -import com.amazonaws.services.lambda.runtime.events.SQSEvent; +package software.amazon.lambda.powertools.sqs; import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; -import static java.util.Collections.*; +import static java.util.Collections.unmodifiableList; import static java.util.stream.Collectors.joining; +import com.amazonaws.services.lambda.runtime.events.SQSEvent; +import java.util.ArrayList; +import java.util.List; + /** *

* When one or more {@link SQSMessage} fails and if any exception is thrown from {@link SqsMessageHandler#process(SQSMessage)} @@ -47,6 +59,7 @@ public SQSBatchProcessingException(final List exceptions, /** * Details for exceptions that occurred while processing messages in {@link SqsMessageHandler#process(SQSMessage)} + * * @return List of exceptions that occurred while processing messages */ public List getExceptions() { @@ -55,6 +68,7 @@ public List getExceptions() { /** * List of returns from {@link SqsMessageHandler#process(SQSMessage)} that were successfully processed. + * * @return List of returns from successfully processed messages */ public List successMessageReturnValues() { @@ -63,6 +77,7 @@ public List successMessageReturnValues() { /** * Details of {@link SQSMessage} that failed in {@link SqsMessageHandler#process(SQSMessage)} + * * @return List of failed messages */ public List getFailures() { diff --git a/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/SqsBatch.java b/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/SqsBatch.java index cd529ff22..d0ffe6a73 100644 --- a/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/SqsBatch.java +++ b/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/SqsBatch.java @@ -1,14 +1,27 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.sqs; +import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; + +import com.amazonaws.services.lambda.runtime.events.SQSEvent; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import com.amazonaws.services.lambda.runtime.events.SQSEvent; - -import static com.amazonaws.services.lambda.runtime.events.SQSEvent.*; - /** * {@link SqsBatch} is used to process batch messages in {@link SQSEvent} * @@ -22,7 +35,7 @@ * will take care of deleting all the successful messages from SQS. When one or more single message fails processing due * to exception thrown from {@link SqsMessageHandler#process(SQSMessage)}, Lambda execution will fail * with {@link SQSBatchProcessingException}. - * + *

* If all the messages are successfully processes, No SQS messages are deleted explicitly but is rather delegated to * Lambda execution context for deletion. *

@@ -35,14 +48,14 @@ *

* If you want certain exceptions to be treated as permanent failures, i.e. exceptions where the result of retrying will * always be a failure and want these can be immediately moved to the dead letter queue associated to the source SQS queue, - * + *

* you can use {@link SqsBatch#nonRetryableExceptions()} to configure such exceptions. * Make sure function execution role has sqs:GetQueueAttributes permission on source SQS queue and sqs:SendMessage, * sqs:SendMessageBatch permission for configured DLQ. - * + *

* If you want such messages to be deleted instead, set {@link SqsBatch#deleteNonRetryableMessageFromQueue()} to true. * By default its value is false. - * + *

* If there is no DLQ configured on source SQS queue and {@link SqsBatch#nonRetryableExceptions()} attribute is set, if * nonRetryableExceptions occurs from {@link SqsMessageHandler}, such exceptions will still be treated as temporary * exceptions and the message will be moved back to source SQS queue for reprocessing. The same behaviour will occur if @@ -69,6 +82,7 @@ * * ... * + * * @see Amazon SQS dead-letter queues */ @Retention(RetentionPolicy.RUNTIME) diff --git a/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/SqsLargeMessage.java b/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/SqsLargeMessage.java index d96245006..847dd456c 100644 --- a/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/SqsLargeMessage.java +++ b/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/SqsLargeMessage.java @@ -1,3 +1,17 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.sqs; import java.lang.annotation.ElementType; @@ -58,7 +72,6 @@ * *

To disable deletion of payloads setting the following annotation parameter * {@code @SqsLargeMessage(deletePayloads=false)}

- * */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) diff --git a/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/SqsMessageHandler.java b/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/SqsMessageHandler.java index 17e37797c..0c8f03ee9 100644 --- a/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/SqsMessageHandler.java +++ b/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/SqsMessageHandler.java @@ -1,9 +1,23 @@ -package software.amazon.lambda.powertools.sqs; +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ -import com.amazonaws.services.lambda.runtime.events.SQSEvent; +package software.amazon.lambda.powertools.sqs; import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; +import com.amazonaws.services.lambda.runtime.events.SQSEvent; + /** *

* This interface should be implemented for processing {@link SQSMessage} inside {@link SQSEvent} received by lambda @@ -20,6 +34,7 @@ *

  • {@link SqsUtils#batchProcessor(SQSEvent, boolean, SqsMessageHandler)}
  • * *

    + * * @param Return value type from {@link SqsMessageHandler#process(SQSMessage)} */ @FunctionalInterface diff --git a/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/SqsUtils.java b/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/SqsUtils.java index 9fff4dc6f..8c06a6291 100644 --- a/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/SqsUtils.java +++ b/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/SqsUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,30 +11,28 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.sqs; +import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; +import static software.amazon.lambda.powertools.sqs.internal.SqsLargeMessageAspect.processMessages; + +import com.amazonaws.services.lambda.runtime.events.SQSEvent; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; import java.lang.reflect.Constructor; import java.util.ArrayList; -import java.util.LinkedList; import java.util.List; -import java.util.Queue; import java.util.function.Function; import java.util.stream.Collectors; - -import com.amazonaws.services.lambda.runtime.events.SQSEvent; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import software.amazon.awssdk.services.s3.S3Client; import software.amazon.awssdk.services.sqs.SqsClient; import software.amazon.lambda.powertools.sqs.exception.SkippedMessageDueToFailedBatchException; import software.amazon.lambda.powertools.sqs.internal.BatchContext; -import software.amazon.payloadoffloading.PayloadS3Pointer; import software.amazon.lambda.powertools.sqs.internal.SqsLargeMessageAspect; - -import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; -import static software.amazon.lambda.powertools.sqs.internal.SqsLargeMessageAspect.processMessages; +import software.amazon.payloadoffloading.PayloadS3Pointer; /** * A class of helper functions to add additional functionality to {@link SQSEvent} processing. @@ -43,11 +41,10 @@ public final class SqsUtils { private static final Logger LOG = LoggerFactory.getLogger(SqsUtils.class); private static final ObjectMapper objectMapper = new ObjectMapper(); - private static SqsClient client; - private static S3Client s3Client; - // The attribute on an SQS-FIFO message used to record the message group ID private static final String MESSAGE_GROUP_ID = "MessageGroupId"; + private static SqsClient client; + private static S3Client s3Client; private SqsUtils() { } @@ -176,23 +173,24 @@ public static List batchProcessor(final SQSEvent event, * If you want certain exceptions to be treated as permanent failures, i.e. exceptions where the result of retrying will * always be a failure and want these can be immediately moved to the dead letter queue associated to the source SQS queue, * you can use nonRetryableExceptions parameter to configure such exceptions. - * + *

    * Make sure function execution role has sqs:GetQueueAttributes permission on source SQS queue and sqs:SendMessage, * sqs:SendMessageBatch permission for configured DLQ. - * + *

    * If there is no DLQ configured on source SQS queue and {@link SqsBatch#nonRetryableExceptions()} attribute is set, if * nonRetryableExceptions occurs from {@link SqsMessageHandler}, such exceptions will still be treated as temporary * exceptions and the message will be moved back to source SQS queue for reprocessing. The same behaviour will occur if * for some reason the utility is unable to move the message to the DLQ. An example of this could be because the function * is missing the correct permissions. *

    - * @see Amazon SQS dead-letter queues - * @param event {@link SQSEvent} received by lambda function. - * @param handler Class implementing {@link SqsMessageHandler} which will be called for each message in event. + * + * @param event {@link SQSEvent} received by lambda function. + * @param handler Class implementing {@link SqsMessageHandler} which will be called for each message in event. * @param nonRetryableExceptions exception classes that are to be treated as permanent exceptions and to be moved * to DLQ. * @return List of values returned by {@link SqsMessageHandler#process(SQSMessage)} while processing each message. * @throws SQSBatchProcessingException if some messages fail during processing. + * @see Amazon SQS dead-letter queues */ @SafeVarargs public static List batchProcessor(final SQSEvent event, @@ -264,26 +262,26 @@ public static List batchProcessor(final SQSEvent event, * If you want certain exceptions to be treated as permanent failures, i.e. exceptions where the result of retrying will * always be a failure and want these can be immediately moved to the dead letter queue associated to the source SQS queue, * you can use nonRetryableExceptions parameter to configure such exceptions. - * + *

    * Make sure function execution role has sqs:GetQueueAttributes permission on source SQS queue and sqs:SendMessage, * sqs:SendMessageBatch permission for configured DLQ. - * + *

    * If there is no DLQ configured on source SQS queue and {@link SqsBatch#nonRetryableExceptions()} attribute is set, if * nonRetryableExceptions occurs from {@link SqsMessageHandler}, such exceptions will still be treated as temporary * exceptions and the message will be moved back to source SQS queue for reprocessing. The same behaviour will occur if * for some reason the utility is unable to move the message to the DLQ. An example of this could be because the function * is missing the correct permissions. *

    - * @see Amazon SQS dead-letter queues * - * @param event {@link SQSEvent} received by lambda function. - * @param suppressException if this is set to true, No {@link SQSBatchProcessingException} is thrown even on failed - * messages. - * @param handler Class implementing {@link SqsMessageHandler} which will be called for each message in event. + * @param event {@link SQSEvent} received by lambda function. + * @param suppressException if this is set to true, No {@link SQSBatchProcessingException} is thrown even on failed + * messages. + * @param handler Class implementing {@link SqsMessageHandler} which will be called for each message in event. * @param nonRetryableExceptions exception classes that are to be treated as permanent exceptions and to be moved * to DLQ. * @return List of values returned by {@link SqsMessageHandler#process(SQSMessage)} while processing each message. * @throws SQSBatchProcessingException if some messages fail during processing. + * @see Amazon SQS dead-letter queues */ @SafeVarargs public static List batchProcessor(final SQSEvent event, @@ -325,28 +323,29 @@ public static List batchProcessor(final SQSEvent event, * If you want certain exceptions to be treated as permanent failures, i.e. exceptions where the result of retrying will * always be a failure and want these can be immediately moved to the dead letter queue associated to the source SQS queue, * you can use nonRetryableExceptions parameter to configure such exceptions. - * + *

    * Make sure function execution role has sqs:GetQueueAttributes permission on source SQS queue and sqs:SendMessage, * sqs:SendMessageBatch permission for configured DLQ. - * + *

    * If you want such messages to be deleted instead, set deleteNonRetryableMessageFromQueue to true. - * + *

    * If there is no DLQ configured on source SQS queue and {@link SqsBatch#nonRetryableExceptions()} attribute is set, if * nonRetryableExceptions occurs from {@link SqsMessageHandler}, such exceptions will still be treated as temporary * exceptions and the message will be moved back to source SQS queue for reprocessing. The same behaviour will occur if * for some reason the utility is unable to move the message to the DLQ. An example of this could be because the function * is missing the correct permissions. *

    - * @see Amazon SQS dead-letter queues - * @param event {@link SQSEvent} received by lambda function. - * @param suppressException if this is set to true, No {@link SQSBatchProcessingException} is thrown even on failed - * messages. - * @param handler Class implementing {@link SqsMessageHandler} which will be called for each message in event. + * + * @param event {@link SQSEvent} received by lambda function. + * @param suppressException if this is set to true, No {@link SQSBatchProcessingException} is thrown even on failed + * messages. + * @param handler Class implementing {@link SqsMessageHandler} which will be called for each message in event. * @param deleteNonRetryableMessageFromQueue If messages with nonRetryableExceptions are to be deleted from SQS queue. - * @param nonRetryableExceptions exception classes that are to be treated as permanent exceptions and to be moved - * to DLQ. + * @param nonRetryableExceptions exception classes that are to be treated as permanent exceptions and to be moved + * to DLQ. * @return List of values returned by {@link SqsMessageHandler#process(SQSMessage)} while processing each message. * @throws SQSBatchProcessingException if some messages fail during processing. + * @see Amazon SQS dead-letter queues */ @SafeVarargs public static List batchProcessor(final SQSEvent event, @@ -356,7 +355,8 @@ public static List batchProcessor(final SQSEvent event, final Class... nonRetryableExceptions) { SqsMessageHandler handlerInstance = instantiatedHandler(handler); - return batchProcessor(event, suppressException, handlerInstance, deleteNonRetryableMessageFromQueue, nonRetryableExceptions); + return batchProcessor(event, suppressException, handlerInstance, deleteNonRetryableMessageFromQueue, + nonRetryableExceptions); } /** @@ -423,23 +423,24 @@ public static List batchProcessor(final SQSEvent event, * If you want certain exceptions to be treated as permanent failures, i.e. exceptions where the result of retrying will * always be a failure and want these can be immediately moved to the dead letter queue associated to the source SQS queue, * you can use nonRetryableExceptions parameter to configure such exceptions. - * + *

    * Make sure function execution role has sqs:GetQueueAttributes permission on source SQS queue and sqs:SendMessage, * sqs:SendMessageBatch permission for configured DLQ. - * + *

    * If there is no DLQ configured on source SQS queue and {@link SqsBatch#nonRetryableExceptions()} attribute is set, if * nonRetryableExceptions occurs from {@link SqsMessageHandler}, such exceptions will still be treated as temporary * exceptions and the message will be moved back to source SQS queue for reprocessing.The same behaviour will occur if * for some reason the utility is unable to moved the message to the DLQ. An example of this could be because the function * is missing the correct permissions. *

    - * @see Amazon SQS dead-letter queues - * @param event {@link SQSEvent} received by lambda function. - * @param handler Instance of class implementing {@link SqsMessageHandler} which will be called for each message in event. + * + * @param event {@link SQSEvent} received by lambda function. + * @param handler Instance of class implementing {@link SqsMessageHandler} which will be called for each message in event. * @param nonRetryableExceptions exception classes that are to be treated as permanent exceptions and to be moved * to DLQ. * @return List of values returned by {@link SqsMessageHandler#process(SQSMessage)} while processing each message. * @throws SQSBatchProcessingException if some messages fail during processing. + * @see Amazon SQS dead-letter queues */ @SafeVarargs public static List batchProcessor(final SQSEvent event, @@ -491,7 +492,7 @@ public static List batchProcessor(final SQSEvent event, final Class... nonRetryableExceptions) { final List handlerReturn = new ArrayList<>(); - if(client == null) { + if (client == null) { client = SqsClient.create(); } @@ -521,7 +522,8 @@ public static List batchProcessor(final SQSEvent event, String messageGroupId = message.getAttributes() != null ? message.getAttributes().get(MESSAGE_GROUP_ID) : null; if (messageGroupId != null) { - LOG.info("A message in a message batch with messageGroupId {} and messageId {} failed; failing the rest of the batch too" + LOG.info( + "A message in a message batch with messageGroupId {} and messageId {} failed; failing the rest of the batch too" , messageGroupId, message.getMessageId()); failedBatch = true; } @@ -534,14 +536,16 @@ public static List batchProcessor(final SQSEvent event, if (offset < event.getRecords().size()) { event.getRecords() .subList(offset, event.getRecords().size()) - .forEach(message -> { - LOG.info("Skipping message {} as another message with a message group failed in this batch", - message.getMessageId()); - batchContext.addFailure(message, new SkippedMessageDueToFailedBatchException()); - }); + .forEach(message -> + { + LOG.info("Skipping message {} as another message with a message group failed in this batch", + message.getMessageId()); + batchContext.addFailure(message, new SkippedMessageDueToFailedBatchException()); + }); } - batchContext.processSuccessAndHandleFailed(handlerReturn, suppressException, deleteNonRetryableMessageFromQueue, nonRetryableExceptions); + batchContext.processSuccessAndHandleFailed(handlerReturn, suppressException, deleteNonRetryableMessageFromQueue, + nonRetryableExceptions); return handlerReturn; } @@ -552,7 +556,8 @@ private static SqsMessageHandler instantiatedHandler(final Class> constructor = handler.getDeclaredConstructor(handler.getDeclaringClass()); + final Constructor> constructor = + handler.getDeclaredConstructor(handler.getDeclaringClass()); constructor.setAccessible(true); return constructor.newInstance(handler.getDeclaringClass().getDeclaredConstructor().newInstance()); } catch (Exception e) { @@ -576,7 +581,7 @@ public static ObjectMapper objectMapper() { } public static S3Client s3Client() { - if(null == s3Client) { + if (null == s3Client) { SqsUtils.s3Client = S3Client.create(); } diff --git a/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/exception/SkippedMessageDueToFailedBatchException.java b/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/exception/SkippedMessageDueToFailedBatchException.java index 9dbb66509..fbb4289d8 100644 --- a/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/exception/SkippedMessageDueToFailedBatchException.java +++ b/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/exception/SkippedMessageDueToFailedBatchException.java @@ -1,3 +1,17 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.sqs.exception; /** diff --git a/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/internal/BatchContext.java b/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/internal/BatchContext.java index 1e4eff3bf..57ddeb22f 100644 --- a/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/internal/BatchContext.java +++ b/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/internal/BatchContext.java @@ -1,5 +1,26 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.sqs.internal; +import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; +import static java.lang.String.format; +import static java.util.Optional.ofNullable; +import static java.util.stream.Collectors.toList; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -9,8 +30,6 @@ import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.IntStream; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import software.amazon.awssdk.core.SdkBytes; @@ -28,11 +47,6 @@ import software.amazon.lambda.powertools.sqs.SQSBatchProcessingException; import software.amazon.lambda.powertools.sqs.SqsUtils; -import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; -import static java.lang.String.format; -import static java.util.Optional.ofNullable; -import static java.util.stream.Collectors.toList; - public final class BatchContext { private static final Logger LOG = LoggerFactory.getLogger(BatchContext.class); private static final Map QUEUE_ARN_TO_DLQ_URL_MAPPING = new HashMap<>(); @@ -69,16 +83,17 @@ public final void processSuccessAndHandleFailed(final List successReturns exceptions.addAll(messageToException.values()); failedMessages.addAll(messageToException.keySet()); } else { - messageToException.forEach((sqsMessage, exception) -> { - boolean nonRetryableException = isNonRetryableException(exception, nonRetryableExceptions); - - if (nonRetryableException) { - nonRetryableMessageToException.put(sqsMessage, exception); - } else { - exceptions.add(exception); - failedMessages.add(sqsMessage); - } - }); + messageToException.forEach((sqsMessage, exception) -> + { + boolean nonRetryableException = isNonRetryableException(exception, nonRetryableExceptions); + + if (nonRetryableException) { + nonRetryableMessageToException.put(sqsMessage, exception); + } else { + exceptions.add(exception); + failedMessages.add(sqsMessage); + } + }); } List messagesToBeDeleted = new ArrayList<>(success); @@ -126,7 +141,8 @@ private boolean isNonRetryableException(Exception exception, Class aClass.isInstance(exception)); } - private boolean moveNonRetryableMessagesToDlqIfConfigured(Map nonRetryableMessageToException) { + private boolean moveNonRetryableMessagesToDlqIfConfigured( + Map nonRetryableMessageToException) { Optional dlqUrl = fetchDlqUrl(nonRetryableMessageToException); if (!dlqUrl.isPresent()) { @@ -134,76 +150,88 @@ private boolean moveNonRetryableMessagesToDlqIfConfigured(Map dlqMessages = nonRetryableMessageToException.keySet().stream() - .map(sqsMessage -> { - Map messageAttributesMap = new HashMap<>(); + .map(sqsMessage -> + { + Map messageAttributesMap = new HashMap<>(); - sqsMessage.getMessageAttributes().forEach((s, messageAttribute) -> { - MessageAttributeValue.Builder builder = MessageAttributeValue.builder(); + sqsMessage.getMessageAttributes().forEach((s, messageAttribute) -> + { + MessageAttributeValue.Builder builder = MessageAttributeValue.builder(); - builder - .dataType(messageAttribute.getDataType()) - .stringValue(messageAttribute.getStringValue()); + builder + .dataType(messageAttribute.getDataType()) + .stringValue(messageAttribute.getStringValue()); - if (null != messageAttribute.getBinaryValue()) { - builder.binaryValue(SdkBytes.fromByteBuffer(messageAttribute.getBinaryValue())); - } + if (null != messageAttribute.getBinaryValue()) { + builder.binaryValue(SdkBytes.fromByteBuffer(messageAttribute.getBinaryValue())); + } - messageAttributesMap.put(s, builder.build()); - }); + messageAttributesMap.put(s, builder.build()); + }); - return SendMessageBatchRequestEntry.builder() - .messageBody(sqsMessage.getBody()) - .id(sqsMessage.getMessageId()) - .messageAttributes(messageAttributesMap) - .build(); - }) + return SendMessageBatchRequestEntry.builder() + .messageBody(sqsMessage.getBody()) + .id(sqsMessage.getMessageId()) + .messageAttributes(messageAttributesMap) + .build(); + }) .collect(toList()); - List sendMessageBatchResponses = batchRequest(dlqMessages, 10, entriesToSend -> { + List sendMessageBatchResponses = batchRequest(dlqMessages, 10, entriesToSend -> + { - SendMessageBatchResponse sendMessageBatchResponse = client.sendMessageBatch(SendMessageBatchRequest.builder() - .entries(entriesToSend) - .queueUrl(dlqUrl.get()) - .build()); + SendMessageBatchResponse sendMessageBatchResponse = + client.sendMessageBatch(SendMessageBatchRequest.builder() + .entries(entriesToSend) + .queueUrl(dlqUrl.get()) + .build()); - LOG.debug("Response from send batch message to DLQ request {}", sendMessageBatchResponse); + LOG.debug("Response from send batch message to DLQ request {}", sendMessageBatchResponse); - return sendMessageBatchResponse; - }); + return sendMessageBatchResponse; + }); return sendMessageBatchResponses.stream() .filter(response -> null != response && response.hasFailed()) - .peek(sendMessageBatchResponse -> LOG.error("Failed sending message to the DLQ. Entire batch will be re processed. Check if needed permissions are configured for the function. Response: {}", sendMessageBatchResponse)) - .count() == 0; + .peek(sendMessageBatchResponse -> LOG.error( + "Failed sending message to the DLQ. Entire batch will be re processed. Check if needed permissions are configured for the function. Response: {}", + sendMessageBatchResponse)) + .count() == 0; } private Optional fetchDlqUrl(Map nonRetryableMessageToException) { return nonRetryableMessageToException.keySet().stream() .findFirst() - .map(sqsMessage -> QUEUE_ARN_TO_DLQ_URL_MAPPING.computeIfAbsent(sqsMessage.getEventSourceArn(), sourceArn -> { - String queueUrl = url(sourceArn); - - GetQueueAttributesResponse queueAttributes = client.getQueueAttributes(GetQueueAttributesRequest.builder() - .attributeNames(QueueAttributeName.REDRIVE_POLICY) - .queueUrl(queueUrl) - .build()); - - return ofNullable(queueAttributes.attributes().get(QueueAttributeName.REDRIVE_POLICY)) - .map(policy -> { - try { - return SqsUtils.objectMapper().readTree(policy); - } catch (JsonProcessingException e) { - LOG.debug("Unable to parse Re drive policy for queue {}. Even if DLQ exists, failed messages will be send back to main queue.", queueUrl, e); - return null; - } - }) - .map(node -> node.get("deadLetterTargetArn")) - .map(JsonNode::asText) - .map(this::url) - .orElse(null); - })); + .map(sqsMessage -> QUEUE_ARN_TO_DLQ_URL_MAPPING.computeIfAbsent(sqsMessage.getEventSourceArn(), + sourceArn -> + { + String queueUrl = url(sourceArn); + + GetQueueAttributesResponse queueAttributes = + client.getQueueAttributes(GetQueueAttributesRequest.builder() + .attributeNames(QueueAttributeName.REDRIVE_POLICY) + .queueUrl(queueUrl) + .build()); + + return ofNullable(queueAttributes.attributes().get(QueueAttributeName.REDRIVE_POLICY)) + .map(policy -> + { + try { + return SqsUtils.objectMapper().readTree(policy); + } catch (JsonProcessingException e) { + LOG.debug( + "Unable to parse Re drive policy for queue {}. Even if DLQ exists, failed messages will be send back to main queue.", + queueUrl, e); + return null; + } + }) + .map(node -> node.get("deadLetterTargetArn")) + .map(JsonNode::asText) + .map(this::url) + .orElse(null); + })); } private boolean hasFailures() { @@ -213,23 +241,25 @@ private boolean hasFailures() { private void deleteMessagesFromQueue(final List messages) { if (!messages.isEmpty()) { - List entries = messages.stream().map(m -> DeleteMessageBatchRequestEntry.builder() - .id(m.getMessageId()) - .receiptHandle(m.getReceiptHandle()) - .build()).collect(toList()); - - batchRequest(entries, 10, entriesToDelete -> { - DeleteMessageBatchRequest request = DeleteMessageBatchRequest.builder() - .queueUrl(url(messages.get(0).getEventSourceArn())) - .entries(entriesToDelete) - .build(); + List entries = + messages.stream().map(m -> DeleteMessageBatchRequestEntry.builder() + .id(m.getMessageId()) + .receiptHandle(m.getReceiptHandle()) + .build()).collect(toList()); + + batchRequest(entries, 10, entriesToDelete -> + { + DeleteMessageBatchRequest request = DeleteMessageBatchRequest.builder() + .queueUrl(url(messages.get(0).getEventSourceArn())) + .entries(entriesToDelete) + .build(); - DeleteMessageBatchResponse deleteMessageBatchResponse = client.deleteMessageBatch(request); + DeleteMessageBatchResponse deleteMessageBatchResponse = client.deleteMessageBatch(request); - LOG.debug("Response from delete request {}", deleteMessageBatchResponse); + LOG.debug("Response from delete request {}", deleteMessageBatchResponse); - return deleteMessageBatchResponse; - }); + return deleteMessageBatchResponse; + }); } } diff --git a/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/internal/SqsLargeMessageAspect.java b/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/internal/SqsLargeMessageAspect.java index 588d434d7..7022e399a 100644 --- a/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/internal/SqsLargeMessageAspect.java +++ b/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/internal/SqsLargeMessageAspect.java @@ -1,13 +1,31 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.sqs.internal; +import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; +import static java.lang.String.format; +import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.isHandlerMethod; +import static software.amazon.lambda.powertools.sqs.SqsUtils.s3Client; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.SQSEvent; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.function.Function; - -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.events.SQSEvent; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; @@ -24,63 +42,32 @@ import software.amazon.lambda.powertools.sqs.SqsLargeMessage; import software.amazon.payloadoffloading.PayloadS3Pointer; -import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; -import static java.lang.String.format; -import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.isHandlerMethod; -import static software.amazon.lambda.powertools.sqs.SqsUtils.s3Client; - @Aspect public class SqsLargeMessageAspect { private static final Logger LOG = LoggerFactory.getLogger(SqsLargeMessageAspect.class); - @SuppressWarnings({"EmptyMethod"}) - @Pointcut("@annotation(sqsLargeMessage)") - public void callAt(SqsLargeMessage sqsLargeMessage) { - } - - @Around(value = "callAt(sqsLargeMessage) && execution(@SqsLargeMessage * *.*(..))", argNames = "pjp,sqsLargeMessage") - public Object around(ProceedingJoinPoint pjp, - SqsLargeMessage sqsLargeMessage) throws Throwable { - Object[] proceedArgs = pjp.getArgs(); - - if (isHandlerMethod(pjp) - && placedOnSqsEventRequestHandler(pjp)) { - List pointersToDelete = rewriteMessages((SQSEvent) proceedArgs[0]); - - Object proceed = pjp.proceed(proceedArgs); - - if (sqsLargeMessage.deletePayloads()) { - pointersToDelete.forEach(SqsLargeMessageAspect::deleteMessage); - } - return proceed; - } - - return pjp.proceed(proceedArgs); - } - - private List rewriteMessages(SQSEvent sqsEvent) { - List records = sqsEvent.getRecords(); - return processMessages(records); - } - public static List processMessages(final List records) { List s3Pointers = new ArrayList<>(); for (SQSMessage sqsMessage : records) { if (isBodyLargeMessagePointer(sqsMessage.getBody())) { PayloadS3Pointer s3Pointer = Optional.ofNullable(PayloadS3Pointer.fromJson(sqsMessage.getBody())) - .orElseThrow(() -> new FailedProcessingLargePayloadException(format("Failed processing SQS body to extract S3 details. [ %s ].", sqsMessage.getBody()))); - - ResponseInputStream s3Object = callS3Gracefully(s3Pointer, pointer -> { - ResponseInputStream response = s3Client().getObject(GetObjectRequest.builder() - .bucket(pointer.getS3BucketName()) - .key(pointer.getS3Key()) - .build()); - - LOG.debug("Object downloaded with key: " + s3Pointer.getS3Key()); - return response; - }); + .orElseThrow(() -> new FailedProcessingLargePayloadException( + format("Failed processing SQS body to extract S3 details. [ %s ].", + sqsMessage.getBody()))); + + ResponseInputStream s3Object = callS3Gracefully(s3Pointer, pointer -> + { + ResponseInputStream response = + s3Client().getObject(GetObjectRequest.builder() + .bucket(pointer.getS3BucketName()) + .key(pointer.getS3Key()) + .build()); + + LOG.debug("Object downloaded with key: " + s3Pointer.getS3Key()); + return response; + }); sqsMessage.setBody(readStringFromS3Object(s3Object, s3Pointer)); s3Pointers.add(s3Pointer); @@ -100,31 +87,38 @@ private static String readStringFromS3Object(ResponseInputStream { - s3Client().deleteObject(DeleteObjectRequest.builder() - .bucket(pointer.getS3BucketName()) - .key(pointer.getS3Key()) - .build()); - LOG.info("Message deleted from S3: " + s3Pointer.toJson()); - return null; - }); + callS3Gracefully(s3Pointer, pointer -> + { + s3Client().deleteObject(DeleteObjectRequest.builder() + .bucket(pointer.getS3BucketName()) + .key(pointer.getS3Key()) + .build()); + LOG.info("Message deleted from S3: " + s3Pointer.toJson()); + return null; + }); } private static R callS3Gracefully(final PayloadS3Pointer pointer, - final Function function) { + final Function function) { try { return function.apply(pointer); } catch (S3Exception e) { LOG.error("A service exception", e); - throw new FailedProcessingLargePayloadException(format("Failed processing S3 record with [Bucket Name: %s Bucket Key: %s]", pointer.getS3BucketName(), pointer.getS3Key()), e); + throw new FailedProcessingLargePayloadException( + format("Failed processing S3 record with [Bucket Name: %s Bucket Key: %s]", + pointer.getS3BucketName(), pointer.getS3Key()), e); } catch (SdkClientException e) { LOG.error("Some sort of client exception", e); - throw new FailedProcessingLargePayloadException(format("Failed processing S3 record with [Bucket Name: %s Bucket Key: %s]", pointer.getS3BucketName(), pointer.getS3Key()), e); + throw new FailedProcessingLargePayloadException( + format("Failed processing S3 record with [Bucket Name: %s Bucket Key: %s]", + pointer.getS3BucketName(), pointer.getS3Key()), e); } } @@ -134,6 +128,36 @@ public static boolean placedOnSqsEventRequestHandler(ProceedingJoinPoint pjp) { && pjp.getArgs()[1] instanceof Context; } + @SuppressWarnings({"EmptyMethod"}) + @Pointcut("@annotation(sqsLargeMessage)") + public void callAt(SqsLargeMessage sqsLargeMessage) { + } + + @Around(value = "callAt(sqsLargeMessage) && execution(@SqsLargeMessage * *.*(..))", argNames = "pjp,sqsLargeMessage") + public Object around(ProceedingJoinPoint pjp, + SqsLargeMessage sqsLargeMessage) throws Throwable { + Object[] proceedArgs = pjp.getArgs(); + + if (isHandlerMethod(pjp) + && placedOnSqsEventRequestHandler(pjp)) { + List pointersToDelete = rewriteMessages((SQSEvent) proceedArgs[0]); + + Object proceed = pjp.proceed(proceedArgs); + + if (sqsLargeMessage.deletePayloads()) { + pointersToDelete.forEach(SqsLargeMessageAspect::deleteMessage); + } + return proceed; + } + + return pjp.proceed(proceedArgs); + } + + private List rewriteMessages(SQSEvent sqsEvent) { + List records = sqsEvent.getRecords(); + return processMessages(records); + } + public static class FailedProcessingLargePayloadException extends RuntimeException { public FailedProcessingLargePayloadException(String message, Throwable cause) { super(message, cause); diff --git a/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/internal/SqsMessageBatchProcessorAspect.java b/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/internal/SqsMessageBatchProcessorAspect.java index 73e91c3a7..ff0b5b014 100644 --- a/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/internal/SqsMessageBatchProcessorAspect.java +++ b/powertools-sqs/src/main/java/software/amazon/lambda/powertools/sqs/internal/SqsMessageBatchProcessorAspect.java @@ -1,5 +1,23 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.sqs.internal; +import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.isHandlerMethod; +import static software.amazon.lambda.powertools.sqs.SqsUtils.batchProcessor; +import static software.amazon.lambda.powertools.sqs.internal.SqsLargeMessageAspect.placedOnSqsEventRequestHandler; + import com.amazonaws.services.lambda.runtime.events.SQSEvent; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; @@ -7,10 +25,6 @@ import org.aspectj.lang.annotation.Pointcut; import software.amazon.lambda.powertools.sqs.SqsBatch; -import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.isHandlerMethod; -import static software.amazon.lambda.powertools.sqs.SqsUtils.batchProcessor; -import static software.amazon.lambda.powertools.sqs.internal.SqsLargeMessageAspect.placedOnSqsEventRequestHandler; - @Aspect public class SqsMessageBatchProcessorAspect { diff --git a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/SampleSqsHandler.java b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/SampleSqsHandler.java index d48cded5f..557aa214d 100644 --- a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/SampleSqsHandler.java +++ b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/SampleSqsHandler.java @@ -1,3 +1,17 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.sqs; import com.amazonaws.services.lambda.runtime.events.SQSEvent; diff --git a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/SqsUtilsBatchProcessorTest.java b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/SqsUtilsBatchProcessorTest.java index 43a089d2c..42e4b9d8f 100644 --- a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/SqsUtilsBatchProcessorTest.java +++ b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/SqsUtilsBatchProcessorTest.java @@ -1,14 +1,37 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.sqs; +import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.when; +import static software.amazon.lambda.powertools.sqs.SqsUtils.batchProcessor; +import static software.amazon.lambda.powertools.sqs.SqsUtils.overrideSqsClient; + +import com.amazonaws.services.lambda.runtime.events.SQSEvent; +import com.fasterxml.jackson.databind.ObjectMapper; import java.io.IOException; -import java.util.Collection; import java.util.HashMap; import java.util.List; -import java.util.function.Consumer; -import java.util.function.Function; -import com.amazonaws.services.lambda.runtime.events.SQSEvent; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -20,20 +43,6 @@ import software.amazon.awssdk.services.sqs.model.GetQueueAttributesResponse; import software.amazon.awssdk.services.sqs.model.QueueAttributeName; import software.amazon.awssdk.services.sqs.model.SendMessageBatchRequest; -import software.amazon.awssdk.services.sqs.model.SendMessageBatchRequestEntry; - -import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoInteractions; -import static org.mockito.Mockito.when; -import static software.amazon.lambda.powertools.sqs.SqsUtils.batchProcessor; -import static software.amazon.lambda.powertools.sqs.SqsUtils.overrideSqsClient; class SqsUtilsBatchProcessorTest { @@ -51,10 +60,11 @@ void setUp() throws IOException { @Test void shouldBatchProcessAndNotDeleteMessagesWhenAllSuccess() { - List returnValues = batchProcessor(event, false, (message) -> { - interactionClient.listQueues(); - return "Success"; - }); + List returnValues = batchProcessor(event, false, (message) -> + { + interactionClient.listQueues(); + return "Success"; + }); assertThat(returnValues) .hasSize(2) @@ -66,7 +76,8 @@ void shouldBatchProcessAndNotDeleteMessagesWhenAllSuccess() { @ParameterizedTest @ValueSource(classes = {SampleInnerSqsHandler.class, SampleSqsHandler.class}) - void shouldBatchProcessViaClassAndNotDeleteMessagesWhenAllSuccess(Class> handler) { + void shouldBatchProcessViaClassAndNotDeleteMessagesWhenAllSuccess( + Class> handler) { List returnValues = batchProcessor(event, handler); assertThat(returnValues) @@ -80,33 +91,35 @@ void shouldBatchProcessViaClassAndNotDeleteMessagesWhenAllSuccess(Class failedHandler = (message) -> { - if (failedId.equals(message.getMessageId())) { - throw new RuntimeException("Failed processing"); - } + SqsMessageHandler failedHandler = (message) -> + { + if (failedId.equals(message.getMessageId())) { + throw new RuntimeException("Failed processing"); + } - interactionClient.listQueues(); - return "Success"; - }; + interactionClient.listQueues(); + return "Success"; + }; assertThatExceptionOfType(SQSBatchProcessingException.class) .isThrownBy(() -> batchProcessor(event, failedHandler)) - .satisfies(e -> { + .satisfies(e -> + { - assertThat(e.successMessageReturnValues()) - .hasSize(1) - .contains("Success"); + assertThat(e.successMessageReturnValues()) + .hasSize(1) + .contains("Success"); - assertThat(e.getFailures()) - .hasSize(1) - .extracting("messageId") - .contains(failedId); + assertThat(e.getFailures()) + .hasSize(1) + .extracting("messageId") + .contains(failedId); - assertThat(e.getExceptions()) - .hasSize(1) - .extracting("detailMessage") - .contains("Failed processing"); - }); + assertThat(e.getExceptions()) + .hasSize(1) + .extracting("detailMessage") + .contains("Failed processing"); + }); verify(interactionClient).listQueues(); @@ -119,29 +132,31 @@ void shouldBatchProcessAndDeleteSuccessMessageOnPartialFailures() { @Test void shouldBatchProcessAndFullFailuresInBatch() { - SqsMessageHandler failedHandler = (message) -> { - throw new RuntimeException(message.getMessageId()); - }; + SqsMessageHandler failedHandler = (message) -> + { + throw new RuntimeException(message.getMessageId()); + }; assertThatExceptionOfType(SQSBatchProcessingException.class) .isThrownBy(() -> batchProcessor(event, failedHandler)) - .satisfies(e -> { + .satisfies(e -> + { - assertThat(e.successMessageReturnValues()) - .isEmpty(); + assertThat(e.successMessageReturnValues()) + .isEmpty(); - assertThat(e.getFailures()) - .hasSize(2) - .extracting("messageId") - .containsExactly("059f36b4-87a3-44ab-83d2-661975830a7d", - "2e1424d4-f796-459a-8184-9c92662be6da"); + assertThat(e.getFailures()) + .hasSize(2) + .extracting("messageId") + .containsExactly("059f36b4-87a3-44ab-83d2-661975830a7d", + "2e1424d4-f796-459a-8184-9c92662be6da"); - assertThat(e.getExceptions()) - .hasSize(2) - .extracting("detailMessage") - .containsExactly("059f36b4-87a3-44ab-83d2-661975830a7d", - "2e1424d4-f796-459a-8184-9c92662be6da"); - }); + assertThat(e.getExceptions()) + .hasSize(2) + .extracting("detailMessage") + .containsExactly("059f36b4-87a3-44ab-83d2-661975830a7d", + "2e1424d4-f796-459a-8184-9c92662be6da"); + }); verifyNoInteractions(sqsClient); } @@ -150,22 +165,23 @@ void shouldBatchProcessAndFullFailuresInBatch() { void shouldBatchProcessViaClassAndDeleteSuccessMessageOnPartialFailures() { assertThatExceptionOfType(SQSBatchProcessingException.class) .isThrownBy(() -> batchProcessor(event, FailureSampleInnerSqsHandler.class)) - .satisfies(e -> { + .satisfies(e -> + { - assertThat(e.successMessageReturnValues()) - .hasSize(1) - .contains("Success"); + assertThat(e.successMessageReturnValues()) + .hasSize(1) + .contains("Success"); - assertThat(e.getFailures()) - .hasSize(1) - .extracting("messageId") - .contains("2e1424d4-f796-459a-8184-9c92662be6da"); + assertThat(e.getFailures()) + .hasSize(1) + .extracting("messageId") + .contains("2e1424d4-f796-459a-8184-9c92662be6da"); - assertThat(e.getExceptions()) - .hasSize(1) - .extracting("detailMessage") - .contains("Failed processing"); - }); + assertThat(e.getExceptions()) + .hasSize(1) + .extracting("detailMessage") + .contains("Failed processing"); + }); verify(sqsClient).deleteMessageBatch(any(DeleteMessageBatchRequest.class)); } @@ -175,14 +191,15 @@ void shouldBatchProcessViaClassAndDeleteSuccessMessageOnPartialFailures() { void shouldBatchProcessAndSuppressExceptions() { String failedId = "2e1424d4-f796-459a-8184-9c92662be6da"; - SqsMessageHandler failedHandler = (message) -> { - if (failedId.equals(message.getMessageId())) { - throw new RuntimeException("Failed processing"); - } + SqsMessageHandler failedHandler = (message) -> + { + if (failedId.equals(message.getMessageId())) { + throw new RuntimeException("Failed processing"); + } - interactionClient.listQueues(); - return "Success"; - }; + interactionClient.listQueues(); + return "Success"; + }; List returnValues = batchProcessor(event, true, failedHandler); @@ -206,16 +223,6 @@ void shouldBatchProcessViaClassAndSuppressExceptions() { verify(sqsClient).deleteMessageBatch(any(DeleteMessageBatchRequest.class)); } - public class SampleInnerSqsHandler implements SqsMessageHandler { - private int counter; - - @Override - public String process(SQSMessage message) { - interactionClient.listQueues(); - return String.valueOf(counter++); - } - } - @Test void shouldBatchProcessAndMoveNonRetryableExceptionToDlq() { String failedId = "2e1424d4-f796-459a-8184-9c92662be6da"; @@ -226,18 +233,20 @@ void shouldBatchProcessAndMoveNonRetryableExceptionToDlq() { " \"maxReceiveCount\": 2\n" + "}"); - when(sqsClient.getQueueAttributes(any(GetQueueAttributesRequest.class))).thenReturn(GetQueueAttributesResponse.builder() + when(sqsClient.getQueueAttributes(any(GetQueueAttributesRequest.class))).thenReturn( + GetQueueAttributesResponse.builder() .attributes(attributes) - .build()); + .build()); - List batchProcessor = batchProcessor(event, (message) -> { - if (failedId.equals(message.getMessageId())) { - throw new IllegalStateException("Failed processing"); - } + List batchProcessor = batchProcessor(event, (message) -> + { + if (failedId.equals(message.getMessageId())) { + throw new IllegalStateException("Failed processing"); + } - interactionClient.listQueues(); - return "Success"; - }, IllegalStateException.class, IllegalArgumentException.class); + interactionClient.listQueues(); + return "Success"; + }, IllegalStateException.class, IllegalArgumentException.class); assertThat(batchProcessor) .hasSize(1); @@ -255,18 +264,20 @@ void shouldBatchProcessAndDeleteNonRetryableException() { " \"maxReceiveCount\": 2\n" + "}"); - when(sqsClient.getQueueAttributes(any(GetQueueAttributesRequest.class))).thenReturn(GetQueueAttributesResponse.builder() - .attributes(attributes) - .build()); + when(sqsClient.getQueueAttributes(any(GetQueueAttributesRequest.class))).thenReturn( + GetQueueAttributesResponse.builder() + .attributes(attributes) + .build()); - List batchProcessor = batchProcessor(event, false, (message) -> { - if (failedId.equals(message.getMessageId())) { - throw new IllegalStateException("Failed processing"); - } + List batchProcessor = batchProcessor(event, false, (message) -> + { + if (failedId.equals(message.getMessageId())) { + throw new IllegalStateException("Failed processing"); + } - interactionClient.listQueues(); - return "Success"; - }, true, IllegalStateException.class, IllegalArgumentException.class); + interactionClient.listQueues(); + return "Success"; + }, true, IllegalStateException.class, IllegalArgumentException.class); assertThat(batchProcessor) .hasSize(1); @@ -277,26 +288,28 @@ void shouldBatchProcessAndDeleteNonRetryableException() { @Test void shouldDeleteSuccessfulMessageInBatchesOfT10orLess() throws IOException { - SQSEvent batch25Message = MAPPER.readValue(this.getClass().getResource("/sampleSqsBatchEventBatchSize25.json"), SQSEvent.class); + SQSEvent batch25Message = + MAPPER.readValue(this.getClass().getResource("/sampleSqsBatchEventBatchSize25.json"), SQSEvent.class); assertThatExceptionOfType(SQSBatchProcessingException.class) .isThrownBy(() -> batchProcessor(batch25Message, FailureSampleInnerSqsHandler.class)) - .satisfies(e -> { + .satisfies(e -> + { - assertThat(e.successMessageReturnValues()) - .hasSize(24) - .contains("Success"); + assertThat(e.successMessageReturnValues()) + .hasSize(24) + .contains("Success"); - assertThat(e.getFailures()) - .hasSize(1) - .extracting("messageId") - .contains("2e1424d4-f796-459a-8184-9c92662be6da"); + assertThat(e.getFailures()) + .hasSize(1) + .extracting("messageId") + .contains("2e1424d4-f796-459a-8184-9c92662be6da"); - assertThat(e.getExceptions()) - .hasSize(1) - .extracting("detailMessage") - .contains("Failed processing"); - }); + assertThat(e.getExceptions()) + .hasSize(1) + .extracting("detailMessage") + .contains("Failed processing"); + }); ArgumentCaptor captor = ArgumentCaptor.forClass(DeleteMessageBatchRequest.class); @@ -310,7 +323,8 @@ void shouldDeleteSuccessfulMessageInBatchesOfT10orLess() throws IOException { @Test void shouldBatchProcessAndMoveNonRetryableExceptionToDlqInBatchesOfT10orLess() throws IOException { - SQSEvent batch25Message = MAPPER.readValue(this.getClass().getResource("/sampleSqsBatchEventBatchSize25.json"), SQSEvent.class); + SQSEvent batch25Message = + MAPPER.readValue(this.getClass().getResource("/sampleSqsBatchEventBatchSize25.json"), SQSEvent.class); HashMap attributes = new HashMap<>(); @@ -319,18 +333,20 @@ void shouldBatchProcessAndMoveNonRetryableExceptionToDlqInBatchesOfT10orLess() t " \"maxReceiveCount\": 2\n" + "}"); - when(sqsClient.getQueueAttributes(any(GetQueueAttributesRequest.class))).thenReturn(GetQueueAttributesResponse.builder() - .attributes(attributes) - .build()); + when(sqsClient.getQueueAttributes(any(GetQueueAttributesRequest.class))).thenReturn( + GetQueueAttributesResponse.builder() + .attributes(attributes) + .build()); - List batchProcessor = batchProcessor(batch25Message, (message) -> { - if ("2e1424d4-f796-459a-8184-9c92662be6da".equals(message.getMessageId())) { - interactionClient.listQueues(); - return "Success"; - } + List batchProcessor = batchProcessor(batch25Message, (message) -> + { + if ("2e1424d4-f796-459a-8184-9c92662be6da".equals(message.getMessageId())) { + interactionClient.listQueues(); + return "Success"; + } - throw new IllegalStateException("Failed processing"); - }, IllegalStateException.class, IllegalArgumentException.class); + throw new IllegalStateException("Failed processing"); + }, IllegalStateException.class, IllegalArgumentException.class); assertThat(batchProcessor) .hasSize(1); @@ -346,6 +362,16 @@ void shouldBatchProcessAndMoveNonRetryableExceptionToDlqInBatchesOfT10orLess() t .hasSize(24); } + public class SampleInnerSqsHandler implements SqsMessageHandler { + private int counter; + + @Override + public String process(SQSMessage message) { + interactionClient.listQueues(); + return String.valueOf(counter++); + } + } + public class FailureSampleInnerSqsHandler implements SqsMessageHandler { @Override public String process(SQSEvent.SQSMessage message) { diff --git a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/SqsUtilsFifoBatchProcessorTest.java b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/SqsUtilsFifoBatchProcessorTest.java index cfa79dc36..53beeefcb 100644 --- a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/SqsUtilsFifoBatchProcessorTest.java +++ b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/SqsUtilsFifoBatchProcessorTest.java @@ -1,30 +1,44 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.sqs; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static software.amazon.lambda.powertools.sqs.SqsUtils.batchProcessor; +import static software.amazon.lambda.powertools.sqs.SqsUtils.overrideSqsClient; + import com.amazonaws.services.lambda.runtime.events.SQSEvent; import com.amazonaws.services.lambda.runtime.tests.EventLoader; -import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.IOException; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.mockito.*; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoSession; import org.mockito.quality.Strictness; import software.amazon.awssdk.services.sqs.SqsClient; import software.amazon.awssdk.services.sqs.model.DeleteMessageBatchRequest; import software.amazon.awssdk.services.sqs.model.DeleteMessageBatchRequestEntry; import software.amazon.awssdk.services.sqs.model.DeleteMessageBatchResponse; -import java.io.IOException; -import java.util.List; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.stream.Collectors; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.MockitoAnnotations.openMocks; -import static software.amazon.lambda.powertools.sqs.SqsUtils.batchProcessor; -import static software.amazon.lambda.powertools.sqs.SqsUtils.overrideSqsClient; - public class SqsUtilsFifoBatchProcessorTest { private static SQSEvent sqsBatchEvent; @@ -61,10 +75,11 @@ public void tearDown() { public void processWholeBatch() { // Act AtomicInteger processedCount = new AtomicInteger(); - List results = batchProcessor(sqsBatchEvent, false, (message) -> { - processedCount.getAndIncrement(); - return true; - }); + List results = batchProcessor(sqsBatchEvent, false, (message) -> + { + processedCount.getAndIncrement(); + return true; + }); // Assert assertThat(processedCount.get()).isEqualTo(3); @@ -80,31 +95,34 @@ public void processWholeBatch() { @Test public void singleFailureInMiddleOfBatch() { // Arrange - Mockito.when(sqsClient.deleteMessageBatch(deleteMessageBatchCaptor.capture())).thenReturn(DeleteMessageBatchResponse - .builder().build()); + Mockito.when(sqsClient.deleteMessageBatch(deleteMessageBatchCaptor.capture())) + .thenReturn(DeleteMessageBatchResponse + .builder().build()); // Act AtomicInteger processedCount = new AtomicInteger(); assertThatExceptionOfType(SQSBatchProcessingException.class) - .isThrownBy(() -> batchProcessor(sqsBatchEvent, false, (message) -> { - int value = processedCount.getAndIncrement(); - if (value == 1) { - throw new RuntimeException("Whoops"); - } - return true; - })) - - // Assert + .isThrownBy(() -> batchProcessor(sqsBatchEvent, false, (message) -> + { + int value = processedCount.getAndIncrement(); + if (value == 1) { + throw new RuntimeException("Whoops"); + } + return true; + })) + + // Assert .isInstanceOf(SQSBatchProcessingException.class) - .satisfies(e -> { - List failures = ((SQSBatchProcessingException)e).getFailures(); - assertThat(failures.size()).isEqualTo(2); - List failureIds = failures.stream() - .map(SQSEvent.SQSMessage::getMessageId) - .collect(Collectors.toList()); - assertThat(failureIds).contains(sqsBatchEvent.getRecords().get(1).getMessageId()); - assertThat(failureIds).contains(sqsBatchEvent.getRecords().get(2).getMessageId()); - }); + .satisfies(e -> + { + List failures = ((SQSBatchProcessingException) e).getFailures(); + assertThat(failures.size()).isEqualTo(2); + List failureIds = failures.stream() + .map(SQSEvent.SQSMessage::getMessageId) + .collect(Collectors.toList()); + assertThat(failureIds).contains(sqsBatchEvent.getRecords().get(1).getMessageId()); + assertThat(failureIds).contains(sqsBatchEvent.getRecords().get(2).getMessageId()); + }); DeleteMessageBatchRequest deleteRequest = deleteMessageBatchCaptor.getValue(); List messageIds = deleteRequest.entries().stream() @@ -119,20 +137,22 @@ public void singleFailureInMiddleOfBatch() { public void singleFailureAtEndOfBatch() { // Arrange - Mockito.when(sqsClient.deleteMessageBatch(deleteMessageBatchCaptor.capture())).thenReturn(DeleteMessageBatchResponse - .builder().build()); + Mockito.when(sqsClient.deleteMessageBatch(deleteMessageBatchCaptor.capture())) + .thenReturn(DeleteMessageBatchResponse + .builder().build()); // Act AtomicInteger processedCount = new AtomicInteger(); assertThatExceptionOfType(SQSBatchProcessingException.class) - .isThrownBy(() -> batchProcessor(sqsBatchEvent, false, (message) -> { - int value = processedCount.getAndIncrement(); - if (value == 2) { - throw new RuntimeException("Whoops"); - } - return true; - })); + .isThrownBy(() -> batchProcessor(sqsBatchEvent, false, (message) -> + { + int value = processedCount.getAndIncrement(); + if (value == 2) { + throw new RuntimeException("Whoops"); + } + return true; + })); // Assert DeleteMessageBatchRequest deleteRequest = deleteMessageBatchCaptor.getValue(); @@ -150,17 +170,19 @@ public void messageFailureStopsGroupProcessing() { String groupToFail = sqsBatchEvent.getRecords().get(0).getAttributes().get("MessageGroupId"); assertThatExceptionOfType(SQSBatchProcessingException.class) - .isThrownBy(() -> batchProcessor(sqsBatchEvent, (message) -> { - String groupId = message.getAttributes().get("MessageGroupId"); - if (groupId.equals(groupToFail)) { - throw new RuntimeException("Failed processing"); - } - return groupId; - })) - .satisfies(e -> { - assertThat(e.successMessageReturnValues().size()).isEqualTo(0); - assertThat(e.successMessageReturnValues().contains(groupToFail)).isFalse(); - }); + .isThrownBy(() -> batchProcessor(sqsBatchEvent, (message) -> + { + String groupId = message.getAttributes().get("MessageGroupId"); + if (groupId.equals(groupToFail)) { + throw new RuntimeException("Failed processing"); + } + return groupId; + })) + .satisfies(e -> + { + assertThat(e.successMessageReturnValues().size()).isEqualTo(0); + assertThat(e.successMessageReturnValues().contains(groupToFail)).isFalse(); + }); } } diff --git a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/SqsUtilsLargeMessageTest.java b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/SqsUtilsLargeMessageTest.java index 48de3e6a9..d3b675371 100644 --- a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/SqsUtilsLargeMessageTest.java +++ b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/SqsUtilsLargeMessageTest.java @@ -1,13 +1,37 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.sqs; +import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; +import static java.util.Collections.singletonList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.openMocks; + +import com.amazonaws.services.lambda.runtime.events.SQSEvent; import java.io.ByteArrayInputStream; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.function.Consumer; import java.util.stream.Stream; - -import com.amazonaws.services.lambda.runtime.events.SQSEvent; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -28,23 +52,21 @@ import software.amazon.awssdk.utils.StringInputStream; import software.amazon.lambda.powertools.sqs.internal.SqsLargeMessageAspect; -import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; -import static java.util.Collections.singletonList; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoInteractions; -import static org.mockito.Mockito.when; -import static org.mockito.MockitoAnnotations.openMocks; - class SqsUtilsLargeMessageTest { - @Mock - private S3Client s3Client; private static final String BUCKET_NAME = "ms-extended-sqs-client"; private static final String BUCKET_KEY = "c71eb2ae-37e0-4265-8909-32f4153faddf"; + @Mock + private S3Client s3Client; + + private static Stream exception() { + return Stream.of(Arguments.of(S3Exception.builder() + .message("Service Exception") + .build()), + Arguments.of(SdkClientException.builder() + .message("Client Exception") + .build())); + } @BeforeEach void setUp() { @@ -54,16 +76,21 @@ void setUp() { @Test public void testLargeMessage() { - ResponseInputStream s3Response = new ResponseInputStream<>(GetObjectResponse.builder().build(), AbortableInputStream.create(new ByteArrayInputStream("A big message".getBytes()))); + ResponseInputStream s3Response = + new ResponseInputStream<>(GetObjectResponse.builder().build(), + AbortableInputStream.create(new ByteArrayInputStream("A big message".getBytes()))); when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(s3Response); - SQSEvent sqsEvent = messageWithBody("[\"software.amazon.payloadoffloading.PayloadS3Pointer\",{\"s3BucketName\":\"" + BUCKET_NAME + "\",\"s3Key\":\"" + BUCKET_KEY + "\"}]"); + SQSEvent sqsEvent = messageWithBody( + "[\"software.amazon.payloadoffloading.PayloadS3Pointer\",{\"s3BucketName\":\"" + BUCKET_NAME + + "\",\"s3Key\":\"" + BUCKET_KEY + "\"}]"); - Map sqsMessage = SqsUtils.enrichedMessageFromS3(sqsEvent, sqsMessages -> { - Map someBusinessLogic = new HashMap<>(); - someBusinessLogic.put("Message", sqsMessages.get(0).getBody()); - return someBusinessLogic; - }); + Map sqsMessage = SqsUtils.enrichedMessageFromS3(sqsEvent, sqsMessages -> + { + Map someBusinessLogic = new HashMap<>(); + someBusinessLogic.put("Message", sqsMessages.get(0).getBody()); + return someBusinessLogic; + }); assertThat(sqsMessage) .hasSize(1) @@ -74,29 +101,35 @@ public void testLargeMessage() { verify(s3Client).deleteObject(delete.capture()); Assertions.assertThat(delete.getValue()) - .satisfies((Consumer) deleteObjectRequest -> { - assertThat(deleteObjectRequest.bucket()) - .isEqualTo(BUCKET_NAME); + .satisfies((Consumer) deleteObjectRequest -> + { + assertThat(deleteObjectRequest.bucket()) + .isEqualTo(BUCKET_NAME); - assertThat(deleteObjectRequest.key()) - .isEqualTo(BUCKET_KEY); - }); + assertThat(deleteObjectRequest.key()) + .isEqualTo(BUCKET_KEY); + }); } @ParameterizedTest @ValueSource(booleans = {true, false}) public void testLargeMessageDeleteFromS3Toggle(boolean deleteS3Payload) { - ResponseInputStream s3Response = new ResponseInputStream<>(GetObjectResponse.builder().build(), AbortableInputStream.create(new ByteArrayInputStream("A big message".getBytes()))); + ResponseInputStream s3Response = + new ResponseInputStream<>(GetObjectResponse.builder().build(), + AbortableInputStream.create(new ByteArrayInputStream("A big message".getBytes()))); when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(s3Response); - SQSEvent sqsEvent = messageWithBody("[\"software.amazon.payloadoffloading.PayloadS3Pointer\",{\"s3BucketName\":\"" + BUCKET_NAME + "\",\"s3Key\":\"" + BUCKET_KEY + "\"}]"); + SQSEvent sqsEvent = messageWithBody( + "[\"software.amazon.payloadoffloading.PayloadS3Pointer\",{\"s3BucketName\":\"" + BUCKET_NAME + + "\",\"s3Key\":\"" + BUCKET_KEY + "\"}]"); - Map sqsMessage = SqsUtils.enrichedMessageFromS3(sqsEvent, deleteS3Payload, sqsMessages -> { - Map someBusinessLogic = new HashMap<>(); - someBusinessLogic.put("Message", sqsMessages.get(0).getBody()); - return someBusinessLogic; - }); + Map sqsMessage = SqsUtils.enrichedMessageFromS3(sqsEvent, deleteS3Payload, sqsMessages -> + { + Map someBusinessLogic = new HashMap<>(); + someBusinessLogic.put("Message", sqsMessages.get(0).getBody()); + return someBusinessLogic; + }); assertThat(sqsMessage) .hasSize(1) @@ -107,13 +140,14 @@ public void testLargeMessageDeleteFromS3Toggle(boolean deleteS3Payload) { verify(s3Client).deleteObject(delete.capture()); Assertions.assertThat(delete.getValue()) - .satisfies((Consumer) deleteObjectRequest -> { - assertThat(deleteObjectRequest.bucket()) - .isEqualTo(BUCKET_NAME); - - assertThat(deleteObjectRequest.key()) - .isEqualTo(BUCKET_KEY); - }); + .satisfies((Consumer) deleteObjectRequest -> + { + assertThat(deleteObjectRequest.bucket()) + .isEqualTo(BUCKET_NAME); + + assertThat(deleteObjectRequest.key()) + .isEqualTo(BUCKET_KEY); + }); } else { verify(s3Client, never()).deleteObject(any(DeleteObjectRequest.class)); } @@ -121,17 +155,20 @@ public void testLargeMessageDeleteFromS3Toggle(boolean deleteS3Payload) { @Test public void shouldNotProcessSmallMessageBody() { - ResponseInputStream s3Response = new ResponseInputStream<>(GetObjectResponse.builder().build(), AbortableInputStream.create(new ByteArrayInputStream("A big message".getBytes()))); + ResponseInputStream s3Response = + new ResponseInputStream<>(GetObjectResponse.builder().build(), + AbortableInputStream.create(new ByteArrayInputStream("A big message".getBytes()))); when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(s3Response); SQSEvent sqsEvent = messageWithBody("This is small message"); - Map sqsMessage = SqsUtils.enrichedMessageFromS3(sqsEvent, sqsMessages -> { - Map someBusinessLogic = new HashMap<>(); - someBusinessLogic.put("Message", sqsMessages.get(0).getBody()); - return someBusinessLogic; - }); + Map sqsMessage = SqsUtils.enrichedMessageFromS3(sqsEvent, sqsMessages -> + { + Map someBusinessLogic = new HashMap<>(); + someBusinessLogic.put("Message", sqsMessages.get(0).getBody()); + return someBusinessLogic; + }); assertThat(sqsMessage) .containsEntry("Message", "This is small message"); @@ -144,7 +181,9 @@ public void shouldNotProcessSmallMessageBody() { public void shouldFailEntireBatchIfFailedDownloadingFromS3(RuntimeException exception) { when(s3Client.getObject(any(GetObjectRequest.class))).thenThrow(exception); - String messageBody = "[\"software.amazon.payloadoffloading.PayloadS3Pointer\",{\"s3BucketName\":\"" + BUCKET_NAME + "\",\"s3Key\":\"" + BUCKET_KEY + "\"}]"; + String messageBody = + "[\"software.amazon.payloadoffloading.PayloadS3Pointer\",{\"s3BucketName\":\"" + BUCKET_NAME + + "\",\"s3Key\":\"" + BUCKET_KEY + "\"}]"; SQSEvent sqsEvent = messageWithBody(messageBody); assertThatExceptionOfType(SqsLargeMessageAspect.FailedProcessingLargePayloadException.class) @@ -156,16 +195,20 @@ public void shouldFailEntireBatchIfFailedDownloadingFromS3(RuntimeException exce @Test public void shouldFailEntireBatchIfFailedProcessingDownloadMessageFromS3() { - ResponseInputStream s3Response = new ResponseInputStream<>(GetObjectResponse.builder().build(), AbortableInputStream.create(new StringInputStream("test") { - @Override - public void close() throws IOException { - throw new IOException("Failed"); - } - })); + ResponseInputStream s3Response = + new ResponseInputStream<>(GetObjectResponse.builder().build(), + AbortableInputStream.create(new StringInputStream("test") { + @Override + public void close() throws IOException { + throw new IOException("Failed"); + } + })); when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(s3Response); - String messageBody = "[\"software.amazon.payloadoffloading.PayloadS3Pointer\",{\"s3BucketName\":\"" + BUCKET_NAME + "\",\"s3Key\":\"" + BUCKET_KEY + "\"}]"; + String messageBody = + "[\"software.amazon.payloadoffloading.PayloadS3Pointer\",{\"s3BucketName\":\"" + BUCKET_NAME + + "\",\"s3Key\":\"" + BUCKET_KEY + "\"}]"; SQSEvent sqsEvent = messageWithBody(messageBody); assertThatExceptionOfType(SqsLargeMessageAspect.FailedProcessingLargePayloadException.class) @@ -175,15 +218,6 @@ public void close() throws IOException { verify(s3Client, never()).deleteObject(any(DeleteObjectRequest.class)); } - private static Stream exception() { - return Stream.of(Arguments.of(S3Exception.builder() - .message("Service Exception") - .build()), - Arguments.of(SdkClientException.builder() - .message("Client Exception") - .build())); - } - private SQSEvent messageWithBody(String messageBody) { SQSMessage sqsMessage = new SQSMessage(); sqsMessage.setBody(messageBody); diff --git a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/LambdaHandlerApiGateway.java b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/LambdaHandlerApiGateway.java index b0d8177ac..3bad9644f 100644 --- a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/LambdaHandlerApiGateway.java +++ b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/LambdaHandlerApiGateway.java @@ -1,11 +1,25 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.sqs.handlers; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; -import software.amazon.lambda.powertools.sqs.SqsLargeMessage; import software.amazon.lambda.powertools.sqs.SampleSqsHandler; import software.amazon.lambda.powertools.sqs.SqsBatch; +import software.amazon.lambda.powertools.sqs.SqsLargeMessage; public class LambdaHandlerApiGateway implements RequestHandler { diff --git a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/PartialBatchFailureSuppressedHandler.java b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/PartialBatchFailureSuppressedHandler.java index 63f1573bf..172179057 100644 --- a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/PartialBatchFailureSuppressedHandler.java +++ b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/PartialBatchFailureSuppressedHandler.java @@ -1,14 +1,28 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.sqs.handlers; +import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; +import static software.amazon.lambda.powertools.sqs.internal.SqsMessageBatchProcessorAspectTest.interactionClient; + import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.lambda.runtime.events.SQSEvent; import software.amazon.lambda.powertools.sqs.SqsBatch; import software.amazon.lambda.powertools.sqs.SqsMessageHandler; -import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; -import static software.amazon.lambda.powertools.sqs.internal.SqsMessageBatchProcessorAspectTest.interactionClient; - public class PartialBatchFailureSuppressedHandler implements RequestHandler { @Override @SqsBatch(value = InnerMessageHandler.class, suppressException = true) diff --git a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/PartialBatchPartialFailureHandler.java b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/PartialBatchPartialFailureHandler.java index 653459d82..6e3971269 100644 --- a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/PartialBatchPartialFailureHandler.java +++ b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/PartialBatchPartialFailureHandler.java @@ -1,14 +1,28 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.sqs.handlers; +import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; +import static software.amazon.lambda.powertools.sqs.internal.SqsMessageBatchProcessorAspectTest.interactionClient; + import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.lambda.runtime.events.SQSEvent; import software.amazon.lambda.powertools.sqs.SqsBatch; import software.amazon.lambda.powertools.sqs.SqsMessageHandler; -import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; -import static software.amazon.lambda.powertools.sqs.internal.SqsMessageBatchProcessorAspectTest.interactionClient; - public class PartialBatchPartialFailureHandler implements RequestHandler { @Override @SqsBatch(InnerMessageHandler.class) diff --git a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/PartialBatchSuccessHandler.java b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/PartialBatchSuccessHandler.java index 926cdb4f5..acfcd7109 100644 --- a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/PartialBatchSuccessHandler.java +++ b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/PartialBatchSuccessHandler.java @@ -1,14 +1,28 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.sqs.handlers; +import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; +import static software.amazon.lambda.powertools.sqs.internal.SqsMessageBatchProcessorAspectTest.interactionClient; + import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.lambda.runtime.events.SQSEvent; import software.amazon.lambda.powertools.sqs.SqsBatch; import software.amazon.lambda.powertools.sqs.SqsMessageHandler; -import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; -import static software.amazon.lambda.powertools.sqs.internal.SqsMessageBatchProcessorAspectTest.interactionClient; - public class PartialBatchSuccessHandler implements RequestHandler { @Override @SqsBatch(InnerMessageHandler.class) diff --git a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/SqsMessageHandler.java b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/SqsMessageHandler.java index ee8c100e6..de096679f 100644 --- a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/SqsMessageHandler.java +++ b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/SqsMessageHandler.java @@ -1,3 +1,17 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.sqs.handlers; import com.amazonaws.services.lambda.runtime.Context; diff --git a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/SqsMessageHandlerWithNonRetryableHandler.java b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/SqsMessageHandlerWithNonRetryableHandler.java index 6eec87301..74ff02e2c 100644 --- a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/SqsMessageHandlerWithNonRetryableHandler.java +++ b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/SqsMessageHandlerWithNonRetryableHandler.java @@ -1,18 +1,33 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.sqs.handlers; +import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; +import static software.amazon.lambda.powertools.sqs.internal.SqsMessageBatchProcessorAspectTest.interactionClient; + import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.lambda.runtime.events.SQSEvent; import software.amazon.lambda.powertools.sqs.SqsBatch; import software.amazon.lambda.powertools.sqs.SqsMessageHandler; -import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; -import static software.amazon.lambda.powertools.sqs.internal.SqsMessageBatchProcessorAspectTest.interactionClient; - public class SqsMessageHandlerWithNonRetryableHandler implements RequestHandler { @Override - @SqsBatch(value = InnerMessageHandler.class, nonRetryableExceptions = {IllegalStateException.class, IllegalArgumentException.class}) + @SqsBatch(value = InnerMessageHandler.class, nonRetryableExceptions = {IllegalStateException.class, + IllegalArgumentException.class}) public String handleRequest(final SQSEvent sqsEvent, final Context context) { return "Success"; @@ -22,11 +37,11 @@ private class InnerMessageHandler implements SqsMessageHandler { @Override public String process(SQSMessage message) { - if(message.getMessageId().isEmpty()) { + if (message.getMessageId().isEmpty()) { throw new IllegalArgumentException("Invalid message and was moved to DLQ"); } - if("2e1424d4-f796-459a-9696-9c92662ba5da".equals(message.getMessageId())) { + if ("2e1424d4-f796-459a-9696-9c92662ba5da".equals(message.getMessageId())) { throw new RuntimeException("Invalid message and should be reprocessed"); } diff --git a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/SqsMessageHandlerWithNonRetryableHandlerWithDelete.java b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/SqsMessageHandlerWithNonRetryableHandlerWithDelete.java index 789a7b86d..5b341880e 100644 --- a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/SqsMessageHandlerWithNonRetryableHandlerWithDelete.java +++ b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/SqsMessageHandlerWithNonRetryableHandlerWithDelete.java @@ -1,14 +1,28 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.sqs.handlers; +import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; +import static software.amazon.lambda.powertools.sqs.internal.SqsMessageBatchProcessorAspectTest.interactionClient; + import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.lambda.runtime.events.SQSEvent; import software.amazon.lambda.powertools.sqs.SqsBatch; import software.amazon.lambda.powertools.sqs.SqsMessageHandler; -import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; -import static software.amazon.lambda.powertools.sqs.internal.SqsMessageBatchProcessorAspectTest.interactionClient; - public class SqsMessageHandlerWithNonRetryableHandlerWithDelete implements RequestHandler { @Override @@ -24,11 +38,11 @@ private class InnerMessageHandler implements SqsMessageHandler { @Override public String process(SQSMessage message) { - if(message.getMessageId().isEmpty()) { + if (message.getMessageId().isEmpty()) { throw new IllegalArgumentException("Invalid message and was moved to DLQ"); } - if("2e1424d4-f796-459a-9696-9c92662ba5da".equals(message.getMessageId())) { + if ("2e1424d4-f796-459a-9696-9c92662ba5da".equals(message.getMessageId())) { throw new RuntimeException("Invalid message and should be reprocessed"); } diff --git a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/SqsNoDeleteMessageHandler.java b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/SqsNoDeleteMessageHandler.java index 337592004..e96dc5581 100644 --- a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/SqsNoDeleteMessageHandler.java +++ b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/handlers/SqsNoDeleteMessageHandler.java @@ -1,3 +1,17 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.sqs.handlers; import com.amazonaws.services.lambda.runtime.Context; diff --git a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/internal/SqsLargeMessageAspectTest.java b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/internal/SqsLargeMessageAspectTest.java index 22844ab4c..ff04aba25 100644 --- a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/internal/SqsLargeMessageAspectTest.java +++ b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/internal/SqsLargeMessageAspectTest.java @@ -1,14 +1,39 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.sqs.internal; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.util.function.Consumer; -import java.util.stream.Stream; +import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; +import static java.util.Collections.singletonList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.openMocks; +import static software.amazon.lambda.powertools.sqs.internal.SqsLargeMessageAspect.FailedProcessingLargePayloadException; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; import com.amazonaws.services.lambda.runtime.events.SQSEvent; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.function.Consumer; +import java.util.stream.Stream; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -31,30 +56,24 @@ import software.amazon.lambda.powertools.sqs.handlers.SqsMessageHandler; import software.amazon.lambda.powertools.sqs.handlers.SqsNoDeleteMessageHandler; -import static com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; -import static java.util.Collections.singletonList; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoInteractions; -import static org.mockito.Mockito.when; -import static org.mockito.MockitoAnnotations.openMocks; -import static software.amazon.lambda.powertools.sqs.internal.SqsLargeMessageAspect.FailedProcessingLargePayloadException; - public class SqsLargeMessageAspectTest { + private static final String BUCKET_NAME = "bucketname"; + private static final String BUCKET_KEY = "c71eb2ae-37e0-4265-8909-32f4153faddf"; private RequestHandler requestHandler; - @Mock private Context context; - @Mock private S3Client s3Client; - private static final String BUCKET_NAME = "bucketname"; - private static final String BUCKET_KEY = "c71eb2ae-37e0-4265-8909-32f4153faddf"; + private static Stream exception() { + return Stream.of(Arguments.of(S3Exception.builder() + .message("Service Exception") + .build()), + Arguments.of(SdkClientException.builder() + .message("Client Exception") + .build())); + } @BeforeEach void setUp() { @@ -67,7 +86,9 @@ void setUp() { @Test public void testLargeMessage() { when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(s3ObjectWithLargeMessage()); - SQSEvent sqsEvent = messageWithBody("[\"software.amazon.payloadoffloading.PayloadS3Pointer\",{\"s3BucketName\":\"" + BUCKET_NAME + "\",\"s3Key\":\"" + BUCKET_KEY + "\"}]"); + SQSEvent sqsEvent = messageWithBody( + "[\"software.amazon.payloadoffloading.PayloadS3Pointer\",{\"s3BucketName\":\"" + BUCKET_NAME + + "\",\"s3Key\":\"" + BUCKET_KEY + "\"}]"); String response = requestHandler.handleRequest(sqsEvent, context); @@ -79,13 +100,14 @@ public void testLargeMessage() { verify(s3Client).deleteObject(delete.capture()); Assertions.assertThat(delete.getValue()) - .satisfies((Consumer) deleteObjectRequest -> { - assertThat(deleteObjectRequest.bucket()) - .isEqualTo(BUCKET_NAME); - - assertThat(deleteObjectRequest.key()) - .isEqualTo(BUCKET_KEY); - }); + .satisfies((Consumer) deleteObjectRequest -> + { + assertThat(deleteObjectRequest.bucket()) + .isEqualTo(BUCKET_NAME); + + assertThat(deleteObjectRequest.key()) + .isEqualTo(BUCKET_KEY); + }); } @Test @@ -107,7 +129,9 @@ public void shouldNotProcessSmallMessageBody() { public void shouldFailEntireBatchIfFailedDownloadingFromS3(RuntimeException exception) { when(s3Client.getObject(any(GetObjectRequest.class))).thenThrow(exception); - String messageBody = "[\"software.amazon.payloadoffloading.PayloadS3Pointer\",{\"s3BucketName\":\"" + BUCKET_NAME + "\",\"s3Key\":\"" + BUCKET_KEY + "\"}]"; + String messageBody = + "[\"software.amazon.payloadoffloading.PayloadS3Pointer\",{\"s3BucketName\":\"" + BUCKET_NAME + + "\",\"s3Key\":\"" + BUCKET_KEY + "\"}]"; SQSEvent sqsEvent = messageWithBody(messageBody); assertThatExceptionOfType(FailedProcessingLargePayloadException.class) @@ -122,7 +146,9 @@ public void testLargeMessageWithDeletionOff() { requestHandler = new SqsNoDeleteMessageHandler(); when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(s3ObjectWithLargeMessage()); - SQSEvent sqsEvent = messageWithBody("[\"software.amazon.payloadoffloading.PayloadS3Pointer\",{\"s3BucketName\":\"" + BUCKET_NAME + "\",\"s3Key\":\"" + BUCKET_KEY + "\"}]"); + SQSEvent sqsEvent = messageWithBody( + "[\"software.amazon.payloadoffloading.PayloadS3Pointer\",{\"s3BucketName\":\"" + BUCKET_NAME + + "\",\"s3Key\":\"" + BUCKET_KEY + "\"}]"); String response = requestHandler.handleRequest(sqsEvent, context); @@ -131,19 +157,22 @@ public void testLargeMessageWithDeletionOff() { verify(s3Client, never()).deleteObject(any(DeleteObjectRequest.class)); } - @Test public void shouldFailEntireBatchIfFailedProcessingDownloadMessageFromS3() { - ResponseInputStream s3Response = new ResponseInputStream<>(GetObjectResponse.builder().build(), AbortableInputStream.create(new StringInputStream("test") { - @Override - public void close() throws IOException { - throw new IOException("Failed"); - } - })); + ResponseInputStream s3Response = + new ResponseInputStream<>(GetObjectResponse.builder().build(), + AbortableInputStream.create(new StringInputStream("test") { + @Override + public void close() throws IOException { + throw new IOException("Failed"); + } + })); when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(s3Response); - String messageBody = "[\"software.amazon.payloadoffloading.PayloadS3Pointer\",{\"s3BucketName\":\"" + BUCKET_NAME + "\",\"s3Key\":\"" + BUCKET_KEY + "\"}]"; + String messageBody = + "[\"software.amazon.payloadoffloading.PayloadS3Pointer\",{\"s3BucketName\":\"" + BUCKET_NAME + + "\",\"s3Key\":\"" + BUCKET_KEY + "\"}]"; SQSEvent sqsEvent = messageWithBody(messageBody); assertThatExceptionOfType(FailedProcessingLargePayloadException.class) @@ -157,7 +186,9 @@ public void close() throws IOException { public void shouldNotDoAnyProcessingWhenNotSqsEvent() { LambdaHandlerApiGateway handler = new LambdaHandlerApiGateway(); - String messageBody = "[\"software.amazon.payloadoffloading.PayloadS3Pointer\",{\"s3BucketName\":\"" + BUCKET_NAME + "\",\"s3Key\":\"" + BUCKET_KEY + "\"}]"; + String messageBody = + "[\"software.amazon.payloadoffloading.PayloadS3Pointer\",{\"s3BucketName\":\"" + BUCKET_NAME + + "\",\"s3Key\":\"" + BUCKET_KEY + "\"}]"; APIGatewayProxyRequestEvent event = new APIGatewayProxyRequestEvent(); event.setBody(messageBody); @@ -170,16 +201,8 @@ public void shouldNotDoAnyProcessingWhenNotSqsEvent() { } private ResponseInputStream s3ObjectWithLargeMessage() { - return new ResponseInputStream<>(GetObjectResponse.builder().build(), AbortableInputStream.create(new ByteArrayInputStream("A big message".getBytes()))); - } - - private static Stream exception() { - return Stream.of(Arguments.of(S3Exception.builder() - .message("Service Exception") - .build()), - Arguments.of(SdkClientException.builder() - .message("Client Exception") - .build())); + return new ResponseInputStream<>(GetObjectResponse.builder().build(), + AbortableInputStream.create(new ByteArrayInputStream("A big message".getBytes()))); } private SQSEvent messageWithBody(String messageBody) { diff --git a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/internal/SqsMessageBatchProcessorAspectTest.java b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/internal/SqsMessageBatchProcessorAspectTest.java index a65aa486b..b257c1962 100644 --- a/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/internal/SqsMessageBatchProcessorAspectTest.java +++ b/powertools-sqs/src/test/java/software/amazon/lambda/powertools/sqs/internal/SqsMessageBatchProcessorAspectTest.java @@ -1,12 +1,38 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.sqs.internal; -import java.io.IOException; -import java.util.HashMap; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.when; +import static software.amazon.lambda.powertools.sqs.SqsUtils.overrideSqsClient; + import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; import com.amazonaws.services.lambda.runtime.events.SQSEvent; import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.IOException; +import java.util.HashMap; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -27,28 +53,14 @@ import software.amazon.lambda.powertools.sqs.handlers.SqsMessageHandlerWithNonRetryableHandler; import software.amazon.lambda.powertools.sqs.handlers.SqsMessageHandlerWithNonRetryableHandlerWithDelete; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoInteractions; -import static org.mockito.Mockito.when; -import static software.amazon.lambda.powertools.sqs.SqsUtils.overrideSqsClient; - public class SqsMessageBatchProcessorAspectTest { public static final SqsClient interactionClient = mock(SqsClient.class); private static final SqsClient sqsClient = mock(SqsClient.class); private static final ObjectMapper MAPPER = new ObjectMapper(); - + private final Context context = mock(Context.class); private SQSEvent event; private RequestHandler requestHandler; - private final Context context = mock(Context.class); - @BeforeEach void setUp() throws IOException { overrideSqsClient(sqsClient); @@ -73,21 +85,22 @@ void shouldBatchProcessMessageWithSuccessDeletedOnFailureInBatchFromSQS() { assertThatExceptionOfType(SQSBatchProcessingException.class) .isThrownBy(() -> requestHandler.handleRequest(event, context)) - .satisfies(e -> { - assertThat(e.getExceptions()) - .hasSize(1) - .extracting("message") - .containsExactly("2e1424d4-f796-459a-8184-9c92662be6da"); - - assertThat(e.getFailures()) - .hasSize(1) - .extracting("messageId") - .containsExactly("2e1424d4-f796-459a-8184-9c92662be6da"); - - assertThat(e.successMessageReturnValues()) - .hasSize(1) - .contains("Success"); - }); + .satisfies(e -> + { + assertThat(e.getExceptions()) + .hasSize(1) + .extracting("message") + .containsExactly("2e1424d4-f796-459a-8184-9c92662be6da"); + + assertThat(e.getFailures()) + .hasSize(1) + .extracting("messageId") + .containsExactly("2e1424d4-f796-459a-8184-9c92662be6da"); + + assertThat(e.successMessageReturnValues()) + .hasSize(1) + .contains("Success"); + }); verify(interactionClient).listQueues(); verify(sqsClient).deleteMessageBatch(any(DeleteMessageBatchRequest.class)); @@ -124,9 +137,10 @@ void shouldBatchProcessAndMoveNonRetryableExceptionToDlq() { " \"maxReceiveCount\": 2\n" + "}"); - when(sqsClient.getQueueAttributes(any(GetQueueAttributesRequest.class))).thenReturn(GetQueueAttributesResponse.builder() - .attributes(attributes) - .build()); + when(sqsClient.getQueueAttributes(any(GetQueueAttributesRequest.class))).thenReturn( + GetQueueAttributesResponse.builder() + .attributes(attributes) + .build()); requestHandler.handleRequest(event, context); @@ -140,13 +154,14 @@ void shouldBatchProcessAndThrowExceptionForNonRetryableExceptionWhenMoveToDlqRet requestHandler = new SqsMessageHandlerWithNonRetryableHandler(); event.getRecords().get(0).setMessageId(""); - when(sqsClient.sendMessageBatch(any(SendMessageBatchRequest.class))).thenReturn(SendMessageBatchResponse.builder() + when(sqsClient.sendMessageBatch(any(SendMessageBatchRequest.class))).thenReturn( + SendMessageBatchResponse.builder() .failed(BatchResultErrorEntry.builder() .message("Permission Error") .code("KMS.AccessDeniedException") .senderFault(true) .build()) - .build()); + .build()); HashMap attributes = new HashMap<>(); @@ -155,9 +170,10 @@ void shouldBatchProcessAndThrowExceptionForNonRetryableExceptionWhenMoveToDlqRet " \"maxReceiveCount\": 2\n" + "}"); - when(sqsClient.getQueueAttributes(any(GetQueueAttributesRequest.class))).thenReturn(GetQueueAttributesResponse.builder() - .attributes(attributes) - .build()); + when(sqsClient.getQueueAttributes(any(GetQueueAttributesRequest.class))).thenReturn( + GetQueueAttributesResponse.builder() + .attributes(attributes) + .build()); Assertions.assertThatExceptionOfType(SQSBatchProcessingException.class). isThrownBy(() -> requestHandler.handleRequest(event, context)); @@ -192,28 +208,31 @@ void shouldBatchProcessAndFailWithExceptionForNonRetryableExceptionAndNoDlq() { requestHandler = new SqsMessageHandlerWithNonRetryableHandler(); event.getRecords().get(0).setMessageId(""); - event.getRecords().forEach(sqsMessage -> sqsMessage.setEventSourceArn(sqsMessage.getEventSourceArn() + "-temp")); + event.getRecords() + .forEach(sqsMessage -> sqsMessage.setEventSourceArn(sqsMessage.getEventSourceArn() + "-temp")); - when(sqsClient.getQueueAttributes(any(GetQueueAttributesRequest.class))).thenReturn(GetQueueAttributesResponse.builder() - .build()); + when(sqsClient.getQueueAttributes(any(GetQueueAttributesRequest.class))).thenReturn( + GetQueueAttributesResponse.builder() + .build()); assertThatExceptionOfType(SQSBatchProcessingException.class) .isThrownBy(() -> requestHandler.handleRequest(event, context)) - .satisfies(e -> { - assertThat(e.getExceptions()) - .hasSize(1) - .extracting("message") - .containsExactly("Invalid message and was moved to DLQ"); - - assertThat(e.getFailures()) - .hasSize(1) - .extracting("messageId") - .containsExactly(""); - - assertThat(e.successMessageReturnValues()) - .hasSize(1) - .contains("Success"); - }); + .satisfies(e -> + { + assertThat(e.getExceptions()) + .hasSize(1) + .extracting("message") + .containsExactly("Invalid message and was moved to DLQ"); + + assertThat(e.getFailures()) + .hasSize(1) + .extracting("messageId") + .containsExactly(""); + + assertThat(e.successMessageReturnValues()) + .hasSize(1) + .contains("Success"); + }); verify(interactionClient).listQueues(); verify(sqsClient).deleteMessageBatch(any(DeleteMessageBatchRequest.class)); @@ -224,33 +243,36 @@ void shouldBatchProcessAndFailWithExceptionForNonRetryableExceptionAndNoDlq() { void shouldBatchProcessAndFailWithExceptionForNonRetryableExceptionWhenFailedParsingPolicy() { requestHandler = new SqsMessageHandlerWithNonRetryableHandler(); event.getRecords().get(0).setMessageId(""); - event.getRecords().forEach(sqsMessage -> sqsMessage.setEventSourceArn(sqsMessage.getEventSourceArn() + "-temp-queue")); + event.getRecords() + .forEach(sqsMessage -> sqsMessage.setEventSourceArn(sqsMessage.getEventSourceArn() + "-temp-queue")); HashMap attributes = new HashMap<>(); attributes.put(QueueAttributeName.REDRIVE_POLICY, "MalFormedRedrivePolicy"); - when(sqsClient.getQueueAttributes(any(GetQueueAttributesRequest.class))).thenReturn(GetQueueAttributesResponse.builder() - .attributes(attributes) - .build()); + when(sqsClient.getQueueAttributes(any(GetQueueAttributesRequest.class))).thenReturn( + GetQueueAttributesResponse.builder() + .attributes(attributes) + .build()); assertThatExceptionOfType(SQSBatchProcessingException.class) .isThrownBy(() -> requestHandler.handleRequest(event, context)) - .satisfies(e -> { - assertThat(e.getExceptions()) - .hasSize(1) - .extracting("message") - .containsExactly("Invalid message and was moved to DLQ"); - - assertThat(e.getFailures()) - .hasSize(1) - .extracting("messageId") - .containsExactly(""); - - assertThat(e.successMessageReturnValues()) - .hasSize(1) - .contains("Success"); - }); + .satisfies(e -> + { + assertThat(e.getExceptions()) + .hasSize(1) + .extracting("message") + .containsExactly("Invalid message and was moved to DLQ"); + + assertThat(e.getFailures()) + .hasSize(1) + .extracting("messageId") + .containsExactly(""); + + assertThat(e.successMessageReturnValues()) + .hasSize(1) + .contains("Success"); + }); verify(interactionClient).listQueues(); verify(sqsClient).deleteMessageBatch(any(DeleteMessageBatchRequest.class)); @@ -271,27 +293,29 @@ void shouldBatchProcessAndMoveNonRetryableExceptionToDlqAndThrowException() thro " \"maxReceiveCount\": 2\n" + "}"); - when(sqsClient.getQueueAttributes(any(GetQueueAttributesRequest.class))).thenReturn(GetQueueAttributesResponse.builder() - .attributes(attributes) - .build()); + when(sqsClient.getQueueAttributes(any(GetQueueAttributesRequest.class))).thenReturn( + GetQueueAttributesResponse.builder() + .attributes(attributes) + .build()); assertThatExceptionOfType(SQSBatchProcessingException.class) .isThrownBy(() -> requestHandler.handleRequest(event, context)) - .satisfies(e -> { - assertThat(e.getExceptions()) - .hasSize(1) - .extracting("message") - .containsExactly("Invalid message and should be reprocessed"); - - assertThat(e.getFailures()) - .hasSize(1) - .extracting("messageId") - .containsExactly("2e1424d4-f796-459a-9696-9c92662ba5da"); - - assertThat(e.successMessageReturnValues()) - .hasSize(1) - .contains("Success"); - }); + .satisfies(e -> + { + assertThat(e.getExceptions()) + .hasSize(1) + .extracting("message") + .containsExactly("Invalid message and should be reprocessed"); + + assertThat(e.getFailures()) + .hasSize(1) + .extracting("messageId") + .containsExactly("2e1424d4-f796-459a-9696-9c92662ba5da"); + + assertThat(e.successMessageReturnValues()) + .hasSize(1) + .contains("Success"); + }); verify(interactionClient).listQueues(); verify(sqsClient).deleteMessageBatch(any(DeleteMessageBatchRequest.class)); diff --git a/powertools-test-suite/pom.xml b/powertools-test-suite/pom.xml index 5737296da..4a79d2987 100644 --- a/powertools-test-suite/pom.xml +++ b/powertools-test-suite/pom.xml @@ -1,4 +1,18 @@ + + diff --git a/powertools-test-suite/src/test/java/software/amazon/lambda/powertools/testsuite/LoggingOrderTest.java b/powertools-test-suite/src/test/java/software/amazon/lambda/powertools/testsuite/LoggingOrderTest.java index 7c3e79112..55349b267 100644 --- a/powertools-test-suite/src/test/java/software/amazon/lambda/powertools/testsuite/LoggingOrderTest.java +++ b/powertools-test-suite/src/test/java/software/amazon/lambda/powertools/testsuite/LoggingOrderTest.java @@ -1,6 +1,35 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.testsuite; +import static java.util.Collections.emptyMap; +import static java.util.Collections.singletonList; +import static org.apache.commons.lang3.reflect.FieldUtils.writeStaticField; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.openMocks; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.SQSEvent; +import com.amazonaws.services.lambda.runtime.events.models.s3.S3EventNotification; +import com.amazonaws.xray.AWSXRay; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -12,13 +41,6 @@ import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.util.Map; - -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.events.SQSEvent; -import com.amazonaws.services.lambda.runtime.events.models.s3.S3EventNotification; -import com.amazonaws.xray.AWSXRay; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.ThreadContext; import org.junit.jupiter.api.AfterEach; @@ -36,15 +58,6 @@ import software.amazon.lambda.powertools.testsuite.handler.LoggingOrderMessageHandler; import software.amazon.lambda.powertools.testsuite.handler.TracingLoggingStreamMessageHandler; -import static java.util.Collections.emptyMap; -import static java.util.Collections.singletonList; -import static org.apache.commons.lang3.reflect.FieldUtils.writeStaticField; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.fail; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.when; -import static org.mockito.MockitoAnnotations.openMocks; - public class LoggingOrderTest { private static final String BUCKET_NAME = "ms-extended-sqs-client"; @@ -79,25 +92,30 @@ void tearDown() { * after the event has been altered */ @Test - public void testThatLoggingAnnotationActsLast() throws IOException { - ResponseInputStream s3Response = new ResponseInputStream<>(GetObjectResponse.builder().build(), AbortableInputStream.create(new ByteArrayInputStream("A big message".getBytes()))); + public void testThatLoggingAnnotationActsLast() throws IOException { + ResponseInputStream s3Response = + new ResponseInputStream<>(GetObjectResponse.builder().build(), + AbortableInputStream.create(new ByteArrayInputStream("A big message".getBytes()))); when(s3Client.getObject(any(GetObjectRequest.class))).thenReturn(s3Response); - SQSEvent sqsEvent = messageWithBody("[\"software.amazon.payloadoffloading.PayloadS3Pointer\",{\"s3BucketName\":\"" + BUCKET_NAME + "\",\"s3Key\":\"" + BUCKET_KEY + "\"}]"); + SQSEvent sqsEvent = messageWithBody( + "[\"software.amazon.payloadoffloading.PayloadS3Pointer\",{\"s3BucketName\":\"" + BUCKET_NAME + + "\",\"s3Key\":\"" + BUCKET_KEY + "\"}]"); LoggingOrderMessageHandler requestHandler = new LoggingOrderMessageHandler(); requestHandler.handleRequest(sqsEvent, context); assertThat(Files.lines(Paths.get("target/logfile.json"))) .hasSize(2) - .satisfies(line -> { - Map actual = parseToMap(line.get(0)); + .satisfies(line -> + { + Map actual = parseToMap(line.get(0)); - String message = actual.get("message").toString(); + String message = actual.get("message").toString(); - assertThat(message) - .contains("A big message"); - }); + assertThat(message) + .contains("A big message"); + }); } @Test @@ -107,7 +125,8 @@ public void testLoggingAnnotationActsAfterTracingForStreamingHandler() throws IO S3EventNotification s3EventNotification = s3EventNotification(); TracingLoggingStreamMessageHandler handler = new TracingLoggingStreamMessageHandler(); - handler.handleRequest(new ByteArrayInputStream(new ObjectMapper().writeValueAsBytes(s3EventNotification)), output, context); + handler.handleRequest(new ByteArrayInputStream(new ObjectMapper().writeValueAsBytes(s3EventNotification)), + output, context); assertThat(new String(output.toByteArray(), StandardCharsets.UTF_8)) .isNotEmpty(); @@ -121,7 +140,8 @@ private void setupContext() { when(context.getAwsRequestId()).thenReturn("RequestId"); } - private void resetLogLevel(Level level) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { + private void resetLogLevel(Level level) + throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { Method resetLogLevels = LambdaLoggingAspect.class.getDeclaredMethod("resetLogLevels", Level.class); resetLogLevels.setAccessible(true); resetLogLevels.invoke(null, level); @@ -138,25 +158,27 @@ private Map parseToMap(String stringAsJson) { } private S3EventNotification s3EventNotification() { - S3EventNotification.S3EventNotificationRecord record = new S3EventNotification.S3EventNotificationRecord("us-west-2", - "ObjectCreated:Put", - "aws:s3", - null, - "2.1", - new S3EventNotification.RequestParametersEntity("127.0.0.1"), - new S3EventNotification.ResponseElementsEntity("C3D13FE58DE4C810", "FMyUVURIY8/IgAtTv8xRjskZQpcIZ9KG4V5Wp6S7S/JRWeUWerMUE5JgHvANOjpD"), - new S3EventNotification.S3Entity("testConfigRule", - new S3EventNotification.S3BucketEntity("mybucket", - new S3EventNotification.UserIdentityEntity("A3NL1KOZZKExample"), - "arn:aws:s3:::mybucket"), - new S3EventNotification.S3ObjectEntity("HappyFace.jpg", - 1024L, - "d41d8cd98f00b204e9800998ecf8427e", - "096fKKXTRTtl3on89fVO.nfljtsv6qko", - "0055AED6DCD90281E5"), - "1.0"), - new S3EventNotification.UserIdentityEntity("AIDAJDPLRKLG7UEXAMPLE") - ); + S3EventNotification.S3EventNotificationRecord record = + new S3EventNotification.S3EventNotificationRecord("us-west-2", + "ObjectCreated:Put", + "aws:s3", + null, + "2.1", + new S3EventNotification.RequestParametersEntity("127.0.0.1"), + new S3EventNotification.ResponseElementsEntity("C3D13FE58DE4C810", + "FMyUVURIY8/IgAtTv8xRjskZQpcIZ9KG4V5Wp6S7S/JRWeUWerMUE5JgHvANOjpD"), + new S3EventNotification.S3Entity("testConfigRule", + new S3EventNotification.S3BucketEntity("mybucket", + new S3EventNotification.UserIdentityEntity("A3NL1KOZZKExample"), + "arn:aws:s3:::mybucket"), + new S3EventNotification.S3ObjectEntity("HappyFace.jpg", + 1024L, + "d41d8cd98f00b204e9800998ecf8427e", + "096fKKXTRTtl3on89fVO.nfljtsv6qko", + "0055AED6DCD90281E5"), + "1.0"), + new S3EventNotification.UserIdentityEntity("AIDAJDPLRKLG7UEXAMPLE") + ); return new S3EventNotification(singletonList(record)); } diff --git a/powertools-test-suite/src/test/java/software/amazon/lambda/powertools/testsuite/handler/LoggingOrderMessageHandler.java b/powertools-test-suite/src/test/java/software/amazon/lambda/powertools/testsuite/handler/LoggingOrderMessageHandler.java index a85c81b1d..5592b1fd3 100644 --- a/powertools-test-suite/src/test/java/software/amazon/lambda/powertools/testsuite/handler/LoggingOrderMessageHandler.java +++ b/powertools-test-suite/src/test/java/software/amazon/lambda/powertools/testsuite/handler/LoggingOrderMessageHandler.java @@ -1,3 +1,17 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.testsuite.handler; import com.amazonaws.services.lambda.runtime.Context; diff --git a/powertools-test-suite/src/test/java/software/amazon/lambda/powertools/testsuite/handler/TracingLoggingStreamMessageHandler.java b/powertools-test-suite/src/test/java/software/amazon/lambda/powertools/testsuite/handler/TracingLoggingStreamMessageHandler.java index d0f2b3ac5..4a60d0949 100644 --- a/powertools-test-suite/src/test/java/software/amazon/lambda/powertools/testsuite/handler/TracingLoggingStreamMessageHandler.java +++ b/powertools-test-suite/src/test/java/software/amazon/lambda/powertools/testsuite/handler/TracingLoggingStreamMessageHandler.java @@ -1,13 +1,26 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.testsuite.handler; +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestStreamHandler; +import com.fasterxml.jackson.databind.ObjectMapper; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Map; - -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.RequestStreamHandler; -import com.fasterxml.jackson.databind.ObjectMapper; import software.amazon.lambda.powertools.logging.Logging; import software.amazon.lambda.powertools.tracing.Tracing; diff --git a/powertools-tracing/pom.xml b/powertools-tracing/pom.xml index 42a229f42..b5de90f7b 100644 --- a/powertools-tracing/pom.xml +++ b/powertools-tracing/pom.xml @@ -1,4 +1,18 @@ + + @@ -113,4 +127,13 @@ + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + + + \ No newline at end of file diff --git a/powertools-tracing/src/main/java/software/amazon/lambda/powertools/tracing/CaptureMode.java b/powertools-tracing/src/main/java/software/amazon/lambda/powertools/tracing/CaptureMode.java index 9fd09e8ee..29d10d188 100644 --- a/powertools-tracing/src/main/java/software/amazon/lambda/powertools/tracing/CaptureMode.java +++ b/powertools-tracing/src/main/java/software/amazon/lambda/powertools/tracing/CaptureMode.java @@ -1,3 +1,17 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.tracing; public enum CaptureMode { diff --git a/powertools-tracing/src/main/java/software/amazon/lambda/powertools/tracing/Tracing.java b/powertools-tracing/src/main/java/software/amazon/lambda/powertools/tracing/Tracing.java index cb90f3315..6f17a2e33 100644 --- a/powertools-tracing/src/main/java/software/amazon/lambda/powertools/tracing/Tracing.java +++ b/powertools-tracing/src/main/java/software/amazon/lambda/powertools/tracing/Tracing.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.tracing; import java.lang.annotation.ElementType; @@ -39,7 +40,7 @@ * to a sub segment named after the method.

    * *

    To disable this functionality you can specify {@code @Tracing( captureError = false)}

    - *e + * e *

    All traces have a namespace set. If {@code @Tracing( namespace = "ExampleService")} is set * this takes precedent over any value set in the environment variable {@code POWER_TOOLS_SERVICE_NAME}. * If both are undefined then the value will default to {@code service_undefined}

    @@ -48,6 +49,7 @@ @Target(ElementType.METHOD) public @interface Tracing { String namespace() default ""; + /** * @deprecated As of release 1.2.0, replaced by captureMode() * in order to support different modes and support via @@ -55,6 +57,7 @@ */ @Deprecated boolean captureResponse() default true; + /** * @deprecated As of release 1.2.0, replaced by captureMode() * in order to support different modes and support via @@ -62,6 +65,8 @@ */ @Deprecated boolean captureError() default true; + String segmentName() default ""; + CaptureMode captureMode() default CaptureMode.ENVIRONMENT_VAR; } diff --git a/powertools-tracing/src/main/java/software/amazon/lambda/powertools/tracing/TracingUtils.java b/powertools-tracing/src/main/java/software/amazon/lambda/powertools/tracing/TracingUtils.java index 0e956e539..9fb021548 100644 --- a/powertools-tracing/src/main/java/software/amazon/lambda/powertools/tracing/TracingUtils.java +++ b/powertools-tracing/src/main/java/software/amazon/lambda/powertools/tracing/TracingUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,20 +11,20 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.tracing; -import java.util.function.Consumer; +import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.serviceName; + import com.amazonaws.xray.AWSXRay; import com.amazonaws.xray.entities.Entity; import com.amazonaws.xray.entities.Subsegment; import com.fasterxml.jackson.databind.ObjectMapper; - -import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.serviceName; +import java.util.function.Consumer; /** * A class of helper functions to add additional functionality and ease * of use. - * */ public final class TracingUtils { private static ObjectMapper objectMapper; @@ -32,7 +32,7 @@ public final class TracingUtils { /** * Put an annotation to the current subsegment with a String value. * - * @param key the key of the annotation + * @param key the key of the annotation * @param value the value of the annotation */ public static void putAnnotation(String key, String value) { @@ -43,33 +43,33 @@ public static void putAnnotation(String key, String value) { /** * Put an annotation to the current subsegment with a Number value. * - * @param key the key of the annotation + * @param key the key of the annotation * @param value the value of the annotation */ public static void putAnnotation(String key, Number value) { AWSXRay.getCurrentSubsegmentOptional() - .ifPresent(segment -> segment.putAnnotation(key, value)); + .ifPresent(segment -> segment.putAnnotation(key, value)); } /** * Put an annotation to the current subsegment with a Boolean value. * - * @param key the key of the annotation + * @param key the key of the annotation * @param value the value of the annotation */ public static void putAnnotation(String key, Boolean value) { AWSXRay.getCurrentSubsegmentOptional() - .ifPresent(segment -> segment.putAnnotation(key, value)); + .ifPresent(segment -> segment.putAnnotation(key, value)); } /** * Put additional metadata for the current subsegment. - * + *

    * The namespace used will be the namespace of the current subsegment if it * is set else it will follow the namespace process as described in * {@link Tracing} * - * @param key the key of the metadata + * @param key the key of the metadata * @param value the value of the metadata */ public static void putMetadata(String key, Object value) { @@ -83,8 +83,8 @@ public static void putMetadata(String key, Object value) { * Put additional metadata for the current subsegment. * * @param namespace the namespace of the metadata - * @param key the key of the metadata - * @param value the value of the metadata + * @param key the key of the metadata + * @param value the value of the metadata */ public static void putMetadata(String namespace, String key, Object value) { AWSXRay.getCurrentSubsegmentOptional() @@ -94,14 +94,14 @@ public static void putMetadata(String namespace, String key, Object value) { /** * Adds a new subsegment around the passed consumer. This also provides access to * the newly created subsegment. - * + *

    * The namespace used follows the flow as described in {@link Tracing} - * + *

    * This method is intended for use with multi-threaded programming where the * context is lost between threads. * - * @param name the name of the subsegment - * @param entity the current x-ray context + * @param name the name of the subsegment + * @param entity the current x-ray context * @param subsegment the x-ray subsegment for the wrapped consumer */ public static void withEntitySubsegment(String name, Entity entity, Consumer subsegment) { @@ -112,16 +112,17 @@ public static void withEntitySubsegment(String name, Entity entity, Consumer * This method is intended for use with multi-threaded programming where the * context is lost between threads. * - * @param namespace the namespace of the subsegment - * @param name the name of the subsegment - * @param entity the current x-ray context + * @param namespace the namespace of the subsegment + * @param name the name of the subsegment + * @param entity the current x-ray context * @param subsegment the x-ray subsegment for the wrapped consumer */ - public static void withEntitySubsegment(String namespace, String name, Entity entity, Consumer subsegment) { + public static void withEntitySubsegment(String namespace, String name, Entity entity, + Consumer subsegment) { AWSXRay.setTraceEntity(entity); withSubsegment(namespace, name, subsegment); } @@ -129,10 +130,10 @@ public static void withEntitySubsegment(String namespace, String name, Entity en /** * Adds a new subsegment around the passed consumer. This also provides access to * the newly created subsegment. - * + *

    * The namespace used follows the flow as described in {@link Tracing} * - * @param name the name of the subsegment + * @param name the name of the subsegment * @param subsegment the x-ray subsegment for the wrapped consumer */ public static void withSubsegment(String name, Consumer subsegment) { @@ -143,8 +144,8 @@ public static void withSubsegment(String name, Consumer subsegment) * Adds a new subsegment around the passed consumer. This also provides access to * the newly created subsegment. * - * @param namespace the namespace for the subsegment - * @param name the name of the subsegment + * @param namespace the namespace for the subsegment + * @param name the name of the subsegment * @param subsegment the x-ray subsegment for the wrapped consumer */ public static void withSubsegment(String namespace, String name, Consumer subsegment) { diff --git a/powertools-tracing/src/main/java/software/amazon/lambda/powertools/tracing/internal/LambdaTracingAspect.java b/powertools-tracing/src/main/java/software/amazon/lambda/powertools/tracing/internal/LambdaTracingAspect.java index 26feec66b..62416fce6 100644 --- a/powertools-tracing/src/main/java/software/amazon/lambda/powertools/tracing/internal/LambdaTracingAspect.java +++ b/powertools-tracing/src/main/java/software/amazon/lambda/powertools/tracing/internal/LambdaTracingAspect.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,24 +11,25 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.tracing.internal; -import java.util.function.Supplier; +import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.coldStartDone; +import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.isColdStart; +import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.isHandlerMethod; +import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.isSamLocal; +import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.serviceName; +import static software.amazon.lambda.powertools.tracing.TracingUtils.objectMapper; + import com.amazonaws.xray.AWSXRay; import com.amazonaws.xray.entities.Subsegment; +import java.util.function.Supplier; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import software.amazon.lambda.powertools.tracing.Tracing; -import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.coldStartDone; -import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.isColdStart; -import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.isHandlerMethod; -import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.isSamLocal; -import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.serviceName; -import static software.amazon.lambda.powertools.tracing.TracingUtils.objectMapper; - @Aspect public final class LambdaTracingAspect { @SuppressWarnings({"EmptyMethod"}) @@ -42,8 +43,8 @@ public Object around(ProceedingJoinPoint pjp, Object[] proceedArgs = pjp.getArgs(); Subsegment segment = AWSXRay.beginSubsegment( - customSegmentNameOrDefault(tracing, - () -> "## " + pjp.getSignature().getName())); + customSegmentNameOrDefault(tracing, + () -> "## " + pjp.getSignature().getName())); segment.setNamespace(namespace(tracing)); if (isHandlerMethod(pjp)) { @@ -57,7 +58,8 @@ public Object around(ProceedingJoinPoint pjp, try { Object methodReturn = pjp.proceed(proceedArgs); if (captureResponse) { - segment.putMetadata(namespace(tracing), pjp.getSignature().getName() + " response", null != objectMapper() ? objectMapper().writeValueAsString(methodReturn): methodReturn); + segment.putMetadata(namespace(tracing), pjp.getSignature().getName() + " response", + null != objectMapper() ? objectMapper().writeValueAsString(methodReturn) : methodReturn); } if (isHandlerMethod(pjp)) { @@ -67,7 +69,8 @@ public Object around(ProceedingJoinPoint pjp, return methodReturn; } catch (Exception e) { if (captureError) { - segment.putMetadata(namespace(tracing), pjp.getSignature().getName() + " error", null != objectMapper() ? objectMapper().writeValueAsString(e) : e); + segment.putMetadata(namespace(tracing), pjp.getSignature().getName() + " error", + null != objectMapper() ? objectMapper().writeValueAsString(e) : e); } throw e; } finally { @@ -81,7 +84,8 @@ private boolean captureResponse(Tracing powerToolsTracing) { switch (powerToolsTracing.captureMode()) { case ENVIRONMENT_VAR: boolean captureResponse = environmentVariable("POWERTOOLS_TRACER_CAPTURE_RESPONSE"); - return isEnvironmentVariableSet("POWERTOOLS_TRACER_CAPTURE_RESPONSE") ? captureResponse : powerToolsTracing.captureResponse(); + return isEnvironmentVariableSet("POWERTOOLS_TRACER_CAPTURE_RESPONSE") ? captureResponse : + powerToolsTracing.captureResponse(); case RESPONSE: case RESPONSE_AND_ERROR: return true; @@ -95,7 +99,8 @@ private boolean captureError(Tracing powerToolsTracing) { switch (powerToolsTracing.captureMode()) { case ENVIRONMENT_VAR: boolean captureError = environmentVariable("POWERTOOLS_TRACER_CAPTURE_ERROR"); - return isEnvironmentVariableSet("POWERTOOLS_TRACER_CAPTURE_ERROR") ? captureError : powerToolsTracing.captureError(); + return isEnvironmentVariableSet("POWERTOOLS_TRACER_CAPTURE_ERROR") ? captureError : + powerToolsTracing.captureError(); case ERROR: case RESPONSE_AND_ERROR: return true; diff --git a/powertools-tracing/src/main/java/software/amazon/lambda/powertools/tracing/internal/SystemWrapper.java b/powertools-tracing/src/main/java/software/amazon/lambda/powertools/tracing/internal/SystemWrapper.java index da1a92ced..c66b8b7ee 100644 --- a/powertools-tracing/src/main/java/software/amazon/lambda/powertools/tracing/internal/SystemWrapper.java +++ b/powertools-tracing/src/main/java/software/amazon/lambda/powertools/tracing/internal/SystemWrapper.java @@ -1,3 +1,17 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.tracing.internal; public class SystemWrapper { diff --git a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/TracingUtilsTest.java b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/TracingUtilsTest.java index d2a96ec65..69054d0c6 100644 --- a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/TracingUtilsTest.java +++ b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/TracingUtilsTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,8 +11,15 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.tracing; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.entry; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static software.amazon.lambda.powertools.tracing.TracingUtils.withEntitySubsegment; + import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.xray.AWSXRay; import com.amazonaws.xray.entities.Entity; @@ -20,12 +27,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.entry; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static software.amazon.lambda.powertools.tracing.TracingUtils.withEntitySubsegment; - class TracingUtilsTest { @BeforeEach @@ -51,12 +52,12 @@ void shouldSetAnnotationOnCurrentSubSegment() { TracingUtils.putAnnotation("booleanKey", false); assertThat(AWSXRay.getTraceEntity().getAnnotations()) - .hasSize(3) - .contains( - entry("stringKey", "val"), - entry("numberKey", 10), - entry("booleanKey", false) - ); + .hasSize(3) + .contains( + entry("stringKey", "val"), + entry("numberKey", 10), + entry("booleanKey", false) + ); } @Test @@ -94,60 +95,64 @@ void shouldNotSetMetaDataIfNoCurrentSubSegment() { void shouldInvokeCodeBlockWrappedWithinSubsegment() { Context test = mock(Context.class); - TracingUtils.withSubsegment("testSubSegment", subsegment -> { - subsegment.putAnnotation("key", "val"); - subsegment.putMetadata("key", "val"); - test.getFunctionName(); - }); + TracingUtils.withSubsegment("testSubSegment", subsegment -> + { + subsegment.putAnnotation("key", "val"); + subsegment.putMetadata("key", "val"); + test.getFunctionName(); + }); verify(test).getFunctionName(); assertThat(AWSXRay.getTraceEntity().getSubsegments()) .hasSize(1) - .allSatisfy(subsegment -> { - assertThat(subsegment.getName()) - .isEqualTo("## testSubSegment"); + .allSatisfy(subsegment -> + { + assertThat(subsegment.getName()) + .isEqualTo("## testSubSegment"); - assertThat(subsegment.getNamespace()) - .isEqualTo("service_undefined"); + assertThat(subsegment.getNamespace()) + .isEqualTo("service_undefined"); - assertThat(subsegment.getAnnotations()) - .hasSize(1) - .containsEntry("key", "val"); + assertThat(subsegment.getAnnotations()) + .hasSize(1) + .containsEntry("key", "val"); - assertThat(subsegment.getMetadata()) - .hasSize(1); - }); + assertThat(subsegment.getMetadata()) + .hasSize(1); + }); } @Test void shouldInvokeCodeBlockWrappedWithinNamespacedSubsegment() { Context test = mock(Context.class); - TracingUtils.withSubsegment("testNamespace", "testSubSegment", subsegment -> { - subsegment.putAnnotation("key", "val"); - subsegment.putMetadata("key", "val"); - test.getFunctionName(); - }); + TracingUtils.withSubsegment("testNamespace", "testSubSegment", subsegment -> + { + subsegment.putAnnotation("key", "val"); + subsegment.putMetadata("key", "val"); + test.getFunctionName(); + }); verify(test).getFunctionName(); assertThat(AWSXRay.getTraceEntity().getSubsegments()) .hasSize(1) - .allSatisfy(subsegment -> { - assertThat(subsegment.getName()) - .isEqualTo("## testSubSegment"); + .allSatisfy(subsegment -> + { + assertThat(subsegment.getName()) + .isEqualTo("## testSubSegment"); - assertThat(subsegment.getNamespace()) - .isEqualTo("testNamespace"); + assertThat(subsegment.getNamespace()) + .isEqualTo("testNamespace"); - assertThat(subsegment.getAnnotations()) - .hasSize(1) - .containsEntry("key", "val"); + assertThat(subsegment.getAnnotations()) + .hasSize(1) + .containsEntry("key", "val"); - assertThat(subsegment.getMetadata()) - .hasSize(1); - }); + assertThat(subsegment.getMetadata()) + .hasSize(1); + }); } @Test @@ -156,10 +161,11 @@ void shouldInvokeCodeBlockWrappedWithinEntitySubsegment() throws InterruptedExce Entity traceEntity = AWSXRay.getTraceEntity(); - Thread thread = new Thread(() -> withEntitySubsegment("testSubSegment", traceEntity, subsegment -> { - subsegment.putAnnotation("key", "val"); - test.getFunctionName(); - })); + Thread thread = new Thread(() -> withEntitySubsegment("testSubSegment", traceEntity, subsegment -> + { + subsegment.putAnnotation("key", "val"); + test.getFunctionName(); + })); thread.start(); thread.join(); @@ -168,17 +174,18 @@ void shouldInvokeCodeBlockWrappedWithinEntitySubsegment() throws InterruptedExce assertThat(AWSXRay.getTraceEntity().getSubsegments()) .hasSize(1) - .allSatisfy(subsegment -> { - assertThat(subsegment.getName()) - .isEqualTo("## testSubSegment"); - - assertThat(subsegment.getNamespace()) - .isEqualTo("service_undefined"); - - assertThat(subsegment.getAnnotations()) - .hasSize(1) - .containsEntry("key", "val"); - }); + .allSatisfy(subsegment -> + { + assertThat(subsegment.getName()) + .isEqualTo("## testSubSegment"); + + assertThat(subsegment.getNamespace()) + .isEqualTo("service_undefined"); + + assertThat(subsegment.getAnnotations()) + .hasSize(1) + .containsEntry("key", "val"); + }); } @Test @@ -187,10 +194,12 @@ void shouldInvokeCodeBlockWrappedWithinNamespacedEntitySubsegment() throws Inter Entity traceEntity = AWSXRay.getTraceEntity(); - Thread thread = new Thread(() -> withEntitySubsegment("testNamespace", "testSubSegment", traceEntity, subsegment -> { - subsegment.putAnnotation("key", "val"); - test.getFunctionName(); - })); + Thread thread = + new Thread(() -> withEntitySubsegment("testNamespace", "testSubSegment", traceEntity, subsegment -> + { + subsegment.putAnnotation("key", "val"); + test.getFunctionName(); + })); thread.start(); thread.join(); @@ -199,16 +208,17 @@ void shouldInvokeCodeBlockWrappedWithinNamespacedEntitySubsegment() throws Inter assertThat(AWSXRay.getTraceEntity().getSubsegments()) .hasSize(1) - .allSatisfy(subsegment -> { - assertThat(subsegment.getName()) - .isEqualTo("## testSubSegment"); - - assertThat(subsegment.getNamespace()) - .isEqualTo("testNamespace"); - - assertThat(subsegment.getAnnotations()) - .hasSize(1) - .containsEntry("key", "val"); - }); + .allSatisfy(subsegment -> + { + assertThat(subsegment.getName()) + .isEqualTo("## testSubSegment"); + + assertThat(subsegment.getNamespace()) + .isEqualTo("testNamespace"); + + assertThat(subsegment.getAnnotations()) + .hasSize(1) + .containsEntry("key", "val"); + }); } } \ No newline at end of file diff --git a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerToolDisabled.java b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerToolDisabled.java index 78878ffe5..1a9dc851a 100644 --- a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerToolDisabled.java +++ b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerToolDisabled.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.tracing.handlers; import com.amazonaws.services.lambda.runtime.Context; diff --git a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerToolDisabledForStream.java b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerToolDisabledForStream.java index 80f37b8b6..49bc4e095 100644 --- a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerToolDisabledForStream.java +++ b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerToolDisabledForStream.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,13 +11,13 @@ * limitations under the License. * */ -package software.amazon.lambda.powertools.tracing.handlers; -import java.io.InputStream; -import java.io.OutputStream; +package software.amazon.lambda.powertools.tracing.handlers; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestStreamHandler; +import java.io.InputStream; +import java.io.OutputStream; public class PowerToolDisabledForStream implements RequestStreamHandler { diff --git a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabled.java b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabled.java index 3be79fb76..5af3c65af 100644 --- a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabled.java +++ b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabled.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.tracing.handlers; import com.amazonaws.services.lambda.runtime.Context; diff --git a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledExplicitlyForResponseAndError.java b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledExplicitlyForResponseAndError.java index cd026f427..88b42c690 100644 --- a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledExplicitlyForResponseAndError.java +++ b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledExplicitlyForResponseAndError.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,14 +11,15 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.tracing.handlers; +import static software.amazon.lambda.powertools.tracing.CaptureMode.RESPONSE_AND_ERROR; + import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import software.amazon.lambda.powertools.tracing.Tracing; -import static software.amazon.lambda.powertools.tracing.CaptureMode.RESPONSE_AND_ERROR; - public class PowerTracerToolEnabledExplicitlyForResponseAndError implements RequestHandler { @Override diff --git a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledForError.java b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledForError.java index c84d25763..47e23ed18 100644 --- a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledForError.java +++ b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledForError.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,14 +11,15 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.tracing.handlers; +import static software.amazon.lambda.powertools.tracing.CaptureMode.ERROR; + import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import software.amazon.lambda.powertools.tracing.Tracing; -import static software.amazon.lambda.powertools.tracing.CaptureMode.ERROR; - public class PowerTracerToolEnabledForError implements RequestHandler { @Override diff --git a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledForResponse.java b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledForResponse.java index 1e82f2148..be0fe9b24 100644 --- a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledForResponse.java +++ b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledForResponse.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,14 +11,15 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.tracing.handlers; +import static software.amazon.lambda.powertools.tracing.CaptureMode.RESPONSE; + import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import software.amazon.lambda.powertools.tracing.Tracing; -import static software.amazon.lambda.powertools.tracing.CaptureMode.RESPONSE; - public class PowerTracerToolEnabledForResponse implements RequestHandler { @Override diff --git a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledForResponseWithCustomMapper.java b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledForResponseWithCustomMapper.java index b7c908473..a18b1580d 100644 --- a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledForResponseWithCustomMapper.java +++ b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledForResponseWithCustomMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,9 +11,11 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.tracing.handlers; -import java.io.IOException; +import static software.amazon.lambda.powertools.tracing.CaptureMode.RESPONSE; + import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.fasterxml.jackson.core.JsonGenerator; @@ -21,11 +23,10 @@ import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.ser.std.StdSerializer; +import java.io.IOException; import software.amazon.lambda.powertools.tracing.Tracing; import software.amazon.lambda.powertools.tracing.TracingUtils; -import static software.amazon.lambda.powertools.tracing.CaptureMode.RESPONSE; - public class PowerTracerToolEnabledForResponseWithCustomMapper implements RequestHandler { static { ObjectMapper objectMapper = new ObjectMapper(); @@ -35,6 +36,7 @@ public class PowerTracerToolEnabledForResponseWithCustomMapper implements Reques TracingUtils.defaultObjectMapper(objectMapper); } + @Override @Tracing(namespace = "lambdaHandler", captureMode = RESPONSE) public Object handleRequest(Object input, Context context) { @@ -44,6 +46,25 @@ public Object handleRequest(Object input, Context context) { return parentClass; } + public static class ChildSerializer extends StdSerializer { + + public ChildSerializer() { + this(null); + } + + public ChildSerializer(Class t) { + super(t); + } + + @Override + public void serialize(ChildClass value, JsonGenerator jgen, SerializerProvider provider) throws IOException { + jgen.writeStartObject(); + jgen.writeStringField("name", value.name); + jgen.writeStringField("p", value.p.name); + jgen.writeEndObject(); + } + } + public class ParentClass { public String name; public ChildClass c; @@ -66,23 +87,4 @@ public ChildClass(String name, ParentClass p) { this.p = p; } } - - public static class ChildSerializer extends StdSerializer { - - public ChildSerializer() { - this(null); - } - - public ChildSerializer(Class t) { - super(t); - } - - @Override - public void serialize(ChildClass value, JsonGenerator jgen, SerializerProvider provider) throws IOException { - jgen.writeStartObject(); - jgen.writeStringField("name", value.name); - jgen.writeStringField("p", value.p.name); - jgen.writeEndObject(); - } - } } diff --git a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledForStream.java b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledForStream.java index e316ffe7d..5c693445a 100644 --- a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledForStream.java +++ b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledForStream.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,14 +11,14 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.tracing.handlers; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestStreamHandler; -import software.amazon.lambda.powertools.tracing.Tracing; - import java.io.InputStream; import java.io.OutputStream; +import software.amazon.lambda.powertools.tracing.Tracing; public class PowerTracerToolEnabledForStream implements RequestStreamHandler { diff --git a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledForStreamWithNoMetaData.java b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledForStreamWithNoMetaData.java index 4cd381807..4cd2bb0b7 100644 --- a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledForStreamWithNoMetaData.java +++ b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledForStreamWithNoMetaData.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,17 +11,17 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.tracing.handlers; -import java.io.InputStream; -import java.io.OutputStream; +import static software.amazon.lambda.powertools.tracing.CaptureMode.DISABLED; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestStreamHandler; +import java.io.InputStream; +import java.io.OutputStream; import software.amazon.lambda.powertools.tracing.Tracing; -import static software.amazon.lambda.powertools.tracing.CaptureMode.DISABLED; - public class PowerTracerToolEnabledForStreamWithNoMetaData implements RequestStreamHandler { @Override diff --git a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledWithException.java b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledWithException.java index cc184d020..88c506502 100644 --- a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledWithException.java +++ b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledWithException.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.tracing.handlers; import com.amazonaws.services.lambda.runtime.Context; diff --git a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledWithNoMetaData.java b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledWithNoMetaData.java index 2109d6647..b4d77cde3 100644 --- a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledWithNoMetaData.java +++ b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledWithNoMetaData.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,14 +11,15 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.tracing.handlers; +import static software.amazon.lambda.powertools.tracing.CaptureMode.DISABLED; + import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import software.amazon.lambda.powertools.tracing.Tracing; -import static software.amazon.lambda.powertools.tracing.CaptureMode.DISABLED; - public class PowerTracerToolEnabledWithNoMetaData implements RequestHandler { @Override diff --git a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledWithNoMetaDataDeprecated.java b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledWithNoMetaDataDeprecated.java index 2ded5e69f..c13f28df0 100644 --- a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledWithNoMetaDataDeprecated.java +++ b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/handlers/PowerTracerToolEnabledWithNoMetaDataDeprecated.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,14 +11,13 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.tracing.handlers; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import software.amazon.lambda.powertools.tracing.Tracing; -import static software.amazon.lambda.powertools.tracing.CaptureMode.DISABLED; - public class PowerTracerToolEnabledWithNoMetaDataDeprecated implements RequestHandler { @Override diff --git a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/internal/LambdaTracingAspectTest.java b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/internal/LambdaTracingAspectTest.java index 8cd9b2f71..5e3ec6545 100644 --- a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/internal/LambdaTracingAspectTest.java +++ b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/internal/LambdaTracingAspectTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,15 +11,24 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.tracing.internal; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; +import static org.apache.commons.lang3.reflect.FieldUtils.writeStaticField; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatNoException; +import static org.assertj.core.api.Assertions.catchThrowable; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.openMocks; + import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.lambda.runtime.RequestStreamHandler; import com.amazonaws.xray.AWSXRay; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; @@ -41,14 +50,6 @@ import software.amazon.lambda.powertools.tracing.handlers.PowerTracerToolEnabledWithNoMetaDataDeprecated; import software.amazon.lambda.powertools.tracing.nonhandler.PowerToolNonHandler; -import static org.apache.commons.lang3.reflect.FieldUtils.writeStaticField; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatNoException; -import static org.assertj.core.api.Assertions.catchThrowable; -import static org.mockito.Mockito.mockStatic; -import static org.mockito.Mockito.when; -import static org.mockito.MockitoAnnotations.openMocks; - class LambdaTracingAspectTest { private RequestHandler requestHandler; private RequestStreamHandler streamHandler; @@ -118,16 +119,17 @@ void shouldCaptureTraces() { assertThat(AWSXRay.getTraceEntity().getSubsegmentsCopy()) .hasSize(1) - .allSatisfy(subsegment -> { - assertThat(subsegment.getAnnotations()) - .hasSize(2) - .containsEntry("ColdStart", true) - .containsEntry("Service", "lambdaHandler"); - - assertThat(subsegment.getMetadata()) - .hasSize(1) - .containsKey("lambdaHandler"); - }); + .allSatisfy(subsegment -> + { + assertThat(subsegment.getAnnotations()) + .hasSize(2) + .containsEntry("ColdStart", true) + .containsEntry("Service", "lambdaHandler"); + + assertThat(subsegment.getMetadata()) + .hasSize(1) + .containsKey("lambdaHandler"); + }); } @Test @@ -141,20 +143,21 @@ void shouldCaptureTracesWithExceptionMetaData() { assertThat(AWSXRay.getTraceEntity().getSubsegmentsCopy()) .hasSize(1) - .allSatisfy(subsegment -> { - assertThat(subsegment.getAnnotations()) - .hasSize(2) - .containsEntry("ColdStart", true) - .containsEntry("Service", "lambdaHandler"); - - assertThat(subsegment.getMetadata()) - .hasSize(1) - .containsKey("lambdaHandler"); - - assertThat(subsegment.getMetadata().get("lambdaHandler")) - .satisfies(stringObjectMap -> assertThat(stringObjectMap) - .containsEntry("handleRequest error", exception)); - }); + .allSatisfy(subsegment -> + { + assertThat(subsegment.getAnnotations()) + .hasSize(2) + .containsEntry("ColdStart", true) + .containsEntry("Service", "lambdaHandler"); + + assertThat(subsegment.getMetadata()) + .hasSize(1) + .containsKey("lambdaHandler"); + + assertThat(subsegment.getMetadata().get("lambdaHandler")) + .satisfies(stringObjectMap -> assertThat(stringObjectMap) + .containsEntry("handleRequest error", exception)); + }); } @Test @@ -166,16 +169,17 @@ void shouldCaptureTracesForStream() throws IOException { assertThat(AWSXRay.getTraceEntity().getSubsegmentsCopy()) .hasSize(1) - .allSatisfy(subsegment -> { - assertThat(subsegment.getAnnotations()) - .hasSize(2) - .containsEntry("ColdStart", true) - .containsEntry("Service", "streamHandler"); - - assertThat(subsegment.getMetadata()) - .hasSize(1) - .containsKey("streamHandler"); - }); + .allSatisfy(subsegment -> + { + assertThat(subsegment.getAnnotations()) + .hasSize(2) + .containsEntry("ColdStart", true) + .containsEntry("Service", "streamHandler"); + + assertThat(subsegment.getMetadata()) + .hasSize(1) + .containsKey("streamHandler"); + }); } @Test @@ -207,15 +211,16 @@ void shouldCaptureTracesWithNoMetadata() { assertThat(AWSXRay.getTraceEntity().getSubsegmentsCopy()) .hasSize(1) - .allSatisfy(subsegment -> { - assertThat(subsegment.getAnnotations()) - .hasSize(2) - .containsEntry("ColdStart", true) - .containsEntry("Service", "service_undefined"); - - assertThat(subsegment.getMetadata()) - .isEmpty(); - }); + .allSatisfy(subsegment -> + { + assertThat(subsegment.getAnnotations()) + .hasSize(2) + .containsEntry("ColdStart", true) + .containsEntry("Service", "service_undefined"); + + assertThat(subsegment.getMetadata()) + .isEmpty(); + }); } @Test @@ -229,15 +234,16 @@ void shouldCaptureTracesForStreamWithNoMetadata() throws IOException { assertThat(AWSXRay.getTraceEntity().getSubsegmentsCopy()) .hasSize(1) - .allSatisfy(subsegment -> { - assertThat(subsegment.getAnnotations()) - .hasSize(2) - .containsEntry("ColdStart", true) - .containsEntry("Service", "service_undefined"); - - assertThat(subsegment.getMetadata()) - .isEmpty(); - }); + .allSatisfy(subsegment -> + { + assertThat(subsegment.getAnnotations()) + .hasSize(2) + .containsEntry("ColdStart", true) + .containsEntry("Service", "service_undefined"); + + assertThat(subsegment.getMetadata()) + .isEmpty(); + }); } @Test @@ -251,15 +257,16 @@ void shouldCaptureTracesWithNoMetadataDeprecated() { assertThat(AWSXRay.getTraceEntity().getSubsegmentsCopy()) .hasSize(1) - .allSatisfy(subsegment -> { - assertThat(subsegment.getAnnotations()) - .hasSize(2) - .containsEntry("ColdStart", true) - .containsEntry("Service", "service_undefined"); - - assertThat(subsegment.getMetadata()) - .isEmpty(); - }); + .allSatisfy(subsegment -> + { + assertThat(subsegment.getAnnotations()) + .hasSize(2) + .containsEntry("ColdStart", true) + .containsEntry("Service", "service_undefined"); + + assertThat(subsegment.getMetadata()) + .isEmpty(); + }); } @Test @@ -275,15 +282,16 @@ void shouldNotCaptureTracesIfDisabledViaEnvironmentVariable() { assertThat(AWSXRay.getTraceEntity().getSubsegmentsCopy()) .hasSize(1) - .allSatisfy(subsegment -> { - assertThat(subsegment.getAnnotations()) - .hasSize(2) - .containsEntry("ColdStart", true) - .containsEntry("Service", "lambdaHandler"); - - assertThat(subsegment.getMetadata()) - .isEmpty(); - }); + .allSatisfy(subsegment -> + { + assertThat(subsegment.getAnnotations()) + .hasSize(2) + .containsEntry("ColdStart", true) + .containsEntry("Service", "lambdaHandler"); + + assertThat(subsegment.getMetadata()) + .isEmpty(); + }); } } @@ -300,16 +308,17 @@ void shouldCaptureTracesIfExplicitlyEnabledAndEnvironmentVariableIsDisabled() { assertThat(AWSXRay.getTraceEntity().getSubsegmentsCopy()) .hasSize(1) - .allSatisfy(subsegment -> { - assertThat(subsegment.getAnnotations()) - .hasSize(2) - .containsEntry("ColdStart", true) - .containsEntry("Service", "lambdaHandler"); - - assertThat(subsegment.getMetadata()) - .hasSize(1) - .containsKey("lambdaHandler"); - }); + .allSatisfy(subsegment -> + { + assertThat(subsegment.getAnnotations()) + .hasSize(2) + .containsEntry("ColdStart", true) + .containsEntry("Service", "lambdaHandler"); + + assertThat(subsegment.getMetadata()) + .hasSize(1) + .containsKey("lambdaHandler"); + }); } } @@ -324,14 +333,16 @@ void shouldCaptureTracesForSelfReferencingReturnTypesViaCustomMapper() { assertThat(AWSXRay.getTraceEntity().getSubsegmentsCopy()) .hasSize(1) - .allSatisfy(subsegment -> { - assertThat(subsegment.getMetadata()) - .hasSize(1) - .containsKey("lambdaHandler"); + .allSatisfy(subsegment -> + { + assertThat(subsegment.getMetadata()) + .hasSize(1) + .containsKey("lambdaHandler"); - assertThat(subsegment.getMetadata().get("lambdaHandler")) - .hasFieldOrPropertyWithValue("handleRequest response", "{\"name\":\"parent\",\"c\":{\"name\":\"child\",\"p\":\"parent\"}}"); - }); + assertThat(subsegment.getMetadata().get("lambdaHandler")) + .hasFieldOrPropertyWithValue("handleRequest response", + "{\"name\":\"parent\",\"c\":{\"name\":\"child\",\"p\":\"parent\"}}"); + }); assertThatNoException().isThrownBy(AWSXRay::endSegment); @@ -354,16 +365,17 @@ void shouldCaptureTracesIfExplicitlyEnabledBothAndEnvironmentVariableIsDisabled( assertThat(AWSXRay.getTraceEntity().getSubsegmentsCopy()) .hasSize(1) - .allSatisfy(subsegment -> { - assertThat(subsegment.getAnnotations()) - .hasSize(2) - .containsEntry("ColdStart", true) - .containsEntry("Service", "lambdaHandler"); - - assertThat(subsegment.getMetadata()) - .hasSize(1) - .containsKey("lambdaHandler"); - }); + .allSatisfy(subsegment -> + { + assertThat(subsegment.getAnnotations()) + .hasSize(2) + .containsEntry("ColdStart", true) + .containsEntry("Service", "lambdaHandler"); + + assertThat(subsegment.getMetadata()) + .hasSize(1) + .containsKey("lambdaHandler"); + }); } } @@ -384,15 +396,16 @@ void shouldNotCaptureTracesWithExceptionMetaDataIfDisabledViaEnvironmentVariable assertThat(AWSXRay.getTraceEntity().getSubsegmentsCopy()) .hasSize(1) - .allSatisfy(subsegment -> { - assertThat(subsegment.getAnnotations()) - .hasSize(2) - .containsEntry("ColdStart", true) - .containsEntry("Service", "lambdaHandler"); - - assertThat(subsegment.getMetadata()) - .isEmpty(); - }); + .allSatisfy(subsegment -> + { + assertThat(subsegment.getAnnotations()) + .hasSize(2) + .containsEntry("ColdStart", true) + .containsEntry("Service", "lambdaHandler"); + + assertThat(subsegment.getMetadata()) + .isEmpty(); + }); } } @@ -410,20 +423,21 @@ void shouldCaptureTracesWithExceptionMetaDataEnabledExplicitlyAndEnvironmentVari assertThat(AWSXRay.getTraceEntity().getSubsegmentsCopy()) .hasSize(1) - .allSatisfy(subsegment -> { - assertThat(subsegment.getAnnotations()) - .hasSize(2) - .containsEntry("ColdStart", true) - .containsEntry("Service", "lambdaHandler"); - - assertThat(subsegment.getMetadata()) - .hasSize(1) - .containsKey("lambdaHandler"); - - assertThat(subsegment.getMetadata().get("lambdaHandler")) - .satisfies(stringObjectMap -> assertThat(stringObjectMap) - .containsEntry("handleRequest error", exception)); - }); + .allSatisfy(subsegment -> + { + assertThat(subsegment.getAnnotations()) + .hasSize(2) + .containsEntry("ColdStart", true) + .containsEntry("Service", "lambdaHandler"); + + assertThat(subsegment.getMetadata()) + .hasSize(1) + .containsKey("lambdaHandler"); + + assertThat(subsegment.getMetadata().get("lambdaHandler")) + .satisfies(stringObjectMap -> assertThat(stringObjectMap) + .containsEntry("handleRequest error", exception)); + }); } } diff --git a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/nonhandler/PowerToolNonHandler.java b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/nonhandler/PowerToolNonHandler.java index 309eb5598..48ffc93ae 100644 --- a/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/nonhandler/PowerToolNonHandler.java +++ b/powertools-tracing/src/test/java/software/amazon/lambda/powertools/tracing/nonhandler/PowerToolNonHandler.java @@ -1,3 +1,17 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.tracing.nonhandler; import software.amazon.lambda.powertools.tracing.Tracing; diff --git a/powertools-validation/pom.xml b/powertools-validation/pom.xml index 1469183ef..daec3aa99 100644 --- a/powertools-validation/pom.xml +++ b/powertools-validation/pom.xml @@ -1,4 +1,18 @@ + + @@ -119,4 +133,13 @@ + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + + + \ No newline at end of file diff --git a/powertools-validation/src/main/java/software/amazon/lambda/powertools/validation/Validation.java b/powertools-validation/src/main/java/software/amazon/lambda/powertools/validation/Validation.java index 68260cb47..f41364c1a 100644 --- a/powertools-validation/src/main/java/software/amazon/lambda/powertools/validation/Validation.java +++ b/powertools-validation/src/main/java/software/amazon/lambda/powertools/validation/Validation.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,11 +11,13 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.validation; +import static com.networknt.schema.SpecVersion.VersionFlag.V7; + import com.amazonaws.services.lambda.runtime.Context; import com.networknt.schema.SpecVersion.VersionFlag; - import java.io.InputStream; import java.io.OutputStream; import java.lang.annotation.ElementType; @@ -23,8 +25,6 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import static com.networknt.schema.SpecVersion.VersionFlag.V7; - /** * {@link Validation} is used to specify that the annotated method input and/or output needs to be valid.
    * diff --git a/powertools-validation/src/main/java/software/amazon/lambda/powertools/validation/ValidationConfig.java b/powertools-validation/src/main/java/software/amazon/lambda/powertools/validation/ValidationConfig.java index b29adf8d7..baf5e2465 100644 --- a/powertools-validation/src/main/java/software/amazon/lambda/powertools/validation/ValidationConfig.java +++ b/powertools-validation/src/main/java/software/amazon/lambda/powertools/validation/ValidationConfig.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.validation; import com.fasterxml.jackson.databind.JsonNode; @@ -26,7 +27,7 @@ /** * Use this if you need to customize some part of the JSON Schema validation * (eg. specification version, Jackson ObjectMapper, or adding functions to JMESPath). - * + *

    * For everything but the validation features (factory, schemaVersion), {@link ValidationConfig} * is just a wrapper of {@link JsonConfig}. */ @@ -62,7 +63,7 @@ public void setSchemaVersion(SpecVersion.VersionFlag version) { * {@link Base64Function} and {@link Base64GZipFunction} are already built-in. * * @param function the function to add - * @param Must extend {@link BaseFunction} + * @param Must extend {@link BaseFunction} */ public void addFunction(T function) { JsonConfig.get().addFunction(function); diff --git a/powertools-validation/src/main/java/software/amazon/lambda/powertools/validation/ValidationException.java b/powertools-validation/src/main/java/software/amazon/lambda/powertools/validation/ValidationException.java index 2d3e1b350..fd4cb66a6 100644 --- a/powertools-validation/src/main/java/software/amazon/lambda/powertools/validation/ValidationException.java +++ b/powertools-validation/src/main/java/software/amazon/lambda/powertools/validation/ValidationException.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.validation; public class ValidationException extends RuntimeException { diff --git a/powertools-validation/src/main/java/software/amazon/lambda/powertools/validation/ValidationUtils.java b/powertools-validation/src/main/java/software/amazon/lambda/powertools/validation/ValidationUtils.java index 3c2322edc..4eecb3ab5 100644 --- a/powertools-validation/src/main/java/software/amazon/lambda/powertools/validation/ValidationUtils.java +++ b/powertools-validation/src/main/java/software/amazon/lambda/powertools/validation/ValidationUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.validation; import com.amazonaws.services.lambda.runtime.serialization.PojoSerializer; @@ -22,8 +23,6 @@ import com.networknt.schema.JsonSchema; import com.networknt.schema.ValidationMessage; import io.burt.jmespath.Expression; -import software.amazon.lambda.powertools.validation.internal.ValidationAspect; - import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.util.Collections; @@ -31,6 +30,7 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; +import software.amazon.lambda.powertools.validation.internal.ValidationAspect; /** * Validation utility, used to manually validate Json against Json Schema @@ -68,7 +68,8 @@ public static void validate(Object obj, JsonSchema jsonSchema, String envelope) } JsonNode subNode; try { - PojoSerializer pojoSerializer = LambdaEventSerializers.serializerFor(obj.getClass(), ClassLoader.getSystemClassLoader()); + PojoSerializer pojoSerializer = + LambdaEventSerializers.serializerFor(obj.getClass(), ClassLoader.getSystemClassLoader()); ByteArrayOutputStream out = new ByteArrayOutputStream(); pojoSerializer.toJson(obj, out); JsonNode jsonNode = ValidationConfig.get().getObjectMapper().readTree(out.toString("UTF-8")); @@ -89,7 +90,8 @@ public static void validate(Object obj, JsonSchema jsonSchema, String envelope) try { validate(subNode.asText(), jsonSchema); } catch (ValidationException e) { - throw new ValidationException("Invalid format for '" + envelope + "': 'STRING' and no JSON found in it."); + throw new ValidationException( + "Invalid format for '" + envelope + "': 'STRING' and no JSON found in it."); } } else { throw new ValidationException("Invalid format for '" + envelope + "': '" + subNode.getNodeType() + "'"); @@ -208,9 +210,11 @@ public static void validate(JsonNode jsonNode, JsonSchema jsonSchema) throws Val if (!validationMessages.isEmpty()) { String message; try { - message = ValidationConfig.get().getObjectMapper().writeValueAsString(new ValidationErrors(validationMessages)); + message = ValidationConfig.get().getObjectMapper() + .writeValueAsString(new ValidationErrors(validationMessages)); } catch (JsonProcessingException e) { - message = validationMessages.stream().map(ValidationMessage::getMessage).collect(Collectors.joining(", ")); + message = validationMessages.stream().map(ValidationMessage::getMessage) + .collect(Collectors.joining(", ")); } throw new ValidationException(message); } @@ -255,7 +259,8 @@ private static JsonSchema createJsonSchema(String schema) { jsonSchema = ValidationConfig.get().getFactory().getSchema(schemaStream); } catch (Exception e) { - throw new IllegalArgumentException("'" + schema + "' is invalid, verify '" + filePath + "' is in your classpath"); + throw new IllegalArgumentException( + "'" + schema + "' is invalid, verify '" + filePath + "' is in your classpath"); } } else { jsonSchema = ValidationConfig.get().getFactory().getSchema(schema); @@ -270,7 +275,8 @@ private static void validateSchema(String schema, JsonSchema jsonSchema) { validate(jsonSchema.getSchemaNode(), getJsonSchema("classpath:/schemas/meta_schema_" + version)); } catch (ValidationException ve) { - throw new IllegalArgumentException("The schema " + schema + " is not valid, it does not respect the specification " + version, ve); + throw new IllegalArgumentException( + "The schema " + schema + " is not valid, it does not respect the specification " + version, ve); } } diff --git a/powertools-validation/src/main/java/software/amazon/lambda/powertools/validation/internal/ValidationAspect.java b/powertools-validation/src/main/java/software/amazon/lambda/powertools/validation/internal/ValidationAspect.java index e659abbeb..6055f8d58 100644 --- a/powertools-validation/src/main/java/software/amazon/lambda/powertools/validation/internal/ValidationAspect.java +++ b/powertools-validation/src/main/java/software/amazon/lambda/powertools/validation/internal/ValidationAspect.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,8 +11,17 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.validation.internal; +import static com.networknt.schema.SpecVersion.VersionFlag.V201909; +import static java.nio.charset.StandardCharsets.UTF_8; +import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.placedOnRequestHandler; +import static software.amazon.lambda.powertools.utilities.jmespath.Base64Function.decode; +import static software.amazon.lambda.powertools.utilities.jmespath.Base64GZipFunction.decompress; +import static software.amazon.lambda.powertools.validation.ValidationUtils.getJsonSchema; +import static software.amazon.lambda.powertools.validation.ValidationUtils.validate; + import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; import com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPEvent; @@ -43,14 +52,6 @@ import software.amazon.lambda.powertools.validation.Validation; import software.amazon.lambda.powertools.validation.ValidationConfig; -import static com.networknt.schema.SpecVersion.VersionFlag.V201909; -import static java.nio.charset.StandardCharsets.UTF_8; -import static software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor.placedOnRequestHandler; -import static software.amazon.lambda.powertools.utilities.jmespath.Base64Function.decode; -import static software.amazon.lambda.powertools.utilities.jmespath.Base64GZipFunction.decompress; -import static software.amazon.lambda.powertools.validation.ValidationUtils.getJsonSchema; -import static software.amazon.lambda.powertools.validation.ValidationUtils.validate; - /** * Aspect for {@link Validation} annotation */ @@ -108,27 +109,33 @@ public Object around(ProceedingJoinPoint pjp, validate(event.getResourceProperties(), inboundJsonSchema); } else if (obj instanceof KinesisEvent) { KinesisEvent event = (KinesisEvent) obj; - event.getRecords().forEach(record -> validate(decode(record.getKinesis().getData()), inboundJsonSchema)); + event.getRecords() + .forEach(record -> validate(decode(record.getKinesis().getData()), inboundJsonSchema)); } else if (obj instanceof KinesisFirehoseEvent) { KinesisFirehoseEvent event = (KinesisFirehoseEvent) obj; event.getRecords().forEach(record -> validate(decode(record.getData()), inboundJsonSchema)); } else if (obj instanceof KafkaEvent) { KafkaEvent event = (KafkaEvent) obj; - event.getRecords().forEach((s, records) -> records.forEach(record -> validate(decode(record.getValue()), inboundJsonSchema))); + event.getRecords().forEach((s, records) -> records.forEach( + record -> validate(decode(record.getValue()), inboundJsonSchema))); } else if (obj instanceof ActiveMQEvent) { ActiveMQEvent event = (ActiveMQEvent) obj; event.getMessages().forEach(record -> validate(decode(record.getData()), inboundJsonSchema)); } else if (obj instanceof RabbitMQEvent) { RabbitMQEvent event = (RabbitMQEvent) obj; - event.getRmqMessagesByQueue().forEach((s, records) -> records.forEach(record -> validate(decode(record.getData()), inboundJsonSchema))); + event.getRmqMessagesByQueue().forEach((s, records) -> records.forEach( + record -> validate(decode(record.getData()), inboundJsonSchema))); } else if (obj instanceof KinesisAnalyticsFirehoseInputPreprocessingEvent) { - KinesisAnalyticsFirehoseInputPreprocessingEvent event = (KinesisAnalyticsFirehoseInputPreprocessingEvent) obj; + KinesisAnalyticsFirehoseInputPreprocessingEvent event = + (KinesisAnalyticsFirehoseInputPreprocessingEvent) obj; event.getRecords().forEach(record -> validate(decode(record.getData()), inboundJsonSchema)); } else if (obj instanceof KinesisAnalyticsStreamsInputPreprocessingEvent) { - KinesisAnalyticsStreamsInputPreprocessingEvent event = (KinesisAnalyticsStreamsInputPreprocessingEvent) obj; + KinesisAnalyticsStreamsInputPreprocessingEvent event = + (KinesisAnalyticsStreamsInputPreprocessingEvent) obj; event.getRecords().forEach(record -> validate(decode(record.getData()), inboundJsonSchema)); } else { - LOG.warn("Unhandled event type {}, please use the 'envelope' parameter to specify what to validate", obj.getClass().getName()); + LOG.warn("Unhandled event type {}, please use the 'envelope' parameter to specify what to validate", + obj.getClass().getName()); } } } @@ -151,10 +158,12 @@ public Object around(ProceedingJoinPoint pjp, ApplicationLoadBalancerResponseEvent response = (ApplicationLoadBalancerResponseEvent) result; validate(response.getBody(), outboundJsonSchema); } else if (result instanceof KinesisAnalyticsInputPreprocessingResponse) { - KinesisAnalyticsInputPreprocessingResponse response = (KinesisAnalyticsInputPreprocessingResponse) result; + KinesisAnalyticsInputPreprocessingResponse response = + (KinesisAnalyticsInputPreprocessingResponse) result; response.getRecords().forEach(record -> validate(decode(record.getData()), outboundJsonSchema)); } else { - LOG.warn("Unhandled response type {}, please use the 'envelope' parameter to specify what to validate", result.getClass().getName()); + LOG.warn("Unhandled response type {}, please use the 'envelope' parameter to specify what to validate", + result.getClass().getName()); } } diff --git a/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/ValidationUtilsTest.java b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/ValidationUtilsTest.java index 6669e46b1..86dddd3e2 100644 --- a/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/ValidationUtilsTest.java +++ b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/ValidationUtilsTest.java @@ -1,25 +1,38 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package software.amazon.lambda.powertools.validation; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.assertThatNoException; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static software.amazon.lambda.powertools.validation.ValidationUtils.getJsonSchema; +import static software.amazon.lambda.powertools.validation.ValidationUtils.validate; + import com.fasterxml.jackson.databind.JsonNode; import com.networknt.schema.JsonSchema; import com.networknt.schema.SpecVersion; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import software.amazon.lambda.powertools.validation.model.Basket; import software.amazon.lambda.powertools.validation.model.MyCustomEvent; import software.amazon.lambda.powertools.validation.model.Product; -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.assertj.core.api.Assertions.assertThatNoException; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static software.amazon.lambda.powertools.validation.ValidationUtils.getJsonSchema; -import static software.amazon.lambda.powertools.validation.ValidationUtils.validate; - public class ValidationUtilsTest { private String schemaString = "classpath:/schema_v7.json"; @@ -43,16 +56,18 @@ public void testLoadSchemaV7KO() { ValidationConfig.get().setSchemaVersion(SpecVersion.VersionFlag.V7); assertThatThrownBy(() -> getJsonSchema("classpath:/schema_v7_ko.json", true)) .isInstanceOf(IllegalArgumentException.class) - .hasMessage("The schema classpath:/schema_v7_ko.json is not valid, it does not respect the specification V7"); + .hasMessage( + "The schema classpath:/schema_v7_ko.json is not valid, it does not respect the specification V7"); } @Test public void testLoadMetaSchema_NoValidation() { ValidationConfig.get().setSchemaVersion(SpecVersion.VersionFlag.V7); - assertThatNoException().isThrownBy(() -> { - getJsonSchema("classpath:/schema_v7_ko.json", false); - }); + assertThatNoException().isThrownBy(() -> + { + getJsonSchema("classpath:/schema_v7_ko.json", false); + }); } @Test @@ -99,16 +114,19 @@ public void testLoadSchemaNotFound() { @Test public void testValidateJsonNodeOK() throws IOException { - JsonNode node = ValidationConfig.get().getObjectMapper().readTree(this.getClass().getResourceAsStream("/json_ok.json")); + JsonNode node = + ValidationConfig.get().getObjectMapper().readTree(this.getClass().getResourceAsStream("/json_ok.json")); - assertThatNoException().isThrownBy(() -> { - validate(node, schemaString); - }); + assertThatNoException().isThrownBy(() -> + { + validate(node, schemaString); + }); } @Test public void testValidateJsonNodeKO() throws IOException { - JsonNode node = ValidationConfig.get().getObjectMapper().readTree(this.getClass().getResourceAsStream("/json_ko.json")); + JsonNode node = + ValidationConfig.get().getObjectMapper().readTree(this.getClass().getResourceAsStream("/json_ko.json")); assertThatExceptionOfType(ValidationException.class).isThrownBy(() -> validate(node, schema)); } @@ -121,9 +139,10 @@ public void testValidateMapOK() { map.put("name", "FooBar XY"); map.put("price", 258); - assertThatNoException().isThrownBy(() -> { - validate(map, schemaString); - }); + assertThatNoException().isThrownBy(() -> + { + validate(map, schemaString); + }); } @Test @@ -147,9 +166,10 @@ public void testValidateMapNotValidJsonObject() { public void testValidateStringOK() { String json = "{\n \"id\": 43242,\n \"name\": \"FooBar XY\",\n \"price\": 258\n}"; - assertThatNoException().isThrownBy(() -> { - validate(json, schemaString); - }); + assertThatNoException().isThrownBy(() -> + { + validate(json, schemaString); + }); } @Test @@ -163,9 +183,10 @@ public void testValidateStringKO() { public void testValidateObjectOK() { Product product = new Product(42, "FooBar", 42); - assertThatNoException().isThrownBy(() -> { - validate(product, schemaString); - }); + assertThatNoException().isThrownBy(() -> + { + validate(product, schemaString); + }); } @Test @@ -189,9 +210,10 @@ public void testValidateSubObjectOK() { basket.add(product2); MyCustomEvent event = new MyCustomEvent(basket); - assertThatNoException().isThrownBy(() -> { - validate(event, schemaString, "basket.products[0]"); - }); + assertThatNoException().isThrownBy(() -> + { + validate(event, schemaString, "basket.products[0]"); + }); } @Test @@ -203,7 +225,8 @@ public void testValidateSubObjectKO() { basket.add(product2); MyCustomEvent event = new MyCustomEvent(basket); - assertThatExceptionOfType(ValidationException.class).isThrownBy(() -> validate(event, schema, "basket.products[0]")); + assertThatExceptionOfType(ValidationException.class).isThrownBy( + () -> validate(event, schema, "basket.products[0]")); } @Test @@ -227,7 +250,8 @@ public void testValidateSubObjectListKO() { basket.add(product2); MyCustomEvent event = new MyCustomEvent(basket); - assertThatExceptionOfType(ValidationException.class).isThrownBy(() -> validate(event, schema, "basket.products[*]")); + assertThatExceptionOfType(ValidationException.class).isThrownBy( + () -> validate(event, schema, "basket.products[*]")); } @Test diff --git a/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/GenericSchemaV7Handler.java b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/GenericSchemaV7Handler.java index 1c64e7da1..5b8343d1b 100644 --- a/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/GenericSchemaV7Handler.java +++ b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/GenericSchemaV7Handler.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.validation.handlers; import com.amazonaws.services.lambda.runtime.Context; diff --git a/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/MyCustomEventHandler.java b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/MyCustomEventHandler.java index 07954ddff..6989cdbb6 100644 --- a/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/MyCustomEventHandler.java +++ b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/MyCustomEventHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.validation.handlers; import com.amazonaws.services.lambda.runtime.Context; diff --git a/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/SQSWithCustomEnvelopeHandler.java b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/SQSWithCustomEnvelopeHandler.java index 9eb96c0e8..c49ebff69 100644 --- a/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/SQSWithCustomEnvelopeHandler.java +++ b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/SQSWithCustomEnvelopeHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.validation.handlers; import com.amazonaws.services.lambda.runtime.Context; diff --git a/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/SQSWithWrongEnvelopeHandler.java b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/SQSWithWrongEnvelopeHandler.java index c1859f29a..d3f46d4ad 100644 --- a/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/SQSWithWrongEnvelopeHandler.java +++ b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/SQSWithWrongEnvelopeHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.validation.handlers; import com.amazonaws.services.lambda.runtime.Context; diff --git a/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/ValidationInboundStringHandler.java b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/ValidationInboundStringHandler.java index 2e62ba88d..fd5692884 100644 --- a/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/ValidationInboundStringHandler.java +++ b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/handlers/ValidationInboundStringHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.validation.handlers; import com.amazonaws.services.lambda.runtime.Context; diff --git a/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/internal/ResponseEventsArgumentsProvider.java b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/internal/ResponseEventsArgumentsProvider.java index 9df0ff508..b634d6f8c 100644 --- a/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/internal/ResponseEventsArgumentsProvider.java +++ b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/internal/ResponseEventsArgumentsProvider.java @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.validation.internal; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; @@ -18,15 +19,14 @@ import com.amazonaws.services.lambda.runtime.events.APIGatewayV2WebSocketResponse; import com.amazonaws.services.lambda.runtime.events.ApplicationLoadBalancerResponseEvent; import com.amazonaws.services.lambda.runtime.events.KinesisAnalyticsInputPreprocessingResponse; -import org.junit.jupiter.api.extension.ExtensionContext; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.ArgumentsProvider; - import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import java.util.stream.Stream; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; public class ResponseEventsArgumentsProvider implements ArgumentsProvider { @@ -49,9 +49,11 @@ public Stream provideArguments(ExtensionContext context) { KinesisAnalyticsInputPreprocessingResponse kaipResponse = new KinesisAnalyticsInputPreprocessingResponse(); List records = new ArrayList(); ByteBuffer buffer = ByteBuffer.wrap(body.getBytes(StandardCharsets.UTF_8)); - records.add(new KinesisAnalyticsInputPreprocessingResponse.Record("1", KinesisAnalyticsInputPreprocessingResponse.Result.Ok, buffer)); + records.add(new KinesisAnalyticsInputPreprocessingResponse.Record("1", + KinesisAnalyticsInputPreprocessingResponse.Result.Ok, buffer)); kaipResponse.setRecords(records); - return Stream.of(apiGWProxyResponseEvent, apiGWV2HTTPResponse, apiGWV2WebSocketResponse, albResponseEvent, kaipResponse).map(Arguments::of); + return Stream.of(apiGWProxyResponseEvent, apiGWV2HTTPResponse, apiGWV2WebSocketResponse, albResponseEvent, + kaipResponse).map(Arguments::of); } } diff --git a/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/internal/ValidationAspectTest.java b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/internal/ValidationAspectTest.java index 63e93c3ac..9ea596ff3 100644 --- a/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/internal/ValidationAspectTest.java +++ b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/internal/ValidationAspectTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,8 +11,14 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.validation.internal; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.assertThatNoException; +import static org.mockito.Mockito.when; + import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; @@ -34,6 +40,8 @@ import com.amazonaws.services.lambda.runtime.serialization.PojoSerializer; import com.amazonaws.services.lambda.runtime.serialization.events.LambdaEventSerializers; import com.networknt.schema.SpecVersion; +import java.io.IOException; +import java.util.stream.Stream; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.Signature; import org.junit.jupiter.api.BeforeEach; @@ -53,14 +61,6 @@ import software.amazon.lambda.powertools.validation.handlers.ValidationInboundStringHandler; import software.amazon.lambda.powertools.validation.model.MyCustomEvent; -import java.io.IOException; -import java.util.stream.Stream; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.assertj.core.api.Assertions.assertThatNoException; -import static org.mockito.Mockito.when; - public class ValidationAspectTest { @@ -109,9 +109,10 @@ public void testValidateOutboundJsonSchema(Object object) throws Throwable { when(validation.inboundSchema()).thenReturn(""); when(validation.outboundSchema()).thenReturn("classpath:/schema_v7.json"); - assertThatExceptionOfType(ValidationException.class).isThrownBy(() -> { - validationAspect.around(pjp, validation); - }); + assertThatExceptionOfType(ValidationException.class).isThrownBy(() -> + { + validationAspect.around(pjp, validation); + }); } @Test @@ -184,7 +185,8 @@ public void validate_inputKO_schemaInString_shouldThrowValidationException() { @Test public void validate_SQS() { - PojoSerializer pojoSerializer = LambdaEventSerializers.serializerFor(SQSEvent.class, ClassLoader.getSystemClassLoader()); + PojoSerializer pojoSerializer = + LambdaEventSerializers.serializerFor(SQSEvent.class, ClassLoader.getSystemClassLoader()); SQSEvent event = pojoSerializer.fromJson(this.getClass().getResourceAsStream("/sqs.json")); GenericSchemaV7Handler handler = new GenericSchemaV7Handler(); @@ -193,7 +195,8 @@ public void validate_SQS() { @Test public void validate_SQS_CustomEnvelopeTakePrecedence() { - PojoSerializer pojoSerializer = LambdaEventSerializers.serializerFor(SQSEvent.class, ClassLoader.getSystemClassLoader()); + PojoSerializer pojoSerializer = + LambdaEventSerializers.serializerFor(SQSEvent.class, ClassLoader.getSystemClassLoader()); SQSEvent event = pojoSerializer.fromJson(this.getClass().getResourceAsStream("/sqs_message.json")); SQSWithCustomEnvelopeHandler handler = new SQSWithCustomEnvelopeHandler(); @@ -202,7 +205,8 @@ public void validate_SQS_CustomEnvelopeTakePrecedence() { @Test public void validate_SQS_WrongEnvelope_shouldThrowValidationException() { - PojoSerializer pojoSerializer = LambdaEventSerializers.serializerFor(SQSEvent.class, ClassLoader.getSystemClassLoader()); + PojoSerializer pojoSerializer = + LambdaEventSerializers.serializerFor(SQSEvent.class, ClassLoader.getSystemClassLoader()); SQSEvent event = pojoSerializer.fromJson(this.getClass().getResourceAsStream("/sqs_message.json")); SQSWithWrongEnvelopeHandler handler = new SQSWithWrongEnvelopeHandler(); @@ -211,7 +215,8 @@ public void validate_SQS_WrongEnvelope_shouldThrowValidationException() { @Test public void validate_Kinesis() { - PojoSerializer pojoSerializer = LambdaEventSerializers.serializerFor(KinesisEvent.class, ClassLoader.getSystemClassLoader()); + PojoSerializer pojoSerializer = + LambdaEventSerializers.serializerFor(KinesisEvent.class, ClassLoader.getSystemClassLoader()); KinesisEvent event = pojoSerializer.fromJson(this.getClass().getResourceAsStream("/kinesis.json")); GenericSchemaV7Handler handler = new GenericSchemaV7Handler(); @@ -221,7 +226,8 @@ public void validate_Kinesis() { @ParameterizedTest @MethodSource("provideEventAndEventType") public void validateEEvent(String jsonResource, Class eventClass) throws IOException { - Object event = ValidationConfig.get().getObjectMapper().readValue(this.getClass().getResourceAsStream(jsonResource), eventClass); + Object event = ValidationConfig.get().getObjectMapper() + .readValue(this.getClass().getResourceAsStream(jsonResource), eventClass); GenericSchemaV7Handler handler = new GenericSchemaV7Handler(); assertThat(handler.handleRequest(event, context)).isEqualTo("OK"); diff --git a/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/model/Basket.java b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/model/Basket.java index 398f67265..881090bdc 100644 --- a/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/model/Basket.java +++ b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/model/Basket.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.validation.model; import java.util.ArrayList; diff --git a/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/model/MyCustomEvent.java b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/model/MyCustomEvent.java index 12f3f99ca..04c7c3a4a 100644 --- a/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/model/MyCustomEvent.java +++ b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/model/MyCustomEvent.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.validation.model; public class MyCustomEvent { diff --git a/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/model/Product.java b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/model/Product.java index fde888b76..93f5ab39f 100644 --- a/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/model/Product.java +++ b/powertools-validation/src/test/java/software/amazon/lambda/powertools/validation/model/Product.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. + * Copyright 2023 Amazon.com, Inc. or its affiliates. * Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -11,6 +11,7 @@ * limitations under the License. * */ + package software.amazon.lambda.powertools.validation.model; public class Product {