builder.withCacheManager().build()) + */ + public CustomProviderBuilder withCacheManager(CacheManager cacheManager) { + this.cacheManager = cacheManager; + return this; + } - ```java hl_lines="3" - public class AppWithAnnotation implements RequestHandler
builder.withTransformationManager().build()) + */ + public CustomProviderBuilder withTransformationManager(TransformationManager transformationManager) { + this.transformationManager = transformationManager; + return this; + } } ``` -=== "Custom Provider Usage" - - ```java hl_lines="3" - public class AppWithAnnotation implements RequestHandler
+ * @AppConfigParam(key = "my-param", environment = "my-env", application = "my-app") + * String appConfigParam; + *+ */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface AppConfigParam { + String key(); + + /** + * Mandatory. Provide an environment to the {@link AppConfigProvider} + */ + String environment(); + + /** + * Mandatory. Provide an application to the {@link AppConfigProvider} + */ + String application(); + + /** + * Optional Provide a Transformer to transform the returned parameter values. + */ + Class extends Transformer> transformer() default Transformer.class; +} diff --git a/powertools-parameters/powertools-parameters-appconfig/src/main/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigParametersAspect.java b/powertools-parameters/powertools-parameters-appconfig/src/main/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigParametersAspect.java new file mode 100644 index 000000000..7ab4cf23e --- /dev/null +++ b/powertools-parameters/powertools-parameters-appconfig/src/main/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigParametersAspect.java @@ -0,0 +1,54 @@ +/* + * 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.appconfig; + +import java.util.function.BiFunction; +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.FieldSignature; +import software.amazon.lambda.powertools.parameters.BaseParamAspect; + +/** + * Provides the AppConfig parameter aspect. This aspect is responsible for injecting + * parameters from AWS AppConfig into fields annotated with @AppConfigParam. See the + * README and Powertools for Lambda (Java) documentation for information on using this feature. + */ +@Aspect +public class AppConfigParametersAspect extends BaseParamAspect { + + private static BiFunction
builder.withClient().build()) - */ - public AppConfigProvider.Builder withClient(AppConfigDataClient client) { - this.client = client; - return this; - } - - /** - * Mandatory. Provide an environment to the {@link AppConfigProvider} - * - * @param environment the AppConfig environment - * @return the builder to chain calls (eg.
builder.withCacheManager().build()) - */ - public AppConfigProvider.Builder withEnvironment(String environment) { - this.environment = environment; - return this; - } - - /** - * Mandatory. Provide an application to the {@link AppConfigProvider} - * - * @param application the application to pull configuration from - * @return the builder to chain calls (eg.
builder.withCacheManager().build()) - */ - public AppConfigProvider.Builder withApplication(String application) { - this.application = application; - return this; - } - - /** - * Mandatory. Provide a CacheManager to the {@link AppConfigProvider} - * - * @param cacheManager the manager that will handle the cache of parameters - * @return the builder to chain calls (eg.
builder.withCacheManager().build()) - */ - public AppConfigProvider.Builder withCacheManager(CacheManager cacheManager) { - this.cacheManager = cacheManager; - return this; - } - - /** - * Provide a transformationManager to the {@link AppConfigProvider} - * - * @param transformationManager the manager that will handle transformation of parameters - * @return the builder to chain calls (eg.
builder.withTransformationManager().build()) - */ - public AppConfigProvider.Builder withTransformationManager(TransformationManager transformationManager) { - this.transformationManager = transformationManager; - return this; - } - } } diff --git a/powertools-parameters/powertools-parameters-appconfig/src/main/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigProviderBuilder.java b/powertools-parameters/powertools-parameters-appconfig/src/main/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigProviderBuilder.java new file mode 100644 index 000000000..dadacb843 --- /dev/null +++ b/powertools-parameters/powertools-parameters-appconfig/src/main/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigProviderBuilder.java @@ -0,0 +1,124 @@ +/* + * 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.appconfig; + +import software.amazon.awssdk.core.SdkSystemSetting; +import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; +import software.amazon.awssdk.core.client.config.SdkAdvancedClientOption; +import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.appconfigdata.AppConfigDataClient; +import software.amazon.lambda.powertools.common.internal.UserAgentConfigurator; +import software.amazon.lambda.powertools.parameters.BaseProvider; +import software.amazon.lambda.powertools.parameters.ParamProvider; +import software.amazon.lambda.powertools.parameters.cache.CacheManager; +import software.amazon.lambda.powertools.parameters.transform.TransformationManager; + +/** + * Implements a {@link ParamProvider} on top of the AppConfig service. AppConfig provides + */ +public class AppConfigProviderBuilder { + private AppConfigDataClient client; + private CacheManager cacheManager; + private TransformationManager transformationManager; + private String environment; + private String application; + + /** + * Create a {@link AppConfigProvider} instance. + * + * @return a {@link AppConfigProvider} + */ + public AppConfigProvider build() { + if (cacheManager == null) { + cacheManager = new CacheManager(); + } + if (environment == null) { + throw new IllegalStateException("No environment provided; please provide one"); + } + if (application == null) { + throw new IllegalStateException("No application provided; please provide one"); + } + + // Create a AppConfigDataClient if we haven't been given one + if (client == null) { + client = AppConfigDataClient.builder() + .httpClientBuilder(UrlConnectionHttpClient.builder()) + .region(Region.of(System.getenv(SdkSystemSetting.AWS_REGION.environmentVariable()))) + .overrideConfiguration(ClientOverrideConfiguration.builder() + .putAdvancedOption(SdkAdvancedClientOption.USER_AGENT_SUFFIX, + UserAgentConfigurator.getUserAgent(BaseProvider.PARAMETERS)).build()) + .build(); + } + + return new AppConfigProvider(cacheManager, transformationManager, client, environment, application); + } + + /** + * Set custom {@link AppConfigDataClient} to pass to the {@link AppConfigProvider}.
builder.withClient().build()) + */ + public AppConfigProviderBuilder withClient(AppConfigDataClient client) { + this.client = client; + return this; + } + + /** + * Mandatory. Provide an environment to the {@link AppConfigProvider} + * + * @param environment the AppConfig environment + * @return the builder to chain calls (eg.
builder.withCacheManager().build()) + */ + public AppConfigProviderBuilder withEnvironment(String environment) { + this.environment = environment; + return this; + } + + /** + * Mandatory. Provide an application to the {@link AppConfigProvider} + * + * @param application the application to pull configuration from + * @return the builder to chain calls (eg.
builder.withCacheManager().build()) + */ + public AppConfigProviderBuilder withApplication(String application) { + this.application = application; + return this; + } + + /** + * Provide a CacheManager to the {@link AppConfigProvider} + * + * @param cacheManager the manager that will handle the cache of parameters + * @return the builder to chain calls (eg.
builder.withCacheManager().build()) + */ + public AppConfigProviderBuilder withCacheManager(CacheManager cacheManager) { + this.cacheManager = cacheManager; + return this; + } + + /** + * Provide a transformationManager to the {@link AppConfigProvider} + * + * @param transformationManager the manager that will handle transformation of parameters + * @return the builder to chain calls (eg.
builder.withTransformationManager().build()) + */ + public AppConfigProviderBuilder withTransformationManager(TransformationManager transformationManager) { + this.transformationManager = transformationManager; + return this; + } +} diff --git a/powertools-parameters/powertools-parameters-appconfig/src/test/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigParamAspectTest.java b/powertools-parameters/powertools-parameters-appconfig/src/test/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigParamAspectTest.java new file mode 100644 index 000000000..f50e88ec5 --- /dev/null +++ b/powertools-parameters/powertools-parameters-appconfig/src/test/java/software/amazon/lambda/powertools/parameters/appconfig/AppConfigParamAspectTest.java @@ -0,0 +1,55 @@ +/* + * 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.appconfig; + +import static org.apache.commons.lang3.reflect.FieldUtils.writeStaticField; +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.function.BiFunction; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +public class AppConfigParamAspectTest { + + @Test + public void parameterInjectedByProvider() throws Exception { + // Setup our aspect to return a mocked AppConfigProvider + String environment = "myEnvironment"; + String appName = "myApp"; + String key = "myKey"; + String value = "myValue"; + AppConfigProvider provider = Mockito.mock(AppConfigProvider.class); + BiFunction
By default {@code Param} use {@link SSMProvider} as parameter provider. This can be overridden specifying
- * the annotation variable {@code Param(provider =
- * The library provide a provider for AWS System Manager Parameters Store ({@link SSMProvider}) and a provider
- * for AWS Secrets Manager ({@link SecretsProvider}).
- * The user can implement a custom provider by extending the abstract class {@link BaseProvider}.
If the parameter value requires transformation before being assigned to the annotated field - * users can specify a {@link Transformer} - *
+ * Usage: + *+ * @DynamoDbParam(key = "my-param", table = "my-table") + * String myParameter; + **/ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) -public @interface Param { +public @interface DynamoDbParam { + /** + * Mandatory. Partition key from the DynamoDB table + */ String key(); - Class extends BaseProvider> provider() default SSMProvider.class; + /** + * Mandatory. Table name for the DynamoDB table + * @return + */ + String table(); + /** + * Optional Provide a Transformer to transform the returned parameter values. + */ Class extends Transformer> transformer() default Transformer.class; } diff --git a/powertools-parameters/powertools-parameters-dynamodb/src/main/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbParamAspect.java b/powertools-parameters/powertools-parameters-dynamodb/src/main/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbParamAspect.java new file mode 100644 index 000000000..1aa022cbd --- /dev/null +++ b/powertools-parameters/powertools-parameters-dynamodb/src/main/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbParamAspect.java @@ -0,0 +1,51 @@ +/* + * 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.dynamodb; + +import java.util.function.Function; +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.FieldSignature; +import software.amazon.lambda.powertools.parameters.BaseParamAspect; +import software.amazon.lambda.powertools.parameters.BaseProvider; + +/** + * Provides the Amazon DynamoDB parameter aspect. This aspect is responsible for injecting + * parameters from DynamoDB into fields annotated with @DynamoDbParam. See the + * README and Powertools for Lambda (Java) documentation for information on using this feature. + */ +@Aspect +public class DynamoDbParamAspect extends BaseParamAspect { + + private static Function
builder.withClient().build()) + */ + public DynamoDbProviderBuilder withClient(DynamoDbClient client) { + this.client = client; + return this; + } + + /** + * Provide a CacheManager to the {@link DynamoDbProvider} + * + * @param cacheManager the manager that will handle the cache of parameters + * @return the builder to chain calls (eg.
builder.withCacheManager().build()) + */ + public DynamoDbProviderBuilder withCacheManager(CacheManager cacheManager) { + this.cacheManager = cacheManager; + return this; + } + + /** + * Mandatory. Provide a DynamoDB table to the {@link DynamoDbProvider} + * + * @param table the table that parameters will be retrieved from. + * @return the builder to chain calls (eg.
builder.withTable().build()) + */ + public DynamoDbProviderBuilder withTable(String table) { + this.table = table; + return this; + } + + /** + * Provide a transformationManager to the {@link DynamoDbProvider} + * + * @param transformationManager the manager that will handle transformation of parameters + * @return the builder to chain calls (eg.
builder.withTransformationManager().build()) + */ + public DynamoDbProviderBuilder withTransformationManager(TransformationManager transformationManager) { + this.transformationManager = transformationManager; + return this; + } +} diff --git a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/exception/DynamoDbProviderSchemaException.java b/powertools-parameters/powertools-parameters-dynamodb/src/main/java/software/amazon/lambda/powertools/parameters/dynamodb/exception/DynamoDbProviderSchemaException.java similarity index 92% rename from powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/exception/DynamoDbProviderSchemaException.java rename to powertools-parameters/powertools-parameters-dynamodb/src/main/java/software/amazon/lambda/powertools/parameters/dynamodb/exception/DynamoDbProviderSchemaException.java index 77df6e3d3..4a22dbc99 100644 --- a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/exception/DynamoDbProviderSchemaException.java +++ b/powertools-parameters/powertools-parameters-dynamodb/src/main/java/software/amazon/lambda/powertools/parameters/dynamodb/exception/DynamoDbProviderSchemaException.java @@ -12,7 +12,7 @@ * */ -package software.amazon.lambda.powertools.parameters.exception; +package software.amazon.lambda.powertools.parameters.dynamodb.exception; /** * Thrown when the DynamoDbProvider comes across parameter data that diff --git a/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbParamAspectTest.java b/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbParamAspectTest.java new file mode 100644 index 000000000..07e93a7c1 --- /dev/null +++ b/powertools-parameters/powertools-parameters-dynamodb/src/test/java/software/amazon/lambda/powertools/parameters/dynamodb/DynamoDbParamAspectTest.java @@ -0,0 +1,55 @@ +/* + * 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.dynamodb; + +import static org.apache.commons.lang3.reflect.FieldUtils.writeStaticField; +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.function.Function; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +public class DynamoDbParamAspectTest { + + @Test + public void parameterInjectedByProvider() throws Exception { + // Setup our aspect to return a mocked DynamoDbProvider + String tableName = "my-test-tablename"; + String key = "myKey"; + String value = "myValue"; + DynamoDbProvider provider = Mockito.mock(DynamoDbProvider.class); + + Function
+ * @SecretsParam(key = "my-secret") + * String mySecret; + *+ */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface SecretsParam { + /** + * Mandatory. key from the secrets manager store. + * @return + */ + String key(); + + /** + * Optional. a transfer to apply to the value + */ + Class extends Transformer> transformer() default Transformer.class; +} diff --git a/powertools-parameters/powertools-parameters-secrets/src/main/java/software/amazon/lambda/powertools/parameters/secrets/SecretsParamAspect.java b/powertools-parameters/powertools-parameters-secrets/src/main/java/software/amazon/lambda/powertools/parameters/secrets/SecretsParamAspect.java new file mode 100644 index 000000000..748c88cc9 --- /dev/null +++ b/powertools-parameters/powertools-parameters-secrets/src/main/java/software/amazon/lambda/powertools/parameters/secrets/SecretsParamAspect.java @@ -0,0 +1,48 @@ +/* + * 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.secrets; + +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 org.aspectj.lang.reflect.FieldSignature; +import software.amazon.lambda.powertools.parameters.BaseParamAspect; + +/** + * Provides the Secrets parameter aspect. This aspect is responsible for injecting + * parameters from AWS Secrets Manager into fields annotated with @SecretsParam. See the + * README and Powertools for Lambda (Java) documentation for information on using this feature. + */ +@Aspect +public class SecretsParamAspect extends BaseParamAspect { + + private static Supplier
+ * SecretsProvider provider = SecretsProvider.builder().build(); + * + * String value = provider.get("key"); + * System.out.println(value); + * >>> "value" + * + * // Get a value and cache it for 30 seconds (all others values will now be cached for 30 seconds) + * String value = provider.defaultMaxAge(30, ChronoUnit.SECONDS).get("key"); + * + * // Get a value and cache it for 1 minute (all others values are cached for 5 seconds by default) + * String value = provider.withMaxAge(1, ChronoUnit.MINUTES).get("key"); + * + * // Get a base64 encoded value, decoded into a String, and store it in the cache + * String value = provider.withTransformation(Transformer.base64).get("key"); + * + * // Get a json value, transform it into an Object, and store it in the cache + * TargetObject = provider.withTransformation(Transformer.json).get("key", TargetObject.class); + *+ */ +public class SecretsProvider extends BaseProvider { + + private final SecretsManagerClient client; + + /** + * Use the {@link SecretsProviderBuilder} to create an instance! + * + * @param client custom client you would like to use. + */ + SecretsProvider(CacheManager cacheManager, TransformationManager transformationManager, + SecretsManagerClient client) { + super(cacheManager, transformationManager); + this.client = client; + } + + /** + * Create a builder that can be used to configure and create a {@link SecretsProvider}. + * + * @return a new instance of {@link SecretsProviderBuilder} + */ + public static SecretsProviderBuilder builder() { + return new SecretsProviderBuilder(); + } + + /** + * Create a SecretsProvider with all default settings. + */ + public static SecretsProvider create() { + return new SecretsProviderBuilder().build(); + } + + /** + * Retrieve the parameter value from the AWS Secrets Manager. + * + * @param key key of the parameter + * @return the value of the parameter identified by the key + */ + @Override + protected String getValue(String key) { + GetSecretValueRequest request = GetSecretValueRequest.builder().secretId(key).build(); + + String secretValue = client.getSecretValue(request).secretString(); + if (secretValue == null) { + 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 + protected Map
builder.withClient().build()) + */ + public SecretsProviderBuilder withClient(SecretsManagerClient client) { + this.client = client; + return this; + } + + /** + * Provide a CacheManager to the {@link SecretsProvider} + * + * @param cacheManager the manager that will handle the cache of parameters + * @return the builder to chain calls (eg.
builder.withCacheManager().build()) + */ + public SecretsProviderBuilder withCacheManager(CacheManager cacheManager) { + this.cacheManager = cacheManager; + return this; + } + + /** + * Provide a transformationManager to the {@link SecretsProvider} + * + * @param transformationManager the manager that will handle transformation of parameters + * @return the builder to chain calls (eg.
builder.withTransformationManager().build()) + */ + public SecretsProviderBuilder withTransformationManager(TransformationManager transformationManager) { + this.transformationManager = transformationManager; + return this; + } +} diff --git a/powertools-parameters/powertools-parameters-secrets/src/test/java/software/amazon/lambda/powertools/parameters/secrets/SecretsParamAspectTest.java b/powertools-parameters/powertools-parameters-secrets/src/test/java/software/amazon/lambda/powertools/parameters/secrets/SecretsParamAspectTest.java new file mode 100644 index 000000000..7aa0f0872 --- /dev/null +++ b/powertools-parameters/powertools-parameters-secrets/src/test/java/software/amazon/lambda/powertools/parameters/secrets/SecretsParamAspectTest.java @@ -0,0 +1,49 @@ +/* + * 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.secrets; + +import static org.apache.commons.lang3.reflect.FieldUtils.writeStaticField; +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.function.Supplier; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +public class SecretsParamAspectTest { + + @Test + public void parameterInjectedByProvider() throws Exception { + // Setup our aspect to return a mocked SecretsProvider + String key = "myKey"; + String value = "mySecretValue"; + SecretsProvider provider = Mockito.mock(SecretsProvider.class); + + Supplier
+ * @SSMParam(key = "/my/parameter") + * String myParameter; + *+ */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface SSMParam { + /** + * Mandatory. Key from the SSM parameter store + * @return + */ + String key(); + + + /** + * Optional. a transfer to apply to the value + */ + Class extends Transformer> transformer() default Transformer.class; +} diff --git a/powertools-parameters/powertools-parameters-ssm/src/main/java/software/amazon/lambda/powertools/parameters/ssm/SSMParamAspect.java b/powertools-parameters/powertools-parameters-ssm/src/main/java/software/amazon/lambda/powertools/parameters/ssm/SSMParamAspect.java new file mode 100644 index 000000000..b4d370506 --- /dev/null +++ b/powertools-parameters/powertools-parameters-ssm/src/main/java/software/amazon/lambda/powertools/parameters/ssm/SSMParamAspect.java @@ -0,0 +1,49 @@ +/* + * 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.ssm; + +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 org.aspectj.lang.reflect.FieldSignature; +import software.amazon.lambda.powertools.parameters.BaseParamAspect; + +/** + * Provides the SSM parameter store parameter aspect. This aspect is responsible for injecting + * parameters from SSM Parameter Store into fields annotated with @SSMParam. See the + * README and Powertools for Lambda (Java) documentation for information on using this feature. + */ +@Aspect +public class SSMParamAspect extends BaseParamAspect { + + // This supplier produces a new SSMProvider each time it is called + private static Supplier
- * SSMProvider provider = ParamManager.getSsmProvider(); + * SSMProvider provider = SSMProvider.builder().build(); * * String value = provider.get("key"); * System.out.println(value); @@ -80,33 +73,30 @@ public class SSMProvider extends BaseProvider { * Constructor with custom {@link SsmClient}.
* Use when you need to customize region or any other attribute of the client.
*- * Use the {@link SSMProvider.Builder} to create an instance of it. + * Use the {@link SSMProviderBuilder} to create an instance of it. * - * @param client custom client you would like to use. + * @param client custom client you would like to use. + * @param transformationManager Null, or a transformation manager */ - SSMProvider(CacheManager cacheManager, SsmClient client) { - super(cacheManager); + SSMProvider(CacheManager cacheManager, TransformationManager transformationManager, SsmClient client) { + super(cacheManager, transformationManager); this.client = client; } /** - * Constructor with only a CacheManager
- *- * Used in {@link ParamManager#createProvider(Class)} + * Create a builder that can be used to configure and create a {@link SSMProvider}. * - * @param cacheManager handles the parameter caching + * @return a new instance of {@link SSMProviderBuilder} */ - SSMProvider(CacheManager cacheManager) { - this(cacheManager, Builder.createClient()); + public static SSMProviderBuilder builder() { + return new SSMProviderBuilder(); } /** - * Create a builder that can be used to configure and create a {@link SSMProvider}. - * - * @return a new instance of {@link SSMProvider.Builder} + * Create a SSMProvider with all default settings. */ - public static SSMProvider.Builder builder() { - return new SSMProvider.Builder(); + public static SSMProvider create() { + return new SSMProviderBuilder().build(); } /** @@ -124,33 +114,6 @@ public String getValue(String key) { return client.getParameter(request).parameter().value(); } - /** - * {@inheritDoc} - */ - @Override - public SSMProvider defaultMaxAge(int maxAge, ChronoUnit unit) { - super.defaultMaxAge(maxAge, unit); - return this; - } - - /** - * {@inheritDoc} - */ - @Override - public SSMProvider withMaxAge(int maxAge, ChronoUnit unit) { - super.withMaxAge(maxAge, unit); - return this; - } - - /** - * {@inheritDoc} - */ - @Override - public SSMProvider withTransformation(Class extends Transformer> transformerClass) { - super.withTransformation(transformerClass); - return this; - } - /** * Tells System Manager Parameter Store to decrypt the parameter value.
* By default, parameter values are not decrypted.
@@ -210,7 +173,7 @@ private MapgetMultipleBis(String path, String nextToken) { res.parameters().forEach(parameter -> { /* Standardize the parameter name - The parameter name returned by SSM will contained the full path. + The parameter name returned by SSM will contain the full path. However, for readability, we should return only the part after the path. */ @@ -242,75 +205,4 @@ SsmClient getClient() { return client; } - 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()))) - .overrideConfiguration(ClientOverrideConfiguration.builder() - .putAdvancedOption(SdkAdvancedClientOption.USER_AGENT_SUFFIX, - UserAgentConfigurator.getUserAgent(PARAMETERS)).build()) - .build(); - } - - /** - * Create a {@link SSMProvider} instance. - * - * @return a {@link SSMProvider} - */ - public SSMProvider build() { - if (cacheManager == null) { - throw new IllegalStateException("No CacheManager provided, please provide one"); - } - SSMProvider provider; - if (client == null) { - client = createClient(); - } - - provider = new SSMProvider(cacheManager, client); - - if (transformationManager != null) { - provider.setTransformationManager(transformationManager); - } - return provider; - } - - /** - * Set custom {@link SsmClient} to pass to the {@link SSMProvider}.
- * Use it if you want to customize the region or any other part of the client. - * - * @param client Custom client - * @return the builder to chain calls (eg.builder.withClient().build()) - */ - public SSMProvider.Builder withClient(SsmClient client) { - this.client = client; - return this; - } - - /** - * Mandatory. Provide a CacheManager to the {@link SSMProvider} - * - * @param cacheManager the manager that will handle the cache of parameters - * @return the builder to chain calls (eg.builder.withCacheManager().build()) - */ - public SSMProvider.Builder withCacheManager(CacheManager cacheManager) { - this.cacheManager = cacheManager; - return this; - } - - /** - * Provide a transformationManager to the {@link SSMProvider} - * - * @param transformationManager the manager that will handle transformation of parameters - * @return the builder to chain calls (eg.builder.withTransformationManager().build()) - */ - public SSMProvider.Builder withTransformationManager(TransformationManager transformationManager) { - this.transformationManager = transformationManager; - return this; - } - } } diff --git a/powertools-parameters/powertools-parameters-ssm/src/main/java/software/amazon/lambda/powertools/parameters/ssm/SSMProviderBuilder.java b/powertools-parameters/powertools-parameters-ssm/src/main/java/software/amazon/lambda/powertools/parameters/ssm/SSMProviderBuilder.java new file mode 100644 index 000000000..3b4fff1b3 --- /dev/null +++ b/powertools-parameters/powertools-parameters-ssm/src/main/java/software/amazon/lambda/powertools/parameters/ssm/SSMProviderBuilder.java @@ -0,0 +1,98 @@ +/* + * 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.ssm; + +import software.amazon.awssdk.core.SdkSystemSetting; +import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; +import software.amazon.awssdk.core.client.config.SdkAdvancedClientOption; +import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.ssm.SsmClient; +import software.amazon.lambda.powertools.common.internal.UserAgentConfigurator; +import software.amazon.lambda.powertools.parameters.BaseProvider; +import software.amazon.lambda.powertools.parameters.cache.CacheManager; +import software.amazon.lambda.powertools.parameters.transform.TransformationManager; + +/** + * Builder for the {@link SSMProvider} + */ +public class SSMProviderBuilder { + 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()))) + .overrideConfiguration(ClientOverrideConfiguration.builder() + .putAdvancedOption(SdkAdvancedClientOption.USER_AGENT_SUFFIX, + UserAgentConfigurator.getUserAgent(BaseProvider.PARAMETERS)).build()) + .build(); + } + + /** + * Create a {@link SSMProvider} instance. + * + * @return a {@link SSMProvider} + */ + public SSMProvider build() { + if (cacheManager == null) { + cacheManager = new CacheManager(); + } + SSMProvider provider; + if (client == null) { + client = createClient(); + } + + provider = new SSMProvider(cacheManager, transformationManager, client); + + return provider; + } + + /** + * Set custom {@link SsmClient} to pass to the {@link SSMProvider}.
+ * Use it if you want to customize the region or any other part of the client. + * + * @param client Custom client + * @return the builder to chain calls (eg.builder.withClient().build()) + */ + public SSMProviderBuilder withClient(SsmClient client) { + this.client = client; + return this; + } + + /** + * Provide a CacheManager to the {@link SSMProvider} + * + * @param cacheManager the manager that will handle the cache of parameters + * @return the builder to chain calls (eg.builder.withCacheManager().build()) + */ + public SSMProviderBuilder withCacheManager(CacheManager cacheManager) { + this.cacheManager = cacheManager; + return this; + } + + /** + * Provide a transformationManager to the {@link SSMProvider} + * + * @param transformationManager the manager that will handle transformation of parameters + * @return the builder to chain calls (eg.builder.withTransformationManager().build()) + */ + public SSMProviderBuilder withTransformationManager(TransformationManager transformationManager) { + this.transformationManager = transformationManager; + return this; + } +} diff --git a/powertools-parameters/powertools-parameters-ssm/src/test/java/software/amazon/lambda/powertools/parameters/ssm/SSMParamAspectTest.java b/powertools-parameters/powertools-parameters-ssm/src/test/java/software/amazon/lambda/powertools/parameters/ssm/SSMParamAspectTest.java new file mode 100644 index 000000000..e56d20ffa --- /dev/null +++ b/powertools-parameters/powertools-parameters-ssm/src/test/java/software/amazon/lambda/powertools/parameters/ssm/SSMParamAspectTest.java @@ -0,0 +1,52 @@ +/* + * 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.ssm; + +import static org.apache.commons.lang3.reflect.FieldUtils.writeStaticField; +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.function.Supplier; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +public class SSMParamAspectTest { + + // This class tests the SSM Param aspect in the same fashion + // as the tests for the aspects for the other providers. + + @Test + public void parameterInjectedByProvider() throws Exception { + + String key = "myKey"; + String value = "mySecretValue"; + SSMProvider provider = Mockito.mock(SSMProvider.class); + + SupplierproviderBuilder = () -> provider; + writeStaticField(SSMParamAspect.class, "providerBuilder", providerBuilder, true); + + // Setup our mocked SSMProvider to return a value for our test data + Mockito.when(provider.get(key)).thenReturn(value); + + // Create an instance of a class and let the SSMParamAspect inject it + MyInjectedClass obj = new MyInjectedClass(); + assertThat(obj.mySecret).isEqualTo(value); + } + + class MyInjectedClass { + @SSMParam(key = "myKey") + public String mySecret; + } + +} diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/SSMProviderTest.java b/powertools-parameters/powertools-parameters-ssm/src/test/java/software/amazon/lambda/powertools/parameters/ssm/SSMProviderTest.java similarity index 84% rename from powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/SSMProviderTest.java rename to powertools-parameters/powertools-parameters-ssm/src/test/java/software/amazon/lambda/powertools/parameters/ssm/SSMProviderTest.java index 2a4f8f927..b105da438 100644 --- a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/SSMProviderTest.java +++ b/powertools-parameters/powertools-parameters-ssm/src/test/java/software/amazon/lambda/powertools/parameters/ssm/SSMProviderTest.java @@ -12,16 +12,10 @@ * */ -package software.amazon.lambda.powertools.parameters; +package software.amazon.lambda.powertools.parameters.ssm; 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; @@ -31,8 +25,11 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; +import org.mockito.ArgumentMatchers; import org.mockito.Captor; import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; import software.amazon.awssdk.services.ssm.SsmClient; import software.amazon.awssdk.services.ssm.model.GetParameterRequest; import software.amazon.awssdk.services.ssm.model.GetParameterResponse; @@ -62,9 +59,9 @@ public class SSMProviderTest { @BeforeEach public void init() { - openMocks(this); + MockitoAnnotations.openMocks(this); cacheManager = new CacheManager(); - provider = new SSMProvider(cacheManager, client); + provider = new SSMProvider(cacheManager, null, client); } @Test @@ -100,7 +97,7 @@ public void getMultiple() { parameters.add(Parameter.builder().name("/prod/app1/key2").value("foo2").build()); parameters.add(Parameter.builder().name("/prod/app1/key3").value("foo3").build()); GetParametersByPathResponse response = GetParametersByPathResponse.builder().parameters(parameters).build(); - when(client.getParametersByPath(paramByPathCaptor.capture())).thenReturn(response); + Mockito.when(client.getParametersByPath(paramByPathCaptor.capture())).thenReturn(response); Map params = provider.getMultiple("/prod/app1"); assertThat(params).contains( @@ -123,7 +120,7 @@ public void getMultipleWithTrailingSlash() { parameters.add(Parameter.builder().name("/prod/app1/key2").value("foo2").build()); parameters.add(Parameter.builder().name("/prod/app1/key3").value("foo3").build()); GetParametersByPathResponse response = GetParametersByPathResponse.builder().parameters(parameters).build(); - when(client.getParametersByPath(paramByPathCaptor.capture())).thenReturn(response); + Mockito.when(client.getParametersByPath(paramByPathCaptor.capture())).thenReturn(response); Map params = provider.getMultiple("/prod/app1/"); assertThat(params).contains( @@ -146,7 +143,7 @@ public void getMultiple_cached_shouldNotCallSSM() { parameters.add(Parameter.builder().name("/prod/app1/key2").value("foo2").build()); parameters.add(Parameter.builder().name("/prod/app1/key3").value("foo3").build()); GetParametersByPathResponse response = GetParametersByPathResponse.builder().parameters(parameters).build(); - when(client.getParametersByPath(paramByPathCaptor.capture())).thenReturn(response); + Mockito.when(client.getParametersByPath(paramByPathCaptor.capture())).thenReturn(response); provider.getMultiple("/prod/app1"); @@ -156,7 +153,8 @@ public void getMultiple_cached_shouldNotCallSSM() { provider.get("/prod/app1/key2"); provider.get("/prod/app1/key3"); - verify(client, times(1)).getParametersByPath(any(GetParametersByPathRequest.class)); + Mockito.verify(client, Mockito.times(1)) + .getParametersByPath(ArgumentMatchers.any(GetParametersByPathRequest.class)); } @@ -172,7 +170,7 @@ public void getMultipleWithNextToken() { parameters2.add(Parameter.builder().name("/prod/app1/key3").value("foo3").build()); GetParametersByPathResponse response2 = GetParametersByPathResponse.builder().parameters(parameters2).build(); - when(client.getParametersByPath(paramByPathCaptor.capture())).thenReturn(response1, response2); + Mockito.when(client.getParametersByPath(paramByPathCaptor.capture())).thenReturn(response1, response2); Map params = provider.getMultiple("/prod/app1"); @@ -196,22 +194,10 @@ public void getMultipleWithNextToken() { assertThat(request2.nextToken()).isEqualTo("123abc"); } - @Test - public void testSecretsProviderBuilderMissingCacheManager_throwsException() { - - // Act & Assert - assertThatIllegalStateException().isThrownBy(() -> SSMProvider.builder() - .withClient(client) - .withTransformationManager(transformationManager) - .build()) - .withMessage("No CacheManager provided, please provide one"); - } - private void initMock(String expectedValue) { Parameter parameter = Parameter.builder().value(expectedValue).build(); GetParameterResponse result = GetParameterResponse.builder().parameter(parameter).build(); - when(client.getParameter(paramCaptor.capture())).thenReturn(result); - provider.defaultMaxAge(1, ChronoUnit.DAYS); + Mockito.when(client.getParameter(paramCaptor.capture())).thenReturn(result); provider.withMaxAge(2, ChronoUnit.DAYS); provider.recursive(); } diff --git a/powertools-parameters/powertools-parameters-tests/pom.xml b/powertools-parameters/powertools-parameters-tests/pom.xml new file mode 100644 index 000000000..d8e9b2a02 --- /dev/null +++ b/powertools-parameters/powertools-parameters-tests/pom.xml @@ -0,0 +1,67 @@ + + + \ No newline at end of file diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/BaseProviderTest.java b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/BaseProviderTest.java similarity index 91% rename from powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/BaseProviderTest.java rename to powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/BaseProviderTest.java index edc671e2c..5fa740253 100644 --- a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/BaseProviderTest.java +++ b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/BaseProviderTest.java @@ -25,6 +25,7 @@ import static software.amazon.lambda.powertools.parameters.transform.Transformer.json; import java.time.Clock; +import java.time.Duration; import java.time.temporal.ChronoUnit; import java.util.Base64; import java.util.HashMap; @@ -51,9 +52,8 @@ public void setup() { clock = Clock.systemDefaultZone(); cacheManager = new CacheManager(); - provider = new BasicProvider(cacheManager); transformationManager = new TransformationManager(); - provider.setTransformationManager(transformationManager); + provider = new BasicProvider(cacheManager, transformationManager); } @Test @@ -138,7 +138,8 @@ public void get_customTTL_expired_shouldGetValue() { @Test public void get_customDefaultTTL_cached_shouldGetFromCache() { - provider.defaultMaxAge(12, ChronoUnit.MINUTES).get("foobar"); + provider.cacheManager.setDefaultExpirationTime(Duration.of(12, MINUTES)); + provider.get("foobar"); getFromStore = false; provider.setClock(offset(clock, of(10, MINUTES))); @@ -149,7 +150,7 @@ public void get_customDefaultTTL_cached_shouldGetFromCache() { @Test public void get_customDefaultTTL_expired_shouldGetValue() { - provider.defaultMaxAge(2, ChronoUnit.MINUTES).get("barbaz"); + provider.cacheManager.setDefaultExpirationTime(Duration.of(2, MINUTES)); getFromStore = false; provider.setClock(offset(clock, of(3, MINUTES))); @@ -160,9 +161,7 @@ public void get_customDefaultTTL_expired_shouldGetValue() { @Test public void get_customDefaultTTLAndTTL_cached_shouldGetFromCache() { - provider.defaultMaxAge(12, ChronoUnit.MINUTES) - .withMaxAge(5, SECONDS) - .get("foobaz"); + provider.get("foobaz"); getFromStore = false; provider.setClock(offset(clock, of(4, SECONDS))); @@ -173,9 +172,10 @@ public void get_customDefaultTTLAndTTL_cached_shouldGetFromCache() { @Test public void get_customDefaultTTLAndTTL_expired_shouldGetValue() { - provider.defaultMaxAge(2, ChronoUnit.MINUTES) - .withMaxAge(5, SECONDS) - .get("bariton"); + + provider.cacheManager.setDefaultExpirationTime(Duration.ofMinutes(2)); + + provider.withMaxAge(5, SECONDS).get("bariton"); getFromStore = false; provider.setClock(offset(clock, of(6, SECONDS))); @@ -275,8 +275,9 @@ public void getObject_customTTL_expired_shouldGetValue() { public void getObject_customDefaultTTL_cached_shouldGetFromCache() { provider.setValue("{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}"); - provider.defaultMaxAge(12, ChronoUnit.MINUTES) - .withTransformation(json) + provider.cacheManager.setDefaultExpirationTime(Duration.of(12, MINUTES)); + + provider.withTransformation(json) .get("foo", ObjectToDeserialize.class); getFromStore = false; @@ -290,8 +291,9 @@ public void getObject_customDefaultTTL_cached_shouldGetFromCache() { public void getObject_customDefaultTTL_expired_shouldGetValue() { provider.setValue("{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}"); - provider.defaultMaxAge(2, ChronoUnit.MINUTES) - .withTransformation(json) + provider.cacheManager.setDefaultExpirationTime(Duration.of(2, MINUTES)); + + provider.withTransformation(json) .get("foo", ObjectToDeserialize.class); getFromStore = false; @@ -305,9 +307,10 @@ public void getObject_customDefaultTTL_expired_shouldGetValue() { public void getObject_customDefaultTTLAndTTL_cached_shouldGetFromCache() { provider.setValue("{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}"); - provider.defaultMaxAge(12, ChronoUnit.MINUTES) + provider.cacheManager.setDefaultExpirationTime(Duration.ofSeconds(5)); + + provider.withTransformation(json) .withMaxAge(5, SECONDS) - .withTransformation(json) .get("foo", ObjectToDeserialize.class); getFromStore = false; @@ -321,8 +324,9 @@ public void getObject_customDefaultTTLAndTTL_cached_shouldGetFromCache() { public void getObject_customDefaultTTLAndTTL_expired_shouldGetValue() { provider.setValue("{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}"); - provider.defaultMaxAge(2, ChronoUnit.MINUTES) - .withMaxAge(5, SECONDS) + provider.cacheManager.setDefaultExpirationTime(Duration.ofMinutes(2)); + + provider.withMaxAge(5, SECONDS) .withTransformation(json) .get("foo", ObjectToDeserialize.class); getFromStore = false; @@ -335,7 +339,7 @@ public void getObject_customDefaultTTLAndTTL_expired_shouldGetValue() { @Test public void get_noTransformationManager_shouldThrowException() { - provider.setTransformationManager(null); + provider = new BasicProvider(new CacheManager(), null); assertThatIllegalStateException() .isThrownBy(() -> provider.withTransformation(base64).get("foo")); @@ -343,7 +347,6 @@ public void get_noTransformationManager_shouldThrowException() { @Test public void getObject_noTransformationManager_shouldThrowException() { - provider.setTransformationManager(null); assertThatIllegalStateException() .isThrownBy(() -> provider.get("foo", ObjectToDeserialize.class)); @@ -379,8 +382,8 @@ class BasicProvider extends BaseProvider { private String value = "valueFromStore"; - public BasicProvider(CacheManager cacheManager) { - super(cacheManager); + public BasicProvider(CacheManager cacheManager, TransformationManager transformationManager) { + super(cacheManager, transformationManager); } public void setValue(String value) { diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/ParamManagerIntegrationTest.java b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/ParamProvidersIntegrationTest.java similarity index 81% rename from powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/ParamManagerIntegrationTest.java rename to powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/ParamProvidersIntegrationTest.java index d6fbe66f0..7d790d140 100644 --- a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/ParamManagerIntegrationTest.java +++ b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/ParamProvidersIntegrationTest.java @@ -14,7 +14,6 @@ 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; @@ -25,14 +24,12 @@ 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; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.Mock; -import software.amazon.awssdk.services.appconfigdata.AppConfigDataClient; import software.amazon.awssdk.services.dynamodb.DynamoDbClient; import software.amazon.awssdk.services.secretsmanager.SecretsManagerClient; import software.amazon.awssdk.services.secretsmanager.model.GetSecretValueRequest; @@ -43,8 +40,10 @@ import software.amazon.awssdk.services.ssm.model.GetParametersByPathRequest; import software.amazon.awssdk.services.ssm.model.GetParametersByPathResponse; import software.amazon.awssdk.services.ssm.model.Parameter; +import software.amazon.lambda.powertools.parameters.secrets.SecretsProvider; +import software.amazon.lambda.powertools.parameters.ssm.SSMProvider; -public class ParamManagerIntegrationTest { +public class ParamProvidersIntegrationTest { @Mock SsmClient ssmClient; @@ -59,19 +58,17 @@ public class ParamManagerIntegrationTest { SecretsManagerClient secretsManagerClient; @Captor ArgumentCaptor4.0.0 ++ + +software.amazon.lambda +powertools-parent +2.0.0-SNAPSHOT +../../pom.xml +powertools-parameters-tests +Powertools parameters tests that cut across all the parameters providers ++ + + +true ++ ++ +software.amazon.lambda +powertools-parameters-ssm +test +${project.version} ++ +software.amazon.lambda +powertools-parameters-secrets +test +${project.version} ++ + + +software.amazon.lambda +powertools-parameters-dynamodb +test +${project.version} ++ +org.junit.jupiter +junit-jupiter-api +test ++ +org.junit.jupiter +junit-jupiter-engine +test ++ +org.apache.commons +commons-lang3 +test ++ +org.assertj +assertj-core +test ++ +org.aspectj +aspectjweaver +test +secretsCaptor; - @Mock - private AppConfigDataClient appConfigDataClient; @BeforeEach public void setup() throws IllegalAccessException { openMocks(this); - - writeStaticField(ParamManager.class, "providers", new ConcurrentHashMap<>(), true); } @Test public void ssmProvider_get() { - SSMProvider ssmProvider = ParamManager.getSsmProvider(ssmClient); + SSMProvider ssmProvider = SSMProvider.builder() + .withClient(ssmClient) + .build(); String expectedValue = "value"; Parameter parameter = Parameter.builder().value(expectedValue).build(); @@ -87,7 +84,9 @@ public void ssmProvider_get() { @Test public void ssmProvider_getMultiple() { - SSMProvider ssmProvider = ParamManager.getSsmProvider(ssmClient); + SSMProvider ssmProvider = SSMProvider.builder() + .withClient(ssmClient) + .build(); List parameters = new ArrayList<>(); parameters.add(Parameter.builder().name("/prod/app1/key1").value("foo1").build()); @@ -112,7 +111,9 @@ public void ssmProvider_getMultiple() { @Test public void secretsProvider_get() { - SecretsProvider secretsProvider = ParamManager.getSecretsProvider(secretsManagerClient); + SecretsProvider secretsProvider = SecretsProvider.builder() + .withClient(secretsManagerClient) + .build(); String expectedValue = "Value1"; GetSecretValueResponse response = GetSecretValueResponse.builder().secretString(expectedValue).build(); @@ -125,24 +126,4 @@ public void secretsProvider_get() { verify(secretsManagerClient, times(1)).getSecretValue(any(GetSecretValueRequest.class)); } - @Test - public void getDynamoDbProvider() { - - // Act - DynamoDbProvider provider = ParamManager.getDynamoDbProvider(ddbClient, "test-table"); - - // Assert - assertThat(provider).isNotNull(); - } - - @Test - public void getAppConfigProvider() { - - // Act - AppConfigProvider provider = ParamManager.getAppConfigProvider(appConfigDataClient, "test-env", "test-app"); - - // Assert - assertThat(provider).isNotNull(); - - } } diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/cache/CacheManagerTest.java b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/cache/CacheManagerTest.java similarity index 100% rename from powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/cache/CacheManagerTest.java rename to powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/cache/CacheManagerTest.java diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/cache/DataStoreTest.java b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/cache/DataStoreTest.java similarity index 100% rename from powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/cache/DataStoreTest.java rename to powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/cache/DataStoreTest.java diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/internal/AnotherObject.java b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/internal/AnotherObject.java similarity index 100% rename from powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/internal/AnotherObject.java rename to powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/internal/AnotherObject.java diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/internal/CustomProvider.java b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/internal/CustomProvider.java similarity index 97% rename from powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/internal/CustomProvider.java rename to powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/internal/CustomProvider.java index 2c9db3712..a98d68c22 100644 --- a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/internal/CustomProvider.java +++ b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/internal/CustomProvider.java @@ -25,7 +25,7 @@ public class CustomProvider extends BaseProvider { private final Map values = new HashMap<>(); public CustomProvider(CacheManager cacheManager) { - super(cacheManager); + super(cacheManager, null); values.put("/simple", "value"); values.put("/base64", Base64.getEncoder().encodeToString("value".getBytes())); values.put("/json", "{\"foo\":\"Foo\", \"bar\":42, \"baz\":123456789}"); diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/transform/Base64TransformerTest.java b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/transform/Base64TransformerTest.java similarity index 100% rename from powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/transform/Base64TransformerTest.java rename to powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/transform/Base64TransformerTest.java diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/transform/JsonTransformerTest.java b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/transform/JsonTransformerTest.java similarity index 100% rename from powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/transform/JsonTransformerTest.java rename to powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/transform/JsonTransformerTest.java diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/transform/ObjectToDeserialize.java b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/transform/ObjectToDeserialize.java similarity index 100% rename from powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/transform/ObjectToDeserialize.java rename to powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/transform/ObjectToDeserialize.java diff --git a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/transform/TransformationManagerTest.java b/powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/transform/TransformationManagerTest.java similarity index 100% rename from powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/transform/TransformationManagerTest.java rename to powertools-parameters/powertools-parameters-tests/src/test/java/software/amazon/lambda/powertools/parameters/transform/TransformationManagerTest.java diff --git a/powertools-parameters/spotbugs-exclude.xml b/powertools-parameters/spotbugs-exclude.xml new file mode 100644 index 000000000..d48e9bee1 --- /dev/null +++ b/powertools-parameters/spotbugs-exclude.xml @@ -0,0 +1,82 @@ + + + \ No newline at end of file diff --git a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/BaseParamAspect.java b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/BaseParamAspect.java new file mode 100644 index 000000000..f7ebdf97a --- /dev/null +++ b/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/BaseParamAspect.java @@ -0,0 +1,57 @@ +/* + * 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 org.aspectj.lang.reflect.FieldSignature; +import software.amazon.lambda.powertools.parameters.transform.Transformer; + +/** + * Provides a common base for all parameter aspects. This lets us group functionality that + * we need to reimplement in each aspect. This class should be extended for each + * additional parameter aspect. + */ +public abstract class BaseParamAspect { + + /** + * Gets the parameter value from the provider and transforms it if necessary. This transformation + * is generic across all parameter providers. + * + * @param key The key of the parameter to get + * @param transformer The transformer to use to transform the parameter value + * @param provider A concrete instance of the parameter provider to retrieve the value from + * @param fieldSignature The signature of the field that the parameter is being injected into + * @return The value of the parameter, transformed if necessary + */ + protected Object getAndTransform(String key, Class extends Transformer> transformer, BaseProvider provider, + FieldSignature fieldSignature) { + if (transformer.isInterface()) { + // No transformation + return provider.get(key); + } else { + Class fieldType = fieldSignature.getFieldType(); + if (String.class.isAssignableFrom(fieldType)) { + // Basic transformation + return provider + .withTransformation(transformer) + .get(key); + } else { + // Complex transformation + return provider + .withTransformation(transformer) + .get(key, fieldType); + } + } + } +} 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 edb82f5ec..bedace28c 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 @@ -34,11 +34,12 @@ public abstract class BaseProvider implements ParamProvider { public static final String PARAMETERS = "parameters"; protected final CacheManager cacheManager; - private TransformationManager transformationManager; + private final TransformationManager transformationManager; private Clock clock; - public BaseProvider(CacheManager cacheManager) { + public BaseProvider(CacheManager cacheManager, TransformationManager transformationManager) { this.cacheManager = cacheManager; + this.transformationManager = transformationManager; } /** @@ -59,25 +60,10 @@ public BaseProvider(CacheManager cacheManager) { */ protected abstract Map+ ++ + ++ ++ + + ++ + + ++ + + ++ + + ++ + + ++ + + ++ + + ++ + + ++ + + ++ + + ++ + + ++ + + + ++ + + ++ + + ++ + + + ++ + getMultipleValues(String path); - /** - * (Optional) Set the default max age for the cache of all parameters. Override the default 5 seconds.
- * If for some parameters, you need to set a different maxAge, use {@link #withMaxAge(int, ChronoUnit)}.
- * Use {@link #withMaxAge(int, ChronoUnit)} after {#defaultMaxAge(int, ChronoUnit)} in the chain. - * - * @param maxAge Maximum time to cache the parameter, before calling the underlying parameter store. - * @param unit Unit of time - * @return the provider itself in order to chain calls (eg.provider.defaultMaxAge(10, SECONDS).get("key")). - */ - protected BaseProvider defaultMaxAge(int maxAge, ChronoUnit unit) { - Duration duration = Duration.of(maxAge, unit); - cacheManager.setDefaultExpirationTime(duration); - return this; - } - /** * (Optional) Builder method to call before {@link #get(String)} or {@link #get(String, Class)} * to set cache max age for the parameter to get.
- * The max age is reset to default (either 5 or a custom value set with {@link #defaultMaxAge}) after each get, + * The max age is reset to default (either 5 or a custom value that may be set on the CacheManager) after each get, * so you need to use this method for each parameter to cache with non-default max age.
* * Not Thread Safe: calling this method simultaneously by several threads @@ -225,10 +211,6 @@ protected void resetToDefaults() { } } - protected void setTransformationManager(TransformationManager transformationManager) { - this.transformationManager = transformationManager; - } - /** * For test purpose * 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 deleted file mode 100644 index 363f39d7c..000000000 --- a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/DynamoDbProvider.java +++ /dev/null @@ -1,213 +0,0 @@ -/* - * 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.core.client.config.ClientOverrideConfiguration; -import software.amazon.awssdk.core.client.config.SdkAdvancedClientOption; -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.AttributeValue; -import software.amazon.awssdk.services.dynamodb.model.GetItemRequest; -import software.amazon.awssdk.services.dynamodb.model.GetItemResponse; -import software.amazon.awssdk.services.dynamodb.model.QueryRequest; -import software.amazon.awssdk.services.dynamodb.model.QueryResponse; -import software.amazon.lambda.powertools.common.internal.UserAgentConfigurator; -import software.amazon.lambda.powertools.parameters.cache.CacheManager; -import software.amazon.lambda.powertools.parameters.exception.DynamoDbProviderSchemaException; -import software.amazon.lambda.powertools.parameters.transform.TransformationManager; - -/** - * 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 { - - private final DynamoDbClient client; - private final String tableName; - - DynamoDbProvider(CacheManager cacheManager, DynamoDbClient client, String tableName) { - super(cacheManager); - this.client = client; - this.tableName = tableName; - } - - DynamoDbProvider(CacheManager cacheManager, String tableName) { - 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. - * - * @param key key of the parameter - * @return The value, if it exists, null if it doesn't. Throws if the row exists but doesn't match the schema. - */ - @Override - protected String getValue(String key) { - GetItemResponse resp = client.getItem(GetItemRequest.builder() - .tableName(tableName) - .key(Collections.singletonMap("id", AttributeValue.fromS(key))) - .attributesToGet("value") - .build()); - - // If we have an item at the key, we should be able to get a 'val' out of it. If not it's - // exceptional. - // If we don't have an item at the key, we should return null. - if (resp.hasItem() && !resp.item().values().isEmpty()) { - if (!resp.item().containsKey("value")) { - throw new DynamoDbProviderSchemaException("Missing 'value': " + resp.item().toString()); - } - return resp.item().get("value").s(); - } - - return null; - } - - /** - * Returns multiple values from the DynamoDB parameter provider. - * - * @param path Parameter store path - * @return All values matching the given path, and an empty map if none do. Throws if any records exist that don't match the schema. - */ - @Override - protected MapgetMultipleValues(String path) { - - QueryResponse resp = client.query(QueryRequest.builder() - .tableName(tableName) - .keyConditionExpression("id = :v_id") - .expressionAttributeValues(Collections.singletonMap(":v_id", AttributeValue.fromS(path))) - .build()); - - return resp - .items() - .stream() - .peek((i) -> - { - if (!i.containsKey("sk")) { - throw new DynamoDbProviderSchemaException("Missing 'sk': " + i.toString()); - } - if (!i.containsKey("value")) { - throw new DynamoDbProviderSchemaException("Missing 'value': " + i.toString()); - } - }) - .collect( - Collectors.toMap( - (i) -> i.get("sk").s(), - (i) -> i.get("value").s())); - - - } - - 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()))) - .overrideConfiguration(ClientOverrideConfiguration.builder() - .putAdvancedOption(SdkAdvancedClientOption.USER_AGENT_SUFFIX, - UserAgentConfigurator.getUserAgent(PARAMETERS)).build()) - .build(); - } - - /** - * Create a {@link DynamoDbProvider} instance. - * - * @return a {@link DynamoDbProvider} - */ - public DynamoDbProvider build() { - if (cacheManager == null) { - throw new IllegalStateException("No CacheManager provided; please provide one"); - } - if (table == null) { - throw new IllegalStateException("No DynamoDB table name provided; please provide one"); - } - DynamoDbProvider provider; - if (client == null) { - client = createClient(); - } - provider = new DynamoDbProvider(cacheManager, client, table); - - if (transformationManager != null) { - provider.setTransformationManager(transformationManager); - } - return provider; - } - - /** - * Set custom {@link DynamoDbClient} to pass to the {@link DynamoDbClient}.
- * Use it if you want to customize the region or any other part of the client. - * - * @param client Custom client - * @return the builder to chain calls (eg.builder.withClient().build()) - */ - public DynamoDbProvider.Builder withClient(DynamoDbClient client) { - this.client = client; - return this; - } - - /** - * Mandatory. Provide a CacheManager to the {@link DynamoDbProvider} - * - * @param cacheManager the manager that will handle the cache of parameters - * @return the builder to chain calls (eg.builder.withCacheManager().build()) - */ - public DynamoDbProvider.Builder withCacheManager(CacheManager cacheManager) { - this.cacheManager = cacheManager; - return this; - } - - /** - * Mandatory. Provide a DynamoDB table to the {@link DynamoDbProvider} - * - * @param table the table that parameters will be retrieved from. - * @return the builder to chain calls (eg.builder.withTable().build()) - */ - public DynamoDbProvider.Builder withTable(String table) { - this.table = table; - return this; - } - - /** - * Provide a transformationManager to the {@link DynamoDbProvider} - * - * @param transformationManager the manager that will handle transformation of parameters - * @return the builder to chain calls (eg.builder.withTransformationManager().build()) - */ - public DynamoDbProvider.Builder withTransformationManager(TransformationManager transformationManager) { - this.transformationManager = transformationManager; - return this; - } - } -} 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 deleted file mode 100644 index 6fee0f114..000000000 --- a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/ParamManager.java +++ /dev/null @@ -1,194 +0,0 @@ -/* - * 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.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; -import software.amazon.awssdk.services.ssm.SsmClient; -import software.amazon.lambda.powertools.parameters.cache.CacheManager; -import software.amazon.lambda.powertools.parameters.transform.TransformationManager; - -/** - * Utility class to retrieve instances of parameter providers. - * Each instance is unique (singleton). - */ -public final class ParamManager { - - private static final CacheManager cacheManager = new CacheManager(); - private static final TransformationManager transformationManager = new TransformationManager(); - - // NOTE: For testing purposes `providers` cannot be final - private static ConcurrentHashMap, BaseProvider> providers = new ConcurrentHashMap<>(); - - /** - * 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. - * - * @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 staticT getProvider(Class providerClass) { - if (providerClass == null) { - 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"); - } - return (T) providers.computeIfAbsent(providerClass, ParamManager::createProvider); - } - - /** - * 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() { - return getProvider(SecretsProvider.class); - } - - /** - * 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() { - return getProvider(SSMProvider.class); - } - - /** - * Get a {@link DynamoDbProvider} with default {@link DynamoDbClient}
- * If you need to customize the region, or other part of the client, use {@link ParamManager#getDynamoDbProvider(DynamoDbClient, String)} - */ - public static DynamoDbProvider getDynamoDbProvider(String tableName) { - // Because we need a DDB table name to configure our client, we can't use - // ParamManager#getProvider. This means that we need to make sure we do the same stuff - - // set transformation manager and cache manager. - return DynamoDbProvider.builder() - .withCacheManager(cacheManager) - .withTable(tableName) - .withTransformationManager(transformationManager) - .build(); - } - - /** - * 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) { - // Because we need a DDB table name to configure our client, we can't use - // ParamManager#getProvider. This means that we need to make sure we do the same stuff - - // set transformation manager and cache manager. - return AppConfigProvider.builder() - .withCacheManager(cacheManager) - .withTransformationManager(transformationManager) - .withEnvironment(environment) - .withApplication(application) - .build(); - } - - - /** - * 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) { - return (SecretsProvider) providers.computeIfAbsent(SecretsProvider.class, (k) -> SecretsProvider.builder() - .withClient(client) - .withCacheManager(cacheManager) - .withTransformationManager(transformationManager) - .build()); - } - - /** - * 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) { - return (SSMProvider) providers.computeIfAbsent(SSMProvider.class, (k) -> SSMProvider.builder() - .withClient(client) - .withCacheManager(cacheManager) - .withTransformationManager(transformationManager) - .build()); - } - - /** - * 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) { - return (DynamoDbProvider) providers.computeIfAbsent(DynamoDbProvider.class, (k) -> DynamoDbProvider.builder() - .withClient(client) - .withTable(table) - .withCacheManager(cacheManager) - .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) { - return (AppConfigProvider) providers.computeIfAbsent(AppConfigProvider.class, (k) -> AppConfigProvider.builder() - .withClient(client) - .withCacheManager(cacheManager) - .withTransformationManager(transformationManager) - .withEnvironment(environment) - .withApplication(application) - .build()); - } - - - public static CacheManager getCacheManager() { - return cacheManager; - } - - public static TransformationManager getTransformationManager() { - return transformationManager; - } - - staticT 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) - provider.setTransformationManager(transformationManager); - return provider; - } catch (ReflectiveOperationException e) { - throw new RuntimeException("Unexpected error occurred. Please raise issue at " + - "https://github.com/aws-powertools/powertools-lambda-java/issues", e); - } - } - -} 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 deleted file mode 100644 index b77f501f2..000000000 --- a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/SecretsProvider.java +++ /dev/null @@ -1,226 +0,0 @@ -/* - * 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 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.core.client.config.ClientOverrideConfiguration; -import software.amazon.awssdk.core.client.config.SdkAdvancedClientOption; -import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; -import software.amazon.awssdk.regions.Region; -import software.amazon.awssdk.services.secretsmanager.SecretsManagerClient; -import software.amazon.awssdk.services.secretsmanager.model.GetSecretValueRequest; -import software.amazon.lambda.powertools.common.internal.UserAgentConfigurator; -import software.amazon.lambda.powertools.parameters.cache.CacheManager; -import software.amazon.lambda.powertools.parameters.transform.TransformationManager; -import software.amazon.lambda.powertools.parameters.transform.Transformer; - -/** - * AWS Secrets Manager Parameter Provider
- * - * Samples: - *- * SecretsProvider provider = ParamManager.getSecretsProvider(); - * - * String value = provider.get("key"); - * System.out.println(value); - * >>> "value" - * - * // Get a value and cache it for 30 seconds (all others values will now be cached for 30 seconds) - * String value = provider.defaultMaxAge(30, ChronoUnit.SECONDS).get("key"); - * - * // Get a value and cache it for 1 minute (all others values are cached for 5 seconds by default) - * String value = provider.withMaxAge(1, ChronoUnit.MINUTES).get("key"); - * - * // Get a base64 encoded value, decoded into a String, and store it in the cache - * String value = provider.withTransformation(Transformer.base64).get("key"); - * - * // Get a json value, transform it into an Object, and store it in the cache - * TargetObject = provider.withTransformation(Transformer.json).get("key", TargetObject.class); - *- */ -public class SecretsProvider extends BaseProvider { - - private final SecretsManagerClient client; - - /** - * 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. - */ - SecretsProvider(CacheManager cacheManager, SecretsManagerClient client) { - super(cacheManager); - this.client = client; - } - - /** - * 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. - * - * @param key key of the parameter - * @return the value of the parameter identified by the key - */ - @Override - protected String getValue(String key) { - GetSecretValueRequest request = GetSecretValueRequest.builder().secretId(key).build(); - - String secretValue = client.getSecretValue(request).secretString(); - if (secretValue == null) { - 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 - protected Map
getMultipleValues(String path) { - throw new UnsupportedOperationException("Impossible to get multiple values from AWS Secrets Manager"); - } - - /** - * {@inheritDoc} - */ - @Override - public SecretsProvider defaultMaxAge(int maxAge, ChronoUnit unit) { - super.defaultMaxAge(maxAge, unit); - return this; - } - - /** - * {@inheritDoc} - */ - @Override - public SecretsProvider withMaxAge(int maxAge, ChronoUnit unit) { - super.withMaxAge(maxAge, unit); - return this; - } - - /** - * {@inheritDoc} - */ - @Override - public SecretsProvider withTransformation(Class extends Transformer> transformerClass) { - super.withTransformation(transformerClass); - return this; - } - - // For test purpose only - SecretsManagerClient getClient() { - return client; - } - - 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()))) - .overrideConfiguration(ClientOverrideConfiguration.builder() - .putAdvancedOption(SdkAdvancedClientOption.USER_AGENT_SUFFIX, - UserAgentConfigurator.getUserAgent(PARAMETERS)).build()) - .build(); - } - - /** - * Create a {@link SecretsProvider} instance. - * - * @return a {@link SecretsProvider} - */ - public SecretsProvider build() { - if (cacheManager == null) { - throw new IllegalStateException("No CacheManager provided, please provide one"); - } - SecretsProvider provider; - if (client == null) { - client = createClient(); - } - - provider = new SecretsProvider(cacheManager, client); - - if (transformationManager != null) { - provider.setTransformationManager(transformationManager); - } - return provider; - } - - /** - * Set custom {@link SecretsManagerClient} to pass to the {@link SecretsProvider}.
- * Use it if you want to customize the region or any other part of the client. - * - * @param client Custom client - * @return the builder to chain calls (eg.builder.withClient().build()) - */ - public Builder withClient(SecretsManagerClient client) { - this.client = client; - return this; - } - - /** - * Mandatory. Provide a CacheManager to the {@link SecretsProvider} - * - * @param cacheManager the manager that will handle the cache of parameters - * @return the builder to chain calls (eg.builder.withCacheManager().build()) - */ - public Builder withCacheManager(CacheManager cacheManager) { - this.cacheManager = cacheManager; - return this; - } - - /** - * Provide a transformationManager to the {@link SecretsProvider} - * - * @param transformationManager the manager that will handle transformation of parameters - * @return the builder to chain calls (eg.builder.withTransformationManager().build()) - */ - public Builder withTransformationManager(TransformationManager transformationManager) { - this.transformationManager = transformationManager; - return this; - } - } -} 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 deleted file mode 100644 index 081af108d..000000000 --- a/powertools-parameters/src/main/java/software/amazon/lambda/powertools/parameters/internal/LambdaParametersAspect.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * 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; -import org.aspectj.lang.annotation.Around; -import org.aspectj.lang.annotation.Aspect; -import org.aspectj.lang.annotation.Pointcut; -import org.aspectj.lang.reflect.FieldSignature; -import software.amazon.lambda.powertools.parameters.BaseProvider; -import software.amazon.lambda.powertools.parameters.Param; -import software.amazon.lambda.powertools.parameters.ParamManager; - -@Aspect -public class LambdaParametersAspect { - - @Pointcut("get(* *) && @annotation(paramAnnotation)") - public void getParam(Param paramAnnotation) { - } - - @Around("getParam(paramAnnotation)") - public Object injectParam(final ProceedingJoinPoint joinPoint, final Param paramAnnotation) { - BaseProvider provider = ParamManager.getProvider(paramAnnotation.provider()); - - if (paramAnnotation.transformer().isInterface()) { - // No transformation - return provider.get(paramAnnotation.key()); - } else { - FieldSignature s = (FieldSignature) joinPoint.getSignature(); - if (String.class.isAssignableFrom(s.getFieldType())) { - // Basic transformation - return provider - .withTransformation(paramAnnotation.transformer()) - .get(paramAnnotation.key()); - } else { - // Complex transformation - return provider - .withTransformation(paramAnnotation.transformer()) - .get(paramAnnotation.key(), s.getFieldType()); - } - } - } - -} 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 deleted file mode 100644 index b84fcf743..000000000 --- a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/ParamManagerTest.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * 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.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 - public void testGetCacheManager() { - - // Act - CacheManager cacheManager = ParamManager.getCacheManager(); - - // Assert - assertNotNull(cacheManager); - } - - @Test - public void testGetTransformationManager() { - - // Act - TransformationManager transformationManager = ParamManager.getTransformationManager(); - - // Assert - assertNotNull(transformationManager); - } - - @Test - public void testCreateProvider() { - - // Act - CustomProvider customProvider = ParamManager.createProvider(CustomProvider.class); - - // Assert - assertNotNull(customProvider); - } - - @Test - public void testCreateUninstanciableProvider_throwsException() { - - // Act & Assert - assertThatRuntimeException().isThrownBy(() -> ParamManager.createProvider(BaseProvider.class)); - } - - @Test - public void testGetProviderWithProviderClass() { - - // Act - SecretsProvider secretsProvider = ParamManager.getProvider(SecretsProvider.class); - - // Assert - assertNotNull(secretsProvider); - } - - @Test - public void testGetProviderWithProviderClass_throwsException() { - - // Act & Assert - assertThatIllegalStateException().isThrownBy(() -> ParamManager.getProvider(null)); - } - - @Test - public void testGetSecretsProvider_withoutParameter_shouldCreateDefaultClient() { - - // Act - SecretsProvider secretsProvider = ParamManager.getSecretsProvider(); - - // Assert - assertNotNull(secretsProvider); - assertNotNull(secretsProvider.getClient()); - } - - @Test - public void testGetSSMProvider_withoutParameter_shouldCreateDefaultClient() { - - // Act - SSMProvider ssmProvider = ParamManager.getSsmProvider(); - - // Assert - assertNotNull(ssmProvider); - assertNotNull(ssmProvider.getClient()); - } - - @Test - public void testGetDynamoDBProvider_requireOtherParameters_throwException() { - - // Act & Assert - assertThatIllegalArgumentException().isThrownBy(() -> ParamManager.getProvider(DynamoDbProvider.class)); - } - - @Test - public void testGetAppConfigProvider_requireOtherParameters_throwException() { - - // Act & Assert - assertThatIllegalArgumentException().isThrownBy(() -> ParamManager.getProvider(AppConfigProvider.class)); - } -} 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 deleted file mode 100644 index 2c246336b..000000000 --- a/powertools-parameters/src/test/java/software/amazon/lambda/powertools/parameters/internal/LambdaParametersAspectTest.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * 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; -import org.mockito.MockedStatic; -import software.amazon.lambda.powertools.parameters.Param; -import software.amazon.lambda.powertools.parameters.ParamManager; -import software.amazon.lambda.powertools.parameters.SSMProvider; -import software.amazon.lambda.powertools.parameters.exception.TransformationException; -import software.amazon.lambda.powertools.parameters.transform.Base64Transformer; -import software.amazon.lambda.powertools.parameters.transform.JsonTransformer; -import software.amazon.lambda.powertools.parameters.transform.ObjectToDeserialize; - -public class LambdaParametersAspectTest { - - @Mock - private SSMProvider defaultProvider; - - @Param(key = "/default") - private String defaultValue; - - @Param(key = "/simple", provider = CustomProvider.class) - private String param; - - @Param(key = "/base64", provider = CustomProvider.class, transformer = Base64Transformer.class) - private String basicTransform; - - @Param(key = "/json", provider = CustomProvider.class, transformer = JsonTransformer.class) - private ObjectToDeserialize complexTransform; - - @Param(key = "/json", provider = CustomProvider.class, transformer = JsonTransformer.class) - private AnotherObject wrongTransform; - - @BeforeEach - public void init() { - openMocks(this); - } - - @Test - public void testDefault_ShouldUseSSMProvider() { - try (MockedStaticmocked = mockStatic(ParamManager.class)) { - mocked.when(() -> ParamManager.getProvider(SSMProvider.class)).thenReturn(defaultProvider); - when(defaultProvider.get("/default")).thenReturn("value"); - - assertThat(defaultValue).isEqualTo("value"); - mocked.verify(() -> ParamManager.getProvider(SSMProvider.class), times(1)); - verify(defaultProvider, times(1)).get("/default"); - - mocked.reset(); - } - } - - @Test - public void testSimple() { - assertThat(param).isEqualTo("value"); - } - - @Test - public void testWithBasicTransform() { - assertThat(basicTransform).isEqualTo("value"); - } - - @Test - public void testWithComplexTransform() { - assertThat(complexTransform) - .isInstanceOf(ObjectToDeserialize.class) - .matches( - o -> o.getFoo().equals("Foo") && - o.getBar() == 42 && - o.getBaz() == 123456789); - } - - @Test - public void testWithComplexTransformWrongTargetClass_ShouldThrowException() { - assertThatExceptionOfType(TransformationException.class) - .isThrownBy(() -> - { - AnotherObject obj = wrongTransform; - }); - } - -} diff --git a/spotbugs-exclude.xml b/spotbugs-exclude.xml index eca7e266f..dc22f22b6 100644 --- a/spotbugs-exclude.xml +++ b/spotbugs-exclude.xml @@ -8,6 +8,40 @@ https://spotbugs.readthedocs.io/en/latest/bugDescriptions.html --> + + + + ++ ++ + + ++ + + ++ + + ++ + + ++ + + ++ + + + ++ + @@ -65,14 +99,6 @@ - - -- - - - - - @@ -140,18 +166,10 @@ - - - - - - -